From 4eaafd8dcb46d745ed876979d5d0da4377ff42ca Mon Sep 17 00:00:00 2001 From: xiachao Date: Mon, 5 Aug 2024 13:49:51 +0800 Subject: [PATCH] common and modules --- common/cache/pom.xml | 67 + .../cache/config/CacheManagerProperties.java | 26 + .../common/cache/config/LocalCacheConfig.java | 49 + .../common/cache/config/RedisConfig.java | 97 ++ .../common/cache/constants/CacheNameEnum.java | 160 ++ .../redis/LettuceConnectionValidConfig.java | 26 + .../redis/LettuceConnectionValidTask.java | 26 + .../thing/common/cache/redis/RedisKeys.java | 65 + .../thing/common/cache/redis/RedisUtils.java | 123 ++ .../common/cache/redis/SysParamsRedis.java | 38 + .../cache/redis/aspect/RedisAspect.java | 39 + .../cache/service/AbstractCacheService.java | 17 + .../cache/service/CacheInitializer.java | 40 + common/core/pom.xml | 129 ++ .../common/core/annotation/CustomExcel.java | 75 + .../annotation/CustomExcelCollection.java | 17 + .../common/core/annotation/LogOperation.java | 21 + .../common/core/config/AsyncEventConfig.java | 41 + .../core/config/RestTemplateConfig.java | 20 + .../thing/common/core/constants/Constant.java | 429 +++++ .../core/container/GlobalContainer.java | 39 + .../common/core/convert/DateConverter.java | 73 + .../common/core/enumeration/ActionType.java | 30 + .../common/core/enumeration/AggType.java | 36 + .../core/enumeration/AlarmSettingType.java | 26 + .../common/core/enumeration/AlarmStatus.java | 26 + .../common/core/enumeration/AlarmType.java | 35 + .../common/core/enumeration/ApiFuncEnum.java | 61 + .../core/enumeration/ApiSeparateAttr.java | 26 + .../common/core/enumeration/ApiType.java | 46 + .../common/core/enumeration/ApiTypeEnum.java | 61 + .../common/core/enumeration/ApiTypes.java | 22 + .../core/enumeration/AttributeEnum.java | 99 ++ .../core/enumeration/AttributeTypeEnum.java | 225 +++ .../core/enumeration/CacheEventType.java | 16 + .../enumeration/CalculationStatusEnum.java | 35 + .../core/enumeration/CalculationSymbol.java | 22 + .../core/enumeration/CarbonLifecycleEnum.java | 35 + .../common/core/enumeration/CloudService.java | 49 + .../core/enumeration/ColumnRelationType.java | 22 + .../common/core/enumeration/CompanyType.java | 22 + .../core/enumeration/ConfigTypeEnum.java | 68 + .../core/enumeration/ContainsCondition.java | 22 + .../common/core/enumeration/DataBaseType.java | 28 + .../enumeration/DataTreatingMarkEnum.java | 22 + .../enumeration/DatasetAttrTimestampType.java | 55 + .../core/enumeration/DatasetInterValType.java | 31 + .../core/enumeration/DatasetSeparateAttr.java | 22 + .../core/enumeration/DatasetStatus.java | 24 + .../DatasetThingRelationStatus.java | 22 + .../common/core/enumeration/DefaultType.java | 39 + .../common/core/enumeration/DeleteEnum.java | 27 + .../core/enumeration/DeviceSendCmdStatus.java | 22 + .../core/enumeration/DeviceTemplateMark.java | 22 + .../core/enumeration/DictRelationEnum.java | 26 + .../common/core/enumeration/DictTypeEnum.java | 60 + .../core/enumeration/EnergyVarietyEnum.java | 23 + .../core/enumeration/ExtendRelationType.java | 41 + .../core/enumeration/FormulaSymbols.java | 31 + .../core/enumeration/GateWayStatus.java | 29 + .../common/core/enumeration/Granularity.java | 50 + .../core/enumeration/GroupTypeEnum.java | 70 + .../core/enumeration/IntervalPushStatus.java | 22 + .../core/enumeration/IsDefaultEnum.java | 21 + .../common/core/enumeration/MenuTypeEnum.java | 28 + .../common/core/enumeration/MimeTypeEnum.java | 64 + .../enumeration/MinioFileContentTypeEnum.java | 353 ++++ .../MonitorPolymerizationType.java | 38 + .../core/enumeration/MonitorTimeType.java | 30 + .../common/core/enumeration/MsgType.java | 34 + .../common/core/enumeration/OrderStatus.java | 26 + .../core/enumeration/PeakValleyEnum.java | 25 + .../core/enumeration/PermissionStatus.java | 22 + .../common/core/enumeration/Protocol.java | 31 + .../core/enumeration/ProtocolPrefix.java | 25 + .../core/enumeration/PushReceiveStatus.java | 22 + .../common/core/enumeration/PushStatus.java | 22 + .../common/core/enumeration/QueryChannel.java | 12 + .../core/enumeration/QueueEventType.java | 24 + .../core/enumeration/QueueOriginType.java | 35 + .../core/enumeration/RegionLeafEnum.java | 23 + .../core/enumeration/RegionLevelEnum.java | 22 + .../core/enumeration/RepeatPushStatus.java | 22 + .../core/enumeration/ReportTypeEnum.java | 40 + .../common/core/enumeration/RoleType.java | 26 + .../common/core/enumeration/RsaKeyType.java | 12 + .../core/enumeration/ScheduleStatus.java | 22 + .../common/core/enumeration/ShowStatus.java | 22 + .../common/core/enumeration/SignEnum.java | 98 ++ .../common/core/enumeration/SignTypeEnum.java | 15 + .../common/core/enumeration/SmsService.java | 29 + .../core/enumeration/SocketEventType.java | 12 + .../common/core/enumeration/SourceStatus.java | 17 + .../core/enumeration/SubscribeStatusEnum.java | 20 + .../core/enumeration/SuperAdminEnum.java | 27 + .../core/enumeration/SuperTenantEnum.java | 22 + .../core/enumeration/SyncUpdateEnum.java | 22 + .../core/enumeration/SysTenantEnum.java | 22 + .../common/core/enumeration/TaskTime.java | 26 + .../common/core/enumeration/TaskTimeType.java | 34 + .../common/core/enumeration/TaskType.java | 22 + .../common/core/enumeration/TaskWeek.java | 43 + .../common/core/enumeration/TbTimeout.java | 17 + .../common/core/enumeration/TemplateMark.java | 30 + .../core/enumeration/TenantGroupEnum.java | 26 + .../core/enumeration/TenantSaveType.java | 24 + .../core/enumeration/ThingAttrType.java | 31 + .../core/enumeration/ThingEnableStatus.java | 29 + .../core/enumeration/ThingExistType.java | 22 + .../core/enumeration/ThingRealType.java | 31 + .../core/enumeration/ThingSortType.java | 10 + .../core/enumeration/ThingStatStatus.java | 22 + .../common/core/enumeration/ThingStatus.java | 55 + .../core/enumeration/TimeHorizonEnum.java | 69 + .../common/core/enumeration/TimeType.java | 101 ++ .../core/enumeration/TreeNodeStatus.java | 22 + .../core/enumeration/TriggerTypeEnum.java | 27 + .../core/enumeration/UserStatusEnum.java | 22 + .../common/core/enumeration/UsernameType.java | 26 + .../common/core/enumeration/menuStatus.java | 22 + .../thing/common/core/event/AuthParam.java | 41 + .../core/event/CacheAlarmActionEvent.java | 38 + .../common/core/event/CacheAlarmEvent.java | 37 + .../core/event/CacheCalculationEvent.java | 50 + .../thing/common/core/event/CacheEvent.java | 17 + .../core/event/ExtensionSocketEvent.java | 25 + .../common/core/event/QueueAlarmEvent.java | 34 + .../common/core/event/QueueCacheEvent.java | 31 + .../core/event/QueueCalculationEvent.java | 31 + .../common/core/event/QueueDeviceEvent.java | 21 + .../common/core/event/QueueSocketEvent.java | 32 + .../common/core/event/SocketClientEvent.java | 34 + .../thing/common/core/event/SocketEvent.java | 17 + .../core/event/SocketTelemetryEvent.java | 44 + .../common/core/exception/ErrorCode.java | 67 + .../common/core/exception/ExceptionUtils.java | 47 + .../exception/GlobalExceptionHandler.java | 37 + .../common/core/exception/SysException.java | 64 + .../expression/functions/AggFunction.java | 151 ++ .../common/core/expression/package-info.java | 6 + .../expression/parser/AggFormulaParser.java | 53 + .../initializer/AbstractAppInitializer.java | 15 + .../initializer/AppInitializeExecutor.java | 39 + .../common/core/message/MessageData.java | 35 + .../common/core/rest/dto/ApiRequest.java | 23 + .../common/core/rest/dto/ApiResponse.java | 32 + .../common/core/rest/service/ApiService.java | 24 + .../core/rest/service/ApiServiceImpl.java | 141 ++ .../com/thing/common/core/utils/AggUtil.java | 48 + .../com/thing/common/core/utils/BizUtils.java | 108 ++ .../thing/common/core/utils/CompareUtils.java | 20 + .../thing/common/core/utils/ConvertUtils.java | 124 ++ .../common/core/utils/DataTypeUtils.java | 192 +++ .../common/core/utils/DateTimeUtils.java | 1492 +++++++++++++++++ .../thing/common/core/utils/EncryptUtils.java | 243 +++ .../thing/common/core/utils/FormulaUtil.java | 158 ++ .../common/core/utils/HttpContextUtils.java | 101 ++ .../com/thing/common/core/utils/IpUtils.java | 151 ++ .../thing/common/core/utils/JacksonUtil.java | 249 +++ .../core/utils/JsonProcessingUtils.java | 81 + .../thing/common/core/utils/JsonUtils.java | 148 ++ .../com/thing/common/core/utils/JwtUtils.java | 39 + .../common/core/utils/KeyGeneratorUtils.java | 30 + .../com/thing/common/core/utils/MapUtil.java | 33 + .../thing/common/core/utils/MessageUtils.java | 27 + .../thing/common/core/utils/PageUtils.java | 99 ++ .../thing/common/core/utils/RandomUtils.java | 23 + .../thing/common/core/utils/RegexUtils.java | 23 + .../common/core/utils/RestTemplateUtils.java | 578 +++++++ .../common/core/utils/ResultCommonUtils.java | 64 + .../common/core/utils/SpringContextUtils.java | 49 + .../com/thing/common/core/utils/SqlUtil.java | 62 + .../common/core/utils/TokenGenerator.java | 45 + .../thing/common/core/utils/TreeUtils.java | 174 ++ .../core/utils/excel/ExcelExportUtil.java | 168 ++ .../common/core/utils/excel/ExcelUtils.java | 203 +++ .../core/utils/export/BaseExportService.java | 467 ++++++ .../utils/export/ExcelEntityGenerator.java | 289 ++++ .../core/utils/export/ExcelExportService.java | 253 +++ .../core/utils/export/ExcelExportUtil.java | 167 ++ .../core/utils/params/SortTreeNode.java | 24 + .../core/utils/params/SplitedTimeParam.java | 20 + .../common/core/utils/params/TreeNode.java | 40 + .../common/core/validator/AssertUtils.java | 93 + .../common/core/validator/ValidatorUtils.java | 52 + .../common/core/validator/group/AddGroup.java | 13 + .../core/validator/group/AliyunGroup.java | 11 + .../core/validator/group/DefaultGroup.java | 13 + .../core/validator/group/FastDFSGroup.java | 11 + .../common/core/validator/group/Group.java | 15 + .../core/validator/group/GroupNameGroup.java | 11 + .../core/validator/group/LocalGroup.java | 11 + .../core/validator/group/MinioGroup.java | 11 + .../core/validator/group/QcloudGroup.java | 11 + .../core/validator/group/QiniuGroup.java | 11 + .../core/validator/group/UpdateGroup.java | 13 + .../core/validator/group/WxOpenIdGroup.java | 11 + .../common/core/web/constants/HttpStatus.java | 59 + .../common/core/web/response/PageData.java | 54 + .../common/core/web/response/Result.java | 83 + .../main/resources/i18n/messages.properties | 53 + .../resources/i18n/messages_en_US.properties | 53 + .../resources/i18n/messages_zh_CN.properties | 53 + .../resources/i18n/messages_zh_TW.properties | 53 + .../main/resources/i18n/validation.properties | 115 ++ .../i18n/validation_en_US.properties | 116 ++ .../i18n/validation_zh_CN.properties | 115 ++ .../i18n/validation_zh_TW.properties | 116 ++ .../src/main/resources/keys/rsa_private.pem | 16 + .../src/main/resources/keys/rsa_public.pem | 6 + common/data/pom.xml | 78 + .../com/thing/common/data/dto/AttrCache.java | 40 + .../thing/common/data/dto/QueueMsgDTO.java | 60 + .../common/data/dto/TelemetryDeleteDTO.java | 31 + .../common/data/dto/TelemetrySaveDTO.java | 27 + .../thing/common/data/dto/ThingAttrParam.java | 37 + .../thing/common/data/dto/TsKvReqParam.java | 37 + .../common/data/event/AbstractQueueEvent.java | 33 + .../common/data/event/DataFilterEvent.java | 18 + .../common/data/event/EventPublisher.java | 37 + .../common/data/event/PublisherExecutor.java | 19 + .../common/data/event/QueueConsumerEvent.java | 17 + .../thing/common/data/event/QueueEvent.java | 15 + .../common/data/tskv/TsKvBaseEntity.java | 51 + .../com/thing/common/data/tskv/TsKvDTO.java | 161 ++ .../thing/common/data/tskv/TsKvEntity.java | 58 + common/data/src/main/proto/queue.proto | 22 + common/orm/pom.xml | 61 + .../common/orm/annotation/DataFilter.java | 63 + .../common/orm/config/MyBatisFlexConfig.java | 61 + .../com/thing/common/orm/dto/BaseDTO.java | 8 + .../thing/common/orm/dto/BaseThingDTO.java | 27 + .../com/thing/common/orm/dto/UserDetail.java | 42 + .../common/orm/entity/BaseDateEntity.java | 55 + .../thing/common/orm/entity/BaseEntity.java | 50 + .../common/orm/entity/BaseInfoEntity.java | 65 + .../common/orm/entity/BaseTenantEntity.java | 66 + .../orm/handler/MybatisExceptionHandler.java | 43 + .../common/orm/listener/AuthInfoReceiver.java | 51 + .../orm/listener/EntityInsertListener.java | 101 ++ .../orm/listener/EntityUpdateListener.java | 58 + .../common/orm/mapper/PowerBaseMapper.java | 67 + .../common/orm/service/IBaseService.java | 40 + .../orm/service/impl/BaseServiceImpl.java | 210 +++ .../thing/common/orm/utils/IdGenerator.java | 47 + .../common/orm/utils/SnowflakeIdWorker.java | 159 ++ common/pom.xml | 29 + common/queue/pom.xml | 109 ++ .../main/java/com/thing/queue/QueueAdmin.java | 23 + .../java/com/thing/queue/QueueCallback.java | 33 + .../java/com/thing/queue/QueueConsumer.java | 70 + .../main/java/com/thing/queue/QueueMsg.java | 8 + .../java/com/thing/queue/QueueMsgDecoder.java | 15 + .../java/com/thing/queue/QueueMsgHeaders.java | 12 + .../com/thing/queue/QueueMsgMetadata.java | 7 + .../java/com/thing/queue/QueueProducer.java | 37 + .../com/thing/queue/QueueThreadFactory.java | 37 + .../thing/queue/cluster/ClusterService.java | 48 + .../queue/cluster/ClusterServiceImpl.java | 91 + .../common/AbstractQueueConsumerTemplate.java | 190 +++ .../queue/memory/InMemoryQueueConsumer.java | 129 ++ .../queue/memory/InMemoryQueueProducer.java | 49 + .../thing/queue/memory/InMemoryStorage.java | 71 + .../queue/memoryto/InMemoryQueueService.java | 16 + .../thing/queue/memoryto/InMemoryStorage.java | 54 + .../queue/memoryto/MemoryConsumerService.java | 67 + .../thing/queue/memoryto/QueueProducer.java | 37 + .../impl/InMemoryQueueServiceImpl.java | 51 + .../thing/queue/message/DefaultQueueMsg.java | 18 + .../queue/message/DefaultQueueMsgHeaders.java | 27 + .../thing/queue/message/ProtoQueueMsg.java | 34 + .../com/thing/queue/message/ServiceQueue.java | 50 + .../com/thing/queue/message/ServiceType.java | 13 + .../partition/HashPartitionServiceImpl.java | 83 + .../queue/partition/PartitionChangeEvent.java | 29 + .../queue/partition/PartitionResetEvent.java | 30 + .../queue/partition/PartitionService.java | 31 + .../queue/partition/TopicPartitionInfo.java | 50 + .../partition/TopicPartitionInfoKey.java | 29 + .../queue/provider/CoreQueueFactory.java | 16 + .../provider/CoreQueueProducerProvider.java | 30 + .../provider/InMemoryCoreQueueFactory.java | 35 + .../queue/provider/QueueProducerProvider.java | 13 + .../provider/RabbitMqCoreQueueFactory.java | 52 + .../thing/queue/rabbitmq/RabbitMqAdmin.java | 72 + .../rabbitmq/RabbitMqConsumerTemplate.java | 121 ++ .../rabbitmq/RabbitMqProducerTemplate.java | 110 ++ .../rabbitmq/RabbitMqQueueArguments.java | 67 + .../queue/rabbitmq/RabbitMqSettings.java | 50 + .../thing/queue/setting/CoreQueueSetting.java | 19 + .../java/com/thing/queue/util/Topics.java | 33 + common/queue/src/main/proto/queue.proto | 64 + common/script/pom.xml | 60 + .../java/com/thing/CustomScriptException.java | 23 + .../java/com/thing/ScriptCreateService.java | 197 +++ .../com/thing/ScriptCreateServiceImpl.java | 381 +++++ .../java/com/thing/ScriptExecutionTask.java | 22 + .../main/java/com/thing/ScriptLanguage.java | 52 + .../src/main/java/com/thing/ScriptLog.java | 35 + .../src/main/java/com/thing/ScriptMsg.java | 42 + .../com/thing/api/AbstractScriptEngine.java | 151 ++ .../api/AbstractScriptInvokeService.java | 166 ++ .../com/thing/api/FormulaInvokeService.java | 40 + .../com/thing/api/FunctionScriptFactory.java | 34 + .../java/com/thing/api/JavaInvokeService.java | 38 + .../main/java/com/thing/api/ScriptEngine.java | 40 + .../com/thing/api/ScriptInvokeService.java | 44 + .../api/aviator/AviatorExecutionTask.java | 22 + .../api/aviator/AviatorInvokeService.java | 21 + .../com/thing/api/aviator/AviatorScript.java | 27 + .../api/aviator/AviatorScriptEngine.java | 124 ++ .../aviator/DefaultAviatorInvokeService.java | 189 +++ .../api/mvel/DefaultTbelInvokeService.java | 156 ++ .../main/java/com/thing/api/mvel/TbDate.java | 106 ++ .../main/java/com/thing/api/mvel/TbJson.java | 46 + .../main/java/com/thing/api/mvel/TbUtils.java | 304 ++++ .../com/thing/api/mvel/TbelExecutionTask.java | 21 + .../com/thing/api/mvel/TbelInvokeService.java | 21 + .../java/com/thing/api/mvel/TbelScript.java | 24 + .../com/thing/api/mvel/TbelScriptEngine.java | 111 ++ .../nashorn/DefaultNashornInvokeService.java | 129 ++ .../api/nashorn/NashornExecutionTask.java | 22 + .../api/nashorn/NashornInvokeService.java | 20 + .../com/thing/api/nashorn/NashornScript.java | 13 + .../api/nashorn/NashornScriptEngine.java | 100 ++ .../thing/modules/cache/bean/ScriptCache.java | 34 + .../cache/runner/ScriptCacheInitializer.java | 58 + .../cache/service/ScriptCacheService.java | 33 + .../service/impl/ScriptCacheServiceImpl.java | 51 + .../controller/ScriptInfoController.java | 172 ++ .../controller/ScriptLogController.java | 92 + .../java/com/thing/modules/dto/QueryMsg.java | 40 + .../com/thing/modules/dto/ScriptInfoDTO.java | 67 + .../com/thing/modules/dto/ScriptLogDTO.java | 39 + .../com/thing/modules/dto/ScriptTypeDTO.java | 21 + .../modules/entity/ScriptInfoEntity.java | 55 + .../thing/modules/entity/ScriptLogEntity.java | 31 + .../thing/modules/excel/ScriptInfoExcel.java | 28 + .../thing/modules/excel/ScriptLogExcel.java | 22 + .../modules/mapper/ScriptInfoMapper.java | 16 + .../thing/modules/mapper/ScriptLogMapper.java | 16 + .../modules/service/ScriptInfoService.java | 101 ++ .../modules/service/ScriptLogService.java | 43 + .../service/impl/ScriptInfoServiceImpl.java | 301 ++++ .../service/impl/ScriptLogServiceImpl.java | 107 ++ .../resources/script/ScriptInfoMapper.xml | 19 + .../main/resources/script/ScriptLogMapper.xml | 16 + common/security/pom.xml | 139 ++ .../java/com/thing/cache/SecurityCache.java | 69 + .../com/thing/cache/SecurityCacheManager.java | 18 + .../java/com/thing/config/FilterConfig.java | 45 + .../java/com/thing/config/ShiroConfig.java | 120 ++ .../java/com/thing/config/WebMvcConfig.java | 74 + .../com/thing/oauth2/AbstractOauth2Realm.java | 31 + .../java/com/thing/oauth2/Oauth2Filter.java | 96 ++ .../java/com/thing/oauth2/Oauth2Token.java | 28 + .../main/java/com/thing/password/BCrypt.java | 657 ++++++++ .../thing/password/BCryptPasswordEncoder.java | 82 + .../com/thing/password/PasswordEncoder.java | 30 + .../com/thing/password/PasswordUtils.java | 56 + .../main/java/com/thing/xss/SqlFilter.java | 42 + .../main/java/com/thing/xss/XssFilter.java | 30 + .../xss/XssHttpServletRequestWrapper.java | 140 ++ .../src/main/java/com/thing/xss/XssUtils.java | 69 + common/transport/pom.xml | 73 + .../api/AbstractTransportContext.java | 45 + .../thing/transport/api/SessionMetaData.java | 61 + .../thing/transport/api/TransportService.java | 96 ++ .../api/TransportServiceCallback.java | 24 + .../transport/api/TransportServiceImpl.java | 324 ++++ .../api/adaptor/AdaptorException.java | 19 + .../transport/api/adaptor/JsonConverter.java | 172 ++ .../transport/api/session/SessionContext.java | 10 + .../api/session/TransportSessionUtil.java | 52 + .../thing/transport/api/tools/RateLimits.java | 38 + .../transport/http/HttpTransportContext.java | 19 + .../transport/http/TelemetryController.java | 80 + .../MqttBrokerConnectController.java | 136 ++ .../controller/MqttBrokerMsgController.java | 97 ++ .../modules/dto/MqttBrokerConnectDTO.java | 43 + .../modules/dto/MqttBrokerMsgDTO.java | 38 + .../entity/MqttBrokerConnectEntity.java | 61 + .../modules/entity/MqttBrokerMsgEntity.java | 39 + .../modules/excel/MqttBrokerConnectExcel.java | 27 + .../modules/excel/MqttBrokerMsgExcel.java | 21 + .../mapper/MqttBrokerConnectMapper.java | 22 + .../modules/mapper/MqttBrokerMsgMapper.java | 22 + .../service/MqttBrokerConnectService.java | 37 + .../modules/service/MqttBrokerMsgService.java | 29 + .../impl/MqttBrokerConnectServiceImpl.java | 88 + .../impl/MqttBrokerMsgServiceImpl.java | 64 + .../transport/mqtt/broker/MqttTopics.java | 18 + .../mqtt/broker/MqttTransportContext.java | 28 + .../mqtt/broker/MqttTransportHandler.java | 303 ++++ .../MqttTransportServerInitializer.java | 32 + .../mqtt/broker/MqttTransportService.java | 79 + .../mqtt/broker/adaptors/JsonMqttAdaptor.java | 87 + .../broker/adaptors/MqttTransportAdaptor.java | 33 + .../AbstractDeviceAwareSessionContext.java | 26 + ...AbstractMqttDeviceAwareSessionContext.java | 40 + .../mqtt/broker/session/DeviceSessionCtx.java | 33 + .../mqtt/broker/session/MqttTopicMatcher.java | 40 + .../thing/transport/util/ConvertSendUtil.java | 67 + common/tskv/pom.xml | 111 ++ .../com/thing/common/tskv/NativeSQLTool.java | 508 ++++++ .../thing/common/tskv/QueryWrapperUtil.java | 88 + .../com/thing/common/tskv/entity/TsKvCk.java | 34 + .../common/tskv/entity/TsKvLatestCk.java | 28 + .../common/tskv/entity/TsKvLatestMy.java | 22 + .../common/tskv/entity/TsKvLatestPg.java | 24 + .../com/thing/common/tskv/entity/TsKvMy.java | 24 + .../com/thing/common/tskv/entity/TsKvPg.java | 24 + .../thing/common/tskv/entity/ViewTsKv.java | 11 + .../common/tskv/entity/ViewTsKvLatest.java | 11 + .../common/tskv/event/CalcLogSaveEvent.java | 18 + .../common/tskv/event/FilterLogSaveEvent.java | 27 + .../thing/common/tskv/event/PeakEvent.java | 21 + .../thing/common/tskv/event/TsKvEvent.java | 18 + .../common/tskv/event/TsKvEventService.java | 7 + .../tskv/event/TsKvEventServiceImpl.java | 92 + .../common/tskv/mapper/TsKvCkMapper.java | 14 + .../tskv/mapper/TsKvLatestCkMapper.java | 11 + .../tskv/mapper/TsKvLatestMyMapper.java | 9 + .../tskv/mapper/TsKvLatestPgMapper.java | 10 + .../common/tskv/mapper/TsKvMyMapper.java | 10 + .../common/tskv/mapper/TsKvPgMapper.java | 10 + .../common/tskv/mapper/ViewTsKvCkMapper.java | 10 + .../tskv/mapper/ViewTsKvLatestCkMapper.java | 11 + .../thing/common/tskv/service/AggType.java | 5 + .../thing/common/tskv/service/AmExecutor.java | 19 + .../thing/common/tskv/service/DBExecutor.java | 26 + .../common/tskv/service/DatabaseType.java | 5 + .../thing/common/tskv/service/HhExecutor.java | 19 + .../common/tskv/service/LatestNativeSQL.java | 87 + .../common/tskv/service/TsKvNativeSQL.java | 819 +++++++++ .../common/tskv/service/TsKvService.java | 385 +++++ .../common/tskv/service/TsKvServiceImpl.java | 279 +++ .../service/latest/LatestBaseService.java | 75 + .../tskv/service/latest/LatestCkService.java | 10 + .../service/latest/LatestCkServiceImpl.java | 90 + .../tskv/service/latest/LatestMyService.java | 10 + .../service/latest/LatestMyServiceImpl.java | 128 ++ .../tskv/service/latest/LatestPgService.java | 10 + .../service/latest/LatestPgServiceImpl.java | 126 ++ .../service/latest/ViewLatestCkService.java | 31 + .../latest/ViewLatestCkServiceImpl.java | 102 ++ .../tskv/service/tskv/TsKvBaseService.java | 278 +++ .../tskv/service/tskv/TsKvCkService.java | 112 ++ .../tskv/service/tskv/TsKvCkServiceImpl.java | 241 +++ .../tskv/service/tskv/TsKvMyService.java | 9 + .../tskv/service/tskv/TsKvMyServiceImpl.java | 422 +++++ .../tskv/service/tskv/TsKvPgService.java | 10 + .../tskv/service/tskv/TsKvPgServiceImpl.java | 424 +++++ .../tskv/service/tskv/ViewTsKvCkService.java | 58 + .../service/tskv/ViewTsKvCkServiceImpl.java | 292 ++++ common/util/pom.xml | 79 + .../com/thing/common/util/AfterStartUp.java | 51 + .../thread/AbstractListeningExecutor.java | 96 ++ .../common/util/thread/CallbackService.java | 67 + .../thread/ForkJoinWorkerThreadFactory.java | 27 + .../common/util/thread/ListeningExecutor.java | 40 + .../thing/common/util/thread/TBExecutors.java | 37 + .../common/util/thread/ThingExecutors.java | 29 + .../ThingForkJoinWorkerThreadFactory.java | 29 + .../util/thread/ThingThreadFactory.java | 37 + .../thing/common/util/time/DaXiaUtils.java | 179 ++ modules/alarm/pom.xml | 33 + .../controller/AlarmConfigController.java | 127 ++ .../alarm/controller/AlarmDictController.java | 117 ++ .../controller/AlarmDisposeLogController.java | 107 ++ .../controller/AlarmRuleActionController.java | 101 ++ .../alarm/controller/AlarmRuleController.java | 111 ++ .../controller/AlarmRuleEntityController.java | 95 ++ .../controller/AlarmRuleLogController.java | 118 ++ .../AlarmRuleSettingController.java | 103 ++ .../thing/alarm/alarm/dto/AlarmConfigDTO.java | 60 + .../alarm/dto/AlarmConfigDeleteParam.java | 18 + .../thing/alarm/alarm/dto/AlarmDictDTO.java | 45 + .../alarm/alarm/dto/AlarmDisposeLogDTO.java | 43 + .../com/thing/alarm/alarm/dto/AlarmJson.java | 83 + .../alarm/dto/AlarmProcessingUserDTO.java | 16 + .../alarm/alarm/dto/AlarmRuleActionCache.java | 15 + .../alarm/dto/AlarmRuleActionCacheDTO.java | 21 + .../alarm/alarm/dto/AlarmRuleActionDTO.java | 45 + .../alarm/dto/AlarmRuleActionDetailDTO.java | 40 + .../alarm/dto/AlarmRuleActionPageDTO.java | 44 + .../thing/alarm/alarm/dto/AlarmRuleDTO.java | 73 + .../alarm/alarm/dto/AlarmRuleEntityDTO.java | 53 + .../alarm/alarm/dto/AlarmRuleLogDTO.java | 62 + .../alarm/alarm/dto/AlarmRuleReportDTO.java | 46 + .../alarm/alarm/dto/AlarmRuleSettingDTO.java | 48 + .../alarm/dto/AlarmRuleSettingPageDTO.java | 36 + .../alarm/alarm/entity/AlarmConfigEntity.java | 85 + .../alarm/alarm/entity/AlarmDictData.java | 21 + .../alarm/alarm/entity/AlarmDictEntity.java | 51 + .../alarm/alarm/entity/AlarmDictType.java | 22 + .../alarm/entity/AlarmDisposeLogEntity.java | 47 + .../alarm/entity/AlarmRuleActionEntity.java | 46 + .../alarm/alarm/entity/AlarmRuleEntity.java | 75 + .../alarm/entity/AlarmRuleEntityEntity.java | 48 + .../alarm/entity/AlarmRuleLogEntity.java | 64 + .../alarm/entity/AlarmRuleSettingEntity.java | 39 + .../alarm/alarm/mapper/AlarmConfigMapper.java | 16 + .../alarm/alarm/mapper/AlarmDictMapper.java | 22 + .../alarm/mapper/AlarmDisposeLogMapper.java | 17 + .../alarm/mapper/AlarmRuleActionMapper.java | 27 + .../alarm/mapper/AlarmRuleEntityMapper.java | 27 + .../alarm/mapper/AlarmRuleLogMapper.java | 38 + .../alarm/alarm/mapper/AlarmRuleMapper.java | 17 + .../alarm/mapper/AlarmRuleSettingMapper.java | 24 + .../alarm/service/AlarmConfigService.java | 26 + .../alarm/alarm/service/AlarmDictService.java | 38 + .../alarm/service/AlarmDisposeLogService.java | 30 + .../alarm/service/AlarmRuleActionService.java | 54 + .../alarm/service/AlarmRuleEntityService.java | 57 + .../alarm/service/AlarmRuleLogService.java | 42 + .../alarm/alarm/service/AlarmRuleService.java | 99 ++ .../service/AlarmRuleSettingService.java | 31 + .../alarm/alarm/service/AlarmService.java | 27 + .../service/impl/AlarmConfigServiceImpl.java | 148 ++ .../service/impl/AlarmDictServiceImpl.java | 102 ++ .../impl/AlarmDisposeLogServiceImpl.java | 143 ++ .../impl/AlarmRuleActionServiceImpl.java | 198 +++ .../impl/AlarmRuleEntityServiceImpl.java | 115 ++ .../service/impl/AlarmRuleLogServiceImpl.java | 192 +++ .../service/impl/AlarmRuleServiceImpl.java | 340 ++++ .../impl/AlarmRuleSettingServiceImpl.java | 181 ++ .../alarm/service/impl/AlarmServiceImpl.java | 168 ++ .../alarm/cache/AlarmCacheInitializer.java | 91 + .../thing/alarm/cache/AlarmCacheService.java | 78 + .../alarm/cache/AlarmCacheServiceImpl.java | 130 ++ .../thing/alarm/cache/AlarmEventListener.java | 209 +++ .../DefaultIntegrationContext.java | 78 + .../configuration/IntegrationContext.java | 25 + .../AviatorScriptConditionEvaluator.java | 44 + .../configuration/condition/Condition.java | 17 + .../condition/ConditionEvaluator.java | 21 + .../configuration/event/ActionEvent.java | 25 + .../event/ActionEventListener.java | 38 + .../event/LogSaveActionEvent.java | 39 + .../configuration/event/MsgActionEvent.java | 51 + .../controller/MsgPushSettingController.java | 114 ++ .../thing/alarm/msgpush/dto/AlarmMsgDTO.java | 14 + .../alarm/msgpush/dto/MsgPushSettingDTO.java | 67 + .../msgpush/entity/MsgPushSettingEntity.java | 58 + .../msgpush/excel/MsgPushSettingExcel.java | 45 + .../msgpush/mapper/MsgPushSettingMapper.java | 11 + .../service/MsgPushSettingService.java | 38 + .../impl/MsgPushSettingServiceImpl.java | 197 +++ .../resources/mapper/AlarmConfigMapper.xml | 26 + .../main/resources/mapper/AlarmDictMapper.xml | 23 + .../mapper/AlarmDisposeLogMapper.xml | 19 + .../mapper/AlarmRuleActionMapper.xml | 78 + .../mapper/AlarmRuleEntityMapper.xml | 37 + .../resources/mapper/AlarmRuleLogMapper.xml | 114 ++ .../main/resources/mapper/AlarmRuleMapper.xml | 26 + .../mapper/AlarmRuleSettingMapper.xml | 45 + .../resources/mapper/MsgPushSettingMapper.xml | 26 + modules/calculation/pom.xml | 39 + .../cache/CalcConfigCacheInitializer.java | 33 + .../cache/CalculationCacheInitializer.java | 57 + .../cache/CalculationCacheService.java | 31 + .../cache/CalculationCacheServiceImpl.java | 54 + .../cache/CalculationEventListener.java | 101 ++ .../controller/CalculationController.java | 52 + .../CalculationFormulaLogController.java | 90 + .../CalculationFormulaSettingController.java | 113 ++ .../CalculationUsageDictController.java | 33 + .../calculation/dto/CalculationAttr.java | 23 + .../dto/CalculationConditionForm.java | 41 + .../calculation/dto/CalculationForm.java | 41 + .../dto/CalculationFormulaAttributeDTO.java | 48 + .../dto/CalculationFormulaLogDTO.java | 35 + .../dto/CalculationFormulaSettingDTO.java | 105 ++ .../calculation/dto/CalculationJson.java | 63 + .../calculation/dto/CalculationParam.java | 59 + .../calculation/dto/CalculationResultDTO.java | 22 + .../dto/CalculationSettingCache.java | 40 + .../calculation/dto/CalculationTrialForm.java | 48 + .../dto/CalculationTrialResult.java | 37 + .../dto/CalculationUsageDictRequest.java | 37 + .../calculation/dto/RecalculationForm.java | 31 + .../CalculationFormulaAttributeEntity.java | 59 + .../entity/CalculationFormulaLogEntity.java | 67 + .../CalculationFormulaSettingEntity.java | 124 ++ .../CalculationFormulaAttributeMapper.java | 27 + .../mapper/CalculationFormulaLogMapper.java | 25 + .../CalculationFormulaSettingMapper.java | 25 + .../CalculationFormulaAttributeService.java | 47 + .../service/CalculationFormulaLogService.java | 63 + .../CalculationFormulaSettingService.java | 47 + .../service/CalculationService.java | 36 + .../service/CalculationUsageDictService.java | 7 + ...alculationFormulaAttributeServiceImpl.java | 105 ++ .../CalculationFormulaLogServiceImpl.java | 168 ++ .../CalculationFormulaSettingServiceImpl.java | 455 +++++ .../service/impl/CalculationServiceImpl.java | 453 +++++ .../impl/CalculationUsageDictServiceImpl.java | 38 + .../controller/CalcLogController.java | 80 + .../CalcTargetConfigController.java | 165 ++ .../com/thing/calculation/dto/CalcLogDTO.java | 72 + .../calculation/dto/CalcSourceConfigDTO.java | 44 + .../calculation/dto/CalcTargetConfigDTO.java | 48 + .../calculation/dto/ExecuteCalcRequest.java | 37 + .../calculation/dto/LogSourceInfoValue.java | 28 + .../thing/calculation/dto/ReCalcRequest.java | 39 + .../calculation/dto/TestCalcRequest.java | 50 + .../calculation/dto/TestCalcResponse.java | 27 + .../calculation/entity/CalcLogEntity.java | 195 +++ .../entity/CalcSourceConfigEntity.java | 52 + .../entity/CalcTargetConfigEntity.java | 63 + .../enumeration/CalcConfigType.java | 67 + .../calculation/enumeration/CalcStatus.java | 36 + .../calculation/excel/CalcConfigExcel.java | 99 ++ .../thing/calculation/excel/CalcLogExcel.java | 61 + .../excel/CalculationTypeExcel.java | 44 + .../calculation/excel/MappingTypeExcel.java | 56 + .../handler/CalcExecuteHandler.java | 165 ++ .../handler/CalcLogSaveHandler.java | 305 ++++ .../CalcHistoryLogSyncInitializer.java | 58 + .../listener/CalcLogSaveEventListener.java | 61 + .../calculation/mapper/CalcLogMapper.java | 20 + .../mapper/CalcSourceConfigMapper.java | 16 + .../mapper/CalcTargetConfigMapper.java | 16 + .../calculation/service/CalcLogService.java | 40 + .../service/CalcSourceConfigService.java | 23 + .../service/CalcTargetConfigService.java | 48 + .../service/impl/CalcLogServiceImpl.java | 186 ++ .../impl/CalcSourceConfigServiceImpl.java | 56 + .../impl/CalcTargetConfigServiceImpl.java | 408 +++++ .../resources/mapper/calc/CalcLogMapper.xml | 19 + .../CalculationFormulaAttributeMapper.xml | 31 + .../CalculationFormulaLogMapper.xml | 32 + .../CalculationFormulaSettingMapper.xml | 62 + modules/carbon-public/pom.xml | 25 + .../thing/carbon/pub/api/CertificateApi.java | 217 +++ .../carbon/pub/api/MaterialFactorApi.java | 79 + .../carbon/pub/api/ProductionModelApi.java | 150 ++ .../carbon/pub/api/ProductionResultApi.java | 120 ++ .../carbon/pub/constant/ApiConstant.java | 12 + .../pub/constant/CarbonFactorConstant.java | 11 + .../carbon/pub/constant/PointsConstant.java | 15 + .../constant/ReportSourceTypeConstant.java | 12 + .../CarbonPubCertificateController.java | 88 + .../controller/CarbonPubEngEffController.java | 105 ++ .../CarbonPubFootprintLibController.java | 65 + .../CarbonPubMaterialFactorController.java | 107 ++ .../CarbonPubPointsLogsController.java | 88 + .../CarbonPubPointsRuleController.java | 68 + .../CarbonPubProductionModelController.java | 171 ++ ...onPubProductionReportConfigController.java | 124 ++ .../CarbonPubProductionReportController.java | 128 ++ .../CarbonPubProductionResultController.java | 174 ++ .../CarbonPubSupplierController.java | 130 ++ .../IotCarbonEnterpriseAuthController.java | 104 ++ .../pub/dto/CarbonPubCertificateDTO.java | 48 + .../carbon/pub/dto/CarbonPubEngEffDTO.java | 49 + .../pub/dto/CarbonPubFootprintInfo.java | 58 + .../pub/dto/CarbonPubLibRecordBaseInfo.java | 54 + .../dto/CarbonPubLibRecordBaseInfoOnYear.java | 71 + .../pub/dto/CarbonPubLibRecordDispose.java | 23 + .../carbon/pub/dto/CarbonPubLibRecordMPT.java | 22 + .../carbon/pub/dto/CarbonPubLibRecordPM.java | 27 + .../carbon/pub/dto/CarbonPubLibRecordPT.java | 19 + .../carbon/pub/dto/CarbonPubLibRecordPU.java | 19 + .../carbon/pub/dto/CarbonPubLibRecordR.java | 199 +++ .../pub/dto/CarbonPubLibRecordSimpleAgg.java | 100 ++ .../dto/CarbonPubMaterialFactorApiDTO.java | 14 + .../pub/dto/CarbonPubMaterialFactorDTO.java | 50 + .../dto/CarbonPubMaterialFactorUseDTO.java | 29 + .../pub/dto/CarbonPubPointsLogsDTO.java | 32 + .../pub/dto/CarbonPubPointsRuleDTO.java | 35 + .../pub/dto/CarbonPubProductionIdName.java | 19 + .../pub/dto/CarbonPubProductionModelDTO.java | 56 + .../CarbonPubProductionReportConfigDTO.java | 36 + .../pub/dto/CarbonPubProductionReportDTO.java | 70 + .../pub/dto/CarbonPubProductionResultDTO.java | 129 ++ .../dto/CarbonPubProductionResultReport.java | 74 + .../carbon/pub/dto/CarbonPubSupplierDTO.java | 51 + .../pub/dto/CarbonPubSupplierDetail.java | 32 + .../pub/dto/CarbonPubSupplierProduct.java | 36 + .../pub/dto/IotCarbonEnterpriseAuthDTO.java | 56 + .../carbon/pub/dto/TranslateRequest.java | 81 + .../carbon/pub/dto/TranslateResponse.java | 42 + .../entity/CarbonPubCertificateEntity.java | 67 + .../pub/entity/CarbonPubEngEffEntity.java | 78 + .../entity/CarbonPubMaterialFactorEntity.java | 85 + .../CarbonPubMaterialFactorUseEntity.java | 40 + .../pub/entity/CarbonPubPointsLogsEntity.java | 47 + .../pub/entity/CarbonPubPointsRuleEntity.java | 52 + .../CarbonPubProductionModelEntity.java | 89 + ...CarbonPubProductionReportConfigEntity.java | 55 + .../CarbonPubProductionReportEntity.java | 82 + .../CarbonPubProductionResultEntity.java | 173 ++ .../pub/entity/CarbonPubSupplierEntity.java | 53 + .../entity/IotCarbonEnterpriseAuthEntity.java | 95 ++ .../carbon/pub/filter/CarbonPubFilter.java | 166 ++ .../pub/filter/CarbonPubFilterConfig.java | 33 + .../pub/filter/CarbonPubRequestWrapper.java | 102 ++ .../pub/filter/CarbonPubResponseWrapper.java | 65 + .../pub/listener/SupplierCreateListener.java | 41 + .../mapper/CarbonPubCertificateMapper.java | 16 + .../pub/mapper/CarbonPubEngEffMapper.java | 16 + .../mapper/CarbonPubMaterialFactorMapper.java | 16 + .../CarbonPubMaterialFactorUseMapper.java | 16 + .../pub/mapper/CarbonPubPointsLogsMapper.java | 16 + .../pub/mapper/CarbonPubPointsRuleMapper.java | 16 + .../CarbonPubProductionModelMapper.java | 16 + ...CarbonPubProductionReportConfigMapper.java | 16 + .../CarbonPubProductionReportMapper.java | 21 + .../CarbonPubProductionResultMapper.java | 14 + .../pub/mapper/CarbonPubSupplierMapper.java | 31 + .../mapper/IotCarbonEnterpriseAuthMapper.java | 16 + .../service/CarbonPubCertificateService.java | 28 + .../pub/service/CarbonPubEngEffService.java | 17 + .../service/CarbonPubFootprintLibService.java | 21 + .../CarbonPubMaterialFactorService.java | 28 + .../CarbonPubMaterialFactorUseService.java | 15 + .../service/CarbonPubPointsLogsService.java | 14 + .../service/CarbonPubPointsRuleService.java | 17 + .../CarbonPubProductionModelService.java | 44 + ...arbonPubProductionReportConfigService.java | 18 + .../CarbonPubProductionReportService.java | 32 + .../CarbonPubProductionResultService.java | 78 + .../pub/service/CarbonPubSupplierService.java | 37 + .../IotCarbonEnterpriseAuthService.java | 21 + .../impl/CarbonPubCertificateServiceImpl.java | 138 ++ .../impl/CarbonPubEngEffServiceImpl.java | 69 + .../CarbonPubFootprintLibServiceImpl.java | 102 ++ .../CarbonPubMaterialFactorServiceImpl.java | 237 +++ ...CarbonPubMaterialFactorUseServiceImpl.java | 36 + .../impl/CarbonPubPointsLogsServiceImpl.java | 31 + .../impl/CarbonPubPointsRuleServiceImpl.java | 39 + .../CarbonPubProductionModelServiceImpl.java | 268 +++ ...nPubProductionReportConfigServiceImpl.java | 65 + .../CarbonPubProductionReportServiceImpl.java | 191 +++ .../CarbonPubProductionResultServiceImpl.java | 675 ++++++++ .../impl/CarbonPubSupplierServiceImpl.java | 258 +++ .../IotCarbonEnterpriseAuthServiceImpl.java | 103 ++ .../pub/task/CarbonReportGenerateTask.java | 134 ++ .../CarbonPubProductionReportMapper.xml | 18 + .../mapper/CarbonPubSupplierMapper.xml | 94 ++ modules/carbon-track/pom.xml | 45 + .../dto/IotCarbonIndirectEnergyDTO.java | 52 + .../entity/IotCarbonIndirectEnergyEntity.java | 84 + .../mapper/IotCarbonIndirectEnergyMapper.java | 16 + .../IotCarbonIndirectEnergyService.java | 22 + .../IotCarbonIndirectEnergyServiceImpl.java | 76 + .../controller/IotCarbonBomController.java | 172 ++ .../carbontrack/bom/dto/BomCarbonReq.java | 15 + .../carbontrack/bom/dto/IotCarbonBomDTO.java | 65 + .../bom/dto/IotCarbonBomExcel.java | 68 + .../bom/dto/IotCarbonBomExcelImport.java | 14 + .../bom/dto/IotCarbonBomImport.java | 16 + .../bom/entity/IotCarbonBomEntity.java | 104 ++ .../bom/mapper/IotCarbonBomMapper.java | 23 + .../bom/service/IotCarbonBomService.java | 45 + .../service/impl/IotCarbonBomServiceImpl.java | 320 ++++ .../IotCarbonCertificateController.java | 126 ++ .../certification/dto/CarbonStageValue.java | 21 + .../dto/IotCarbonCertificateDTO.java | 96 ++ .../certification/dto/SimpleProductInfo.java | 99 ++ .../entity/IotCarbonCertificateEntity.java | 75 + .../mapper/IotCarbonCertificateMapper.java | 14 + .../service/IotCarbonCertificateService.java | 33 + .../impl/IotCarbonCertificateServiceImpl.java | 234 +++ .../controller/ReconnectController.java | 37 + .../ck/datacleaning/dto/CkOrderBomDto.java | 45 + .../ck/datacleaning/dto/CkOrderDto.java | 27 + .../jsonInit/DiscardJsonInit.java | 27 + .../jsonInit/ProductionJsonInit.java | 30 + .../datacleaning/jsonInit/SetpsJsonInit.java | 28 + .../jsonInit/TransportJsonInit.java | 28 + .../ck/datacleaning/jsonInit/UseJsonInit.java | 27 + .../datacleaning/mapper/CkOrderBomMapper.java | 18 + .../ck/datacleaning/mapper/CkOrderMapper.java | 15 + .../ck/datacleaning/service/OrderService.java | 16 + .../service/impl/OrderServiceImpl.java | 429 +++++ .../ck/datacleaning/task/TestController.java | 40 + .../IotCarbonProductionCustomController.java | 62 + .../dto/IotCarbonProductionCustomDTO.java | 30 + .../IotCarbonProductionCustomEntity.java | 43 + .../IotCarbonProductionCustomMapper.java | 16 + .../IotCarbonProductionCustomService.java | 20 + .../IotCarbonProductionCustomServiceImpl.java | 36 + .../standardcal/IndirectEnergyEvent.java | 21 + .../standardcal/ProductionProcessEvent.java | 25 + .../ProductionProcessStatusEvent.java | 20 + .../standardcal/ProductionResultEvent.java | 18 + .../listener/IndirectEnergyListener.java | 188 +++ .../listener/ProductionProcessListener.java | 167 ++ .../ProductionProcessStatusListener.java | 49 + .../listener/ProductionResultListener.java | 339 ++++ .../IotCarbonOutboundConfigController.java | 160 ++ .../dto/IotCarbonOutboundConfigDTO.java | 125 ++ .../entity/IotCarbonOutboundConfigEntity.java | 79 + .../mapper/IotCarbonOutboundConfigMapper.java | 17 + .../IotCarbonOutboundConfigService.java | 14 + .../IotCarbonOutboundConfigServiceImpl.java | 34 + ...bonProductionProcessVarietyController.java | 97 ++ .../IotCarbonProductionProcessVarietyDTO.java | 44 + ...tCarbonProductionProcessVarietyEntity.java | 74 + ...tCarbonProductionProcessVarietyMapper.java | 32 + ...CarbonProductionProcessVarietyService.java | 22 + ...onProductionProcessVarietyServiceImpl.java | 41 + .../controller/UsagePlanController.java | 52 + .../produce/dto/UsagePlanFormParam.java | 77 + .../produce/dto/UsagePlanReportParam.java | 96 ++ .../produce/dto/UsagePlanReportReq.java | 32 + .../produce/service/UsagePlanService.java | 14 + .../service/impl/UsagePlanServiceImpl.java | 150 ++ .../IotCarbonProductionVarietyController.java | 158 ++ .../dto/IotCarbonProductionVarietyDTO.java | 313 ++++ .../carbontrack/production/dto/NormInfo.java | 12 + .../production/dto/NormPageParam.java | 19 + .../production/dto/ProductionEnergyInfo.java | 23 + .../production/dto/ProductionNormInfo.java | 48 + .../production/dto/UsageNormParam.java | 72 + .../IotCarbonProductionVarietyEntity.java | 106 ++ .../IotCarbonProductionVarietyMapper.java | 21 + .../IotCarbonProductionVarietyService.java | 55 + ...IotCarbonProductionVarietyServiceImpl.java | 443 +++++ .../IotCarbonProductionRecordController.java | 116 ++ .../dto/IotCarbonProductionRecordDTO.java | 73 + .../productionRecord/dto/OrderParam.java | 33 + .../dto/ProductionOverviewReq.java | 19 + .../productionRecord/dto/ProductionPage.java | 61 + .../productionRecord/dto/ProductionParam.java | 48 + .../productionRecord/dto/ProductionReq.java | 33 + .../dto/ProductionResult.java | 60 + .../productionRecord/dto/QuantityDto.java | 11 + .../productionRecord/dto/RecordReq.java | 13 + .../IotCarbonProductionRecordEntity.java | 96 ++ .../IotCarbonProductionRecordMapper.java | 44 + .../IotCarbonProductionRecordService.java | 53 + .../IotCarbonProductionRecordServiceImpl.java | 338 ++++ .../IotCarbonProductionResultController.java | 152 ++ .../productionResult/dto/AggCarbon.java | 222 +++ .../productionResult/dto/DisposeDetail.java | 61 + .../dto/IotCarbonProductionResultDTO.java | 126 ++ .../dto/LotCarbonBaseInfo.java | 54 + .../dto/LotCarbonBaseInfoOnYear.java | 71 + .../dto/LotCarbonDispose.java | 18 + .../productionResult/dto/LotCarbonMPT.java | 21 + .../productionResult/dto/LotCarbonPM.java | 23 + .../productionResult/dto/LotCarbonPT.java | 18 + .../productionResult/dto/LotCarbonPU.java | 18 + .../productionResult/dto/LotCarbonR.java | 205 +++ .../dto/LotCarbonSimpleAgg.java | 101 ++ .../productionResult/dto/MptDetail.java | 71 + .../productionResult/dto/PIndirectDetail.java | 67 + .../productionResult/dto/PmDetail.java | 38 + .../productionResult/dto/PmProcessDetail.java | 62 + .../productionResult/dto/PtDetail.java | 63 + .../productionResult/dto/PuDetail.java | 52 + .../IotCarbonProductionResultEntity.java | 223 +++ .../IotCarbonProductionResultMapper.java | 31 + .../IotCarbonProductionResultService.java | 53 + .../IotCarbonProductionResultServiceImpl.java | 726 ++++++++ .../pub/controller/ConfigController.java | 29 + .../controller/ModelCommunityController.java | 122 ++ .../carbontrack/pub/dto/MaterialJsonBean.java | 36 + .../carbontrack/pub/dto/ProcessJsonBean.java | 27 + .../carbontrack/pub/dto/ProductJsonBean.java | 139 ++ .../pub/dto/ProductionModelUploadDTO.java | 81 + .../pub/dto/ProductionResultSyncDTO.java | 173 ++ .../CertificatePointsUpdateRequest.java | 61 + .../request/MaterialFactorDetailRequest.java | 63 + .../request/MaterialFactorPageRequest.java | 85 + ...ProductionIndustryCategoryListRequest.java | 66 + .../ProductionIndustryTypeListRequest.java | 57 + .../request/ProductionModelDetailRequest.java | 79 + .../request/ProductionModelPageRequest.java | 94 ++ .../request/ProductionModelUploadRequest.java | 61 + .../request/ProductionResultSyncRequest.java | 63 + .../pub/response/DefaultPostResponse.java | 23 + .../pub/response/EncryptedResponse.java | 39 + .../pub/service/CertificateSyncService.java | 15 + .../pub/service/MaterialFactorService.java | 18 + .../pub/service/ProductionModelService.java | 29 + .../pub/service/ProductionResultService.java | 18 + .../service/impl/BaseCarbonApiService.java | 49 + .../impl/CertificateSyncServiceImpl.java | 28 + .../impl/MaterialFactorServiceImpl.java | 44 + .../impl/ProductionModelServiceImpl.java | 110 ++ .../impl/ProductionResultServiceImpl.java | 45 + .../pub/utils/CarbonPubRequestUtil.java | 38 + .../controller/IotCarbonRatioController.java | 201 +++ .../ratio/dto/CarBonRatioResult.java | 13 + .../ratio/dto/IotCarbonRatioDTO.java | 62 + .../ratio/entity/IotCarbonRatioEntity.java | 65 + .../ratio/mapper/IotCarbonRatioMapper.java | 26 + .../ratio/service/IotCarbonRatioService.java | 46 + .../impl/IotCarbonRatioServiceImpl.java | 272 +++ ...arbonProductionReportConfigController.java | 87 + .../IotCarbonProductionReportController.java | 122 ++ .../IotCarbonProductionReportConfigDTO.java | 34 + .../dto/IotCarbonProductionReportDTO.java | 61 + .../report/dto/IotCarbonResultReport.java | 93 + ...IotCarbonProductionReportConfigEntity.java | 51 + .../IotCarbonProductionReportEntity.java | 72 + ...IotCarbonProductionReportConfigMapper.java | 16 + .../IotCarbonProductionReportMapper.java | 16 + ...otCarbonProductionReportConfigService.java | 16 + .../IotCarbonProductionReportService.java | 26 + ...rbonProductionReportConfigServiceImpl.java | 48 + .../IotCarbonProductionReportServiceImpl.java | 161 ++ .../controller/RoutematrixController.java | 40 + .../routematrix/dto/PlaceParam.java | 33 + .../carbontrack/routematrix/dto/PlaceReq.java | 13 + .../routematrix/dto/RoutematrixReq.java | 13 + .../service/RoutematrixService.java | 13 + .../service/impl/RoutematrixServiceImpl.java | 145 ++ .../controller/IotCarbonScreenController.java | 133 ++ .../carbontrack/screen/dto/CarbonSummary.java | 37 + .../screen/dto/ProductCarbonLifecycle.java | 42 + .../dto/ProductCarbonLifecycleDetail.java | 149 ++ .../carbontrack/screen/dto/ProductInfo.java | 40 + .../screen/dto/SimpleProductionCarbon.java | 32 + .../service/IotCarbonScreenService.java | 12 + .../impl/IotCarbonScreenServiceImpl.java | 149 ++ .../controller/IotCarbonShareController.java | 70 + .../IotCarbonShareDetailsController.java | 97 ++ .../share/dto/IotCarbonShareDTO.java | 44 + .../share/dto/IotCarbonShareDetailsDTO.java | 56 + .../entity/IotCarbonShareDetailsEntity.java | 74 + .../share/entity/IotCarbonShareEntity.java | 50 + .../mapper/IotCarbonShareDetailsMapper.java | 16 + .../share/mapper/IotCarbonShareMapper.java | 16 + .../service/IotCarbonShareDetailsService.java | 14 + .../share/service/IotCarbonShareService.java | 16 + .../IotCarbonShareDetailsServiceImpl.java | 36 + .../impl/IotCarbonShareServiceImpl.java | 36 + .../IotCarbonProcessStepsController.java | 125 ++ .../steps/dto/IotCarbonProcessResult.java | 31 + .../steps/dto/IotCarbonProcessStepsDTO.java | 38 + .../entity/IotCarbonProcessStepsEntity.java | 62 + .../mapper/IotCarbonProcessStepsMapper.java | 28 + .../service/IotCarbonProcessStepsService.java | 38 + .../IotCarbonProcessStepsServiceImpl.java | 183 ++ .../IotCarbonProductionSyncController.java | 97 ++ .../sync/dto/IotCarbonProductionSyncDTO.java | 42 + .../entity/IotCarbonProductionSyncEntity.java | 35 + .../mapper/IotCarbonProductionSyncMapper.java | 16 + .../IotCarbonProductionSyncService.java | 14 + .../IotCarbonProductionSyncServiceImpl.java | 28 + .../task/CarbonTrackReportGenerateTask.java | 134 ++ .../carbontrack/task/DataSharingService.java | 106 ++ .../carbontrack/task/IndirectEnergyTask.java | 180 ++ .../task/ProductionProcessStatusTask.java | 40 + .../task/ProductionProcessTask.java | 159 ++ .../task/ProductionResultTask.java | 333 ++++ .../IotCarbonUseConfigController.java | 123 ++ .../useConfig/dto/IotCarbonUseConfigDTO.java | 64 + .../useConfig/dto/RecalculateByTimeDTO.java | 19 + .../entity/IotCarbonUseConfigEntity.java | 73 + .../mapper/IotCarbonUseConfigMapper.java | 28 + .../service/IotCarbonUseConfigService.java | 36 + .../impl/IotCarbonUseConfigServiceImpl.java | 114 ++ .../src/main/resources/application-track.yml | 2 + .../src/main/resources/discard.json | 9 + .../resources/mapper/CkOrderBomMapper.xml | 45 + .../main/resources/mapper/CkOrderMapper.xml | 26 + .../resources/mapper/IotCarbonBomMapper.xml | 39 + .../mapper/IotCarbonIndirectEnergyMapper.xml | 7 + .../mapper/IotCarbonOutboundConfigMapper.xml | 14 + .../mapper/IotCarbonProcessStepsMapper.xml | 92 + ...otCarbonProductionProcessVarietyMapper.xml | 80 + .../IotCarbonProductionRecordMapper.xml | 327 ++++ .../IotCarbonProductionResultMapper.xml | 140 ++ .../mapper/IotCarbonProductionSyncMapper.xml | 7 + .../IotCarbonProductionVarietyMapper.xml | 25 + .../resources/mapper/IotCarbonRatioMapper.xml | 45 + .../mapper/IotCarbonUseConfigMapper.xml | 93 + .../src/main/resources/production.json | 17 + .../src/main/resources/setps.json | 37 + .../src/main/resources/transport.json | 5 + .../carbon-track/src/main/resources/use.json | 7 + modules/configuration/pom.xml | 60 + .../controller/IotBoardManageController.java | 161 ++ .../board/dto/IotBoardManageDTO.java | 67 + .../board/entity/IotBoardManageEntity.java | 56 + .../board/excel/IotBoardManageExcel.java | 28 + .../board/mapper/IotBoardManageMapper.java | 17 + .../board/service/IotBoardManageService.java | 42 + .../impl/IotBoardManageServiceImpl.java | 438 +++++ .../IotConfigurationDesigController.java | 74 + .../dto/IotConfigurationDesigDTO.java | 56 + .../entity/IotConfigurationDesigEntity.java | 82 + .../mapper/IotConfigurationDesigMapper.java | 16 + .../service/IotConfigurationDesigService.java | 28 + .../IotConfigurationDesigServiceImpl.java | 135 ++ .../IotNewSourceMaterialController.java | 149 ++ .../dto/IotNewSourceMaterialDTO.java | 72 + .../entity/IotNewSourceMaterialEntity.java | 55 + .../mapper/IotNewSourceMaterialMapper.java | 22 + .../service/IotNewSourceMaterialService.java | 35 + .../impl/IotNewSourceMaterialServiceImpl.java | 243 +++ .../IotSectionDetailController.java | 163 ++ .../section/dto/GroupIofoDTO.java | 18 + .../section/dto/IotSectionDetailDTO.java | 97 ++ .../section/dto/IotSectionGroupDTO.java | 55 + .../section/dto/SectionGroupInfoDTO.java | 27 + .../entity/IotSectionDetailEntity.java | 87 + .../section/excel/IotSectionDetailExcel.java | 36 + .../mapper/IotSectionDetailMapper.java | 27 + .../service/IotSectionDetailService.java | 40 + .../impl/IotSectionDetailServiceImpl.java | 298 ++++ .../material/IotNewSourceMaterialMapper.xml | 25 + .../mapper/section/IotSectionDetailMapper.xml | 49 + modules/dequeue/pom.xml | 28 + .../thing/queue/AbstractConsumerService.java | 104 ++ .../queue/DefaultCoreConsumerService.java | 392 +++++ .../com/thing/queue/QueueConfiguration.java | 76 + .../controller/QueueLogController.java | 113 ++ .../controller/QueueMsgLogController.java | 97 ++ .../thing/queue/modules/dto/QueueLogDTO.java | 45 + .../queue/modules/dto/QueueMsgLogDTO.java | 29 + .../queue/modules/entity/QueueLogEntity.java | 46 + .../modules/entity/QueueMsgLogEntity.java | 35 + .../queue/modules/excel/QueueLogExcel.java | 28 + .../queue/modules/excel/QueueMsgLogExcel.java | 29 + .../queue/modules/mapper/QueueLogMapper.java | 23 + .../modules/mapper/QueueMsgLogMapper.java | 23 + .../modules/service/QueueLogService.java | 29 + .../modules/service/QueueMsgLogService.java | 28 + .../service/impl/QueueLogServiceImpl.java | 58 + .../service/impl/QueueMsgLogServiceImpl.java | 60 + modules/equipment/pom.xml | 28 + .../controller/CheckResultController.java | 118 ++ .../eq/checkresult/dto/CheckResultDTO.java | 60 + .../checkresult/entity/CheckResultEntity.java | 79 + .../checkresult/excel/CheckResultExcel.java | 31 + .../checkresult/mapper/CheckResultMapper.java | 36 + .../service/CheckResultService.java | 57 + .../service/impl/CheckResultServiceImpl.java | 131 ++ .../eq/eqbxwx/controller/EqBxController.java | 423 +++++ .../eqbxwx/controller/EqWxPlanController.java | 463 +++++ .../controller/EqWxReplacementController.java | 115 ++ .../com/thing/eq/eqbxwx/dto/BxInfoDTO.java | 54 + .../com/thing/eq/eqbxwx/dto/CommonDTO.java | 18 + .../java/com/thing/eq/eqbxwx/dto/EqBxDTO.java | 144 ++ .../com/thing/eq/eqbxwx/dto/EqBxResDTO.java | 21 + .../java/com/thing/eq/eqbxwx/dto/EqWxDTO.java | 59 + .../com/thing/eq/eqbxwx/dto/EqWxInfoRes.java | 30 + .../thing/eq/eqbxwx/dto/EqWxPlanAddDTO.java | 23 + .../com/thing/eq/eqbxwx/dto/EqWxPlanDTO.java | 98 ++ .../eq/eqbxwx/dto/EqWxPlanUpdateDTO.java | 18 + .../eq/eqbxwx/dto/EqWxReplacementDTO.java | 28 + .../eq/eqbxwx/dto/ReplaceMentUseDTO.java | 18 + .../com/thing/eq/eqbxwx/dto/WxUseDTO.java | 14 + .../thing/eq/eqbxwx/entity/EqBxEntity.java | 188 +++ .../thing/eq/eqbxwx/entity/EqWxEntity.java | 102 ++ .../eq/eqbxwx/entity/EqWxPlanEntity.java | 121 ++ .../eqbxwx/entity/EqWxReplacementEntity.java | 39 + .../com/thing/eq/eqbxwx/excel/BaseExcel.java | 25 + .../com/thing/eq/eqbxwx/excel/EqBxExcel.java | 57 + .../com/thing/eq/eqbxwx/excel/EqWxExcel.java | 60 + .../thing/eq/eqbxwx/excel/EqWxPlanExcel.java | 55 + .../eq/eqbxwx/excel/EqWxReplacementExcel.java | 28 + .../thing/eq/eqbxwx/mapper/EqBxMapper.java | 38 + .../thing/eq/eqbxwx/mapper/EqWxMapper.java | 17 + .../eq/eqbxwx/mapper/EqWxPlanMapper.java | 22 + .../eqbxwx/mapper/EqWxReplacementMapper.java | 17 + .../thing/eq/eqbxwx/service/EqBxService.java | 48 + .../eq/eqbxwx/service/EqWxPlanService.java | 42 + .../service/EqWxReplacementService.java | 23 + .../thing/eq/eqbxwx/service/EqWxService.java | 15 + .../eqbxwx/service/impl/EqBxServiceImpl.java | 326 ++++ .../service/impl/EqWxPlanServiceImpl.java | 359 ++++ .../impl/EqWxReplacementServiceImpl.java | 68 + .../eqbxwx/service/impl/EqWxServiceImpl.java | 30 + .../eq/eqby/controller/EqByController.java | 283 ++++ .../eqby/controller/EqByDetailController.java | 119 ++ .../eqby/controller/EqByPlanController.java | 200 +++ .../controller/EqByTemplateController.java | 146 ++ .../EqByTemplateDetailController.java | 127 ++ .../eqby/controller/SendMailController.java | 41 + .../java/com/thing/eq/eqby/dto/EqByDTO.java | 83 + .../com/thing/eq/eqby/dto/EqByDetailDTO.java | 45 + .../com/thing/eq/eqby/dto/EqByPlanDTO.java | 97 ++ .../thing/eq/eqby/dto/EqByPlanPartDTO.java | 40 + .../eq/eqby/dto/EqByPlanPartInfoDTO.java | 38 + .../thing/eq/eqby/dto/EqByPlanStatusDTO.java | 29 + .../thing/eq/eqby/dto/EqByTemplateDTO.java | 49 + .../eq/eqby/dto/EqByTemplateDetailDTO.java | 42 + .../java/com/thing/eq/eqby/dto/EqInfo.java | 39 + .../eq/eqby/entity/EqByDetailEntity.java | 68 + .../com/thing/eq/eqby/entity/EqByEntity.java | 103 ++ .../thing/eq/eqby/entity/EqByPlanEntity.java | 127 ++ .../eq/eqby/entity/EqByPlanPartEntity.java | 56 + .../eqby/entity/EqByTemplateDetailEntity.java | 56 + .../eq/eqby/entity/EqByTemplateEntity.java | 69 + .../thing/eq/eqby/excel/EqByDetailExcel.java | 42 + .../com/thing/eq/eqby/excel/EqByExcel.java | 50 + .../thing/eq/eqby/excel/EqByPlanExcel.java | 52 + .../eqby/excel/EqByTemplateDetailExcel.java | 40 + .../eq/eqby/excel/EqByTemplateExcel.java | 41 + .../eq/eqby/mapper/EqByDetailMapper.java | 26 + .../com/thing/eq/eqby/mapper/EqByMapper.java | 28 + .../thing/eq/eqby/mapper/EqByPlanMapper.java | 38 + .../eq/eqby/mapper/EqByPlanPartMapper.java | 27 + .../eqby/mapper/EqByTemplateDetailMapper.java | 27 + .../eq/eqby/mapper/EqByTemplateMapper.java | 30 + .../eq/eqby/service/EqByDetailService.java | 23 + .../eq/eqby/service/EqByPlanPartService.java | 26 + .../eq/eqby/service/EqByPlanService.java | 47 + .../thing/eq/eqby/service/EqByService.java | 37 + .../service/EqByTemplateDetailService.java | 26 + .../eq/eqby/service/EqByTemplateService.java | 33 + .../service/impl/EqByDetailServiceImpl.java | 48 + .../service/impl/EqByPlanPartServiceImpl.java | 53 + .../service/impl/EqByPlanServiceImpl.java | 425 +++++ .../eq/eqby/service/impl/EqByServiceImpl.java | 492 ++++++ .../impl/EqByTemplateDetailServiceImpl.java | 47 + .../service/impl/EqByTemplateServiceImpl.java | 134 ++ .../controller/EqCheckSettingController.java | 119 ++ .../controller/EqCheckStandardController.java | 226 +++ .../EqCheckStandardDetailController.java | 137 ++ .../EqPatrolCheckPlanController.java | 640 +++++++ .../EqPatrolCheckRecordController.java | 665 ++++++++ .../controller/EqSpotCheckPlanController.java | 322 ++++ .../EqSpotCheckRecordController.java | 358 ++++ .../thing/eq/eqcheck/dto/CheckResultRes.java | 22 + .../java/com/thing/eq/eqcheck/dto/DataVo.java | 17 + .../eq/eqcheck/dto/DeviceBaseInfoDTO.java | 38 + .../eq/eqcheck/dto/DeviceCheckRecordVo.java | 35 + .../eq/eqcheck/dto/EqCheckSettingDTO.java | 38 + .../eq/eqcheck/dto/EqCheckStandardDTO.java | 56 + .../eqcheck/dto/EqCheckStandardDetailDTO.java | 39 + .../dto/EqCheckStandardDetailResultDTO.java | 43 + .../eq/eqcheck/dto/EqPatrolCheckPlanDTO.java | 132 ++ .../eq/eqcheck/dto/EqPatrolCheckPlanRes.java | 25 + .../eqcheck/dto/EqPatrolCheckPlanResDTO.java | 25 + .../eqcheck/dto/EqPatrolCheckRecordDTO.java | 74 + .../dto/EqPatrolCheckRecordResDTO.java | 33 + .../dto/EqPatrolCheckRecordUpdateDTO.java | 18 + .../eq/eqcheck/dto/EqSpotCheckPlanDTO.java | 62 + .../eqcheck/dto/EqSpotCheckPlanParamDTO.java | 48 + .../eq/eqcheck/dto/EqSpotCheckRecordDTO.java | 70 + .../thing/eq/eqcheck/dto/ExecutePlanDTO.java | 35 + .../eq/eqcheck/dto/SepcialTsKvParam.java | 20 + .../eqcheck/dto/StandardCheckResultDTO.java | 25 + .../com/thing/eq/eqcheck/dto/TbDataDTO.java | 18 + .../com/thing/eq/eqcheck/dto/ThingDTO.java | 44 + .../thing/eq/eqcheck/dto/TimeValueParam.java | 15 + .../thing/eq/eqcheck/dto/UpdateStatusVo.java | 26 + .../eqcheck/entity/EqCheckSettingEntity.java | 63 + .../entity/EqCheckStandardDetailEntity.java | 68 + .../eqcheck/entity/EqCheckStandardEntity.java | 72 + .../entity/EqPatrolCheckPlanEntity.java | 137 ++ .../entity/EqPatrolCheckRecordEntity.java | 89 + .../eqcheck/entity/EqSpotCheckPlanEntity.java | 64 + .../entity/EqSpotCheckRecordEntity.java | 84 + .../eq/eqcheck/excel/EqCheckSettingExcel.java | 42 + .../excel/EqCheckStandardDetailExcel.java | 44 + .../eqcheck/excel/EqCheckStandardExcel.java | 42 + .../eqcheck/excel/EqPatrolCheckPlanExcel.java | 63 + .../excel/EqPatrolCheckRecordDetailExcel.java | 63 + .../excel/EqPatrolCheckRecordExcel.java | 48 + .../eqcheck/excel/EqSpotCheckPlanExcel.java | 35 + .../eqcheck/excel/EqSpotCheckRecordExcel.java | 43 + .../eqcheck/mapper/EqCheckSettingMapper.java | 34 + .../mapper/EqCheckStandardDetailMapper.java | 16 + .../eqcheck/mapper/EqCheckStandardMapper.java | 19 + .../mapper/EqPatrolCheckPlanMapper.java | 28 + .../mapper/EqPatrolCheckRecordMapper.java | 33 + .../eqcheck/mapper/EqSpotCheckPlanMapper.java | 31 + .../mapper/EqSpotCheckRecordMapper.java | 26 + .../service/EqCheckSettingService.java | 50 + .../service/EqCheckStandardDetailService.java | 42 + .../service/EqCheckStandardService.java | 24 + .../service/EqPatrolCheckPlanService.java | 45 + .../service/EqPatrolCheckRecordService.java | 55 + .../service/EqSpotCheckPlanService.java | 37 + .../service/EqSpotCheckRecordService.java | 26 + .../impl/EqCheckSettingServiceImpl.java | 122 ++ .../EqCheckStandardDetailServiceImpl.java | 102 ++ .../impl/EqCheckStandardServiceImpl.java | 77 + .../impl/EqPatrolCheckPlanServiceImpl.java | 677 ++++++++ .../impl/EqPatrolCheckRecordServiceImpl.java | 220 +++ .../impl/EqSpotCheckPlanServiceImpl.java | 241 +++ .../impl/EqSpotCheckRecordServiceImpl.java | 154 ++ .../controller/EqFileManageController.java | 386 +++++ .../eq/eqfilemanage/dto/DeleteFileParam.java | 22 + .../eq/eqfilemanage/dto/EqFileDeleteDTO.java | 24 + .../eq/eqfilemanage/dto/EqFileManageDTO.java | 57 + .../eqfilemanage/dto/EqFileManageResDTO.java | 23 + .../eqfilemanage/dto/EqFileManageTreeDTO.java | 20 + .../eq/eqfilemanage/dto/MoveFileParams.java | 34 + .../entity/EqFileDeleteEntity.java | 30 + .../entity/EqFileManageEntity.java | 84 + .../eq/eqfilemanage/entity/SysOssEntity.java | 31 + .../eqfilemanage/excel/EqFileDeleteExcel.java | 24 + .../eqfilemanage/excel/EqFileManageExcel.java | 43 + .../mapper/EqFileDeleteMapper.java | 18 + .../mapper/EqFileManageMapper.java | 21 + .../service/EqFileDeleteService.java | 33 + .../service/EqFileManageService.java | 42 + .../service/impl/EqFileDeleteServiceImpl.java | 109 ++ .../service/impl/EqFileManageServiceImpl.java | 269 +++ .../controller/EqScrapController.java | 145 ++ .../IotThingBaseInfoController.java | 128 ++ .../controller/IotThingsController.java | 125 ++ .../eq/eqmanager/dto/EqAttacmentDTO.java | 40 + .../com/thing/eq/eqmanager/dto/EqDTO.java | 110 ++ .../com/thing/eq/eqmanager/dto/EqInfoDTO.java | 42 + .../thing/eq/eqmanager/dto/EqScrapDTO.java | 45 + .../eq/eqmanager/dto/EqTypeNameAndIdDTO.java | 22 + .../eq/eqmanager/dto/IotThingBaseInfoDTO.java | 76 + .../thing/eq/eqmanager/dto/IotThingsDTO.java | 63 + .../eq/eqmanager/dto/ThingsRelationDTO.java | 42 + .../eqmanager/dto/ThingsRelationReqDTO.java | 45 + .../eqmanager/entity/EqAttacmentEntity.java | 61 + .../eq/eqmanager/entity/EqScrapEntity.java | 67 + .../entity/IotThingBaseInfoEntity.java | 149 ++ .../eq/eqmanager/entity/IotThingsEntity.java | 105 ++ .../entity/ThingsRelationEntity.java | 61 + .../eq/eqmanager/excel/EqAttacmentExcel.java | 40 + .../eq/eqmanager/excel/EqInportExcel.java | 47 + .../eq/eqmanager/excel/EqScrapExcel.java | 49 + .../excel/IotThingBaseInfoExcel.java | 56 + .../eq/eqmanager/excel/IotThingsExcel.java | 44 + .../eqmanager/excel/IotThingsPartExcel.java | 34 + .../eqmanager/mapper/EqAttacmentMapper.java | 25 + .../eq/eqmanager/mapper/EqScrapMapper.java | 29 + .../mapper/EqThingsRelationMapper.java | 27 + .../mapper/IotThingBaseInfoMapper.java | 51 + .../eq/eqmanager/mapper/IotThingsDao.java | 30 + .../eq/eqmanager/pojo/IotThingsPojo.java | 53 + .../eqmanager/service/EqAttacmentService.java | 23 + .../eq/eqmanager/service/EqScrapService.java | 36 + .../service/IotThingBaseInfoService.java | 49 + .../eqmanager/service/IotThingsService.java | 35 + .../service/ThingsRelationService.java | 29 + .../service/impl/EqAttacmentServiceImpl.java | 47 + .../service/impl/EqScrapServiceImpl.java | 245 +++ .../impl/EqThingsRelationServiceImpl.java | 76 + .../impl/IotThingBaseInfoServiceImpl.java | 348 ++++ .../service/impl/IotThingsServiceImpl.java | 712 ++++++++ .../controller/EqPlanRecordController.java | 122 ++ .../eq/eqpalnrecord/dto/EqPlanRecordDTO.java | 70 + .../entity/EqPlanRecordEntity.java | 91 + .../eqpalnrecord/excel/EqPlanRecordExcel.java | 54 + .../mapper/EqPlanRecordMapper.java | 21 + .../service/EqPlanRecordService.java | 22 + .../service/impl/EqPlanRecordServiceImpl.java | 96 ++ .../controller/EqPartRecordController.java | 118 ++ .../eq/eqpartrecord/dto/EqPartRecordDTO.java | 49 + .../entity/EqPartRecordEntity.java | 73 + .../eqpartrecord/excel/EqPartRecordExcel.java | 36 + .../mapper/EqPartRecordMapper.java | 27 + .../service/EqPartRecordService.java | 26 + .../service/impl/EqPartRecordServiceImpl.java | 127 ++ .../EqPlanRemindRecordController.java | 53 + .../dto/EqPlanRemindRecordDTO.java | 45 + .../entity/EqPlanRemindRecordEntity.java | 77 + .../excel/EqPlanRemindRecordExcel.java | 46 + .../mapper/EqPlanRemindRecordMapper.java | 20 + .../service/EqPlanRemindRecordService.java | 20 + .../impl/EqPlanRemindRecordServiceImpl.java | 53 + .../controller/EqUserGroupController.java | 165 ++ .../eq/equsergroup/dto/EqUserGroupDTO.java | 42 + .../equsergroup/entity/EqUserGroupEntity.java | 66 + .../equsergroup/excel/EqUserGroupExcel.java | 41 + .../equsergroup/mapper/EqUserGroupMapper.java | 29 + .../service/EqUserGroupService.java | 31 + .../service/impl/EqUserGroupServiceImpl.java | 113 ++ .../thing/eq/feign/DictTypeServiceFeign.java | 20 + .../com/thing/eq/feign/FileServiceFeign.java | 24 + .../thing/eq/feign/IotThingsServiceFeign.java | 25 + .../java/com/thing/eq/feign/TreeService.java | 24 + .../eq/file/controller/FileController.java | 32 + .../eq/file/entiy/EqAttacmentEntity.java | 65 + .../com/thing/eq/file/entiy/FileData.java | 16 + .../com/thing/eq/file/mapper/FileMapper.java | 16 + .../thing/eq/file/service/FileService.java | 23 + .../eq/file/service/impl/FileServiceImpl.java | 135 ++ .../com/thing/eq/message/EmailMessage.java | 49 + .../controller/DeviceMonitorController.java | 52 + .../controller/ReadingRecordController.java | 48 + .../eq/monitor/dto/DeviceMonitorDTO.java | 44 + .../eq/monitor/dto/ReadingRecordDTO.java | 42 + .../thing/eq/monitor/dto/ReadingRecordVO.java | 13 + .../monitor/entity/DeviceMonitorEntity.java | 5 + .../monitor/entity/ReadingRecordEntity.java | 5 + .../eq/monitor/mapper/DeviceMonitorDao.java | 17 + .../monitor/mapper/ReadingRecordMapper.java | 10 + .../monitor/service/DeviceMonitorService.java | 13 + .../monitor/service/ReadingRecordService.java | 12 + .../impl/DeviceMonitorServiceImpl.java | 252 +++ .../impl/ReadingRecordServiceImpl.java | 160 ++ .../controller/EnergyCheckController.java | 360 ++++ .../controller/EnergyProjectController.java | 379 +++++ .../eq/normalize/dto/EnergyCheckDTO.java | 79 + .../eq/normalize/dto/EnergyPeopleDTO.java | 52 + .../eq/normalize/dto/EnergyProjectDTO.java | 81 + .../normalize/entity/EnergyCheckEntity.java | 95 ++ .../normalize/entity/EnergyPeopleEntity.java | 73 + .../normalize/entity/EnergyProjectEntity.java | 91 + .../eq/normalize/excel/EnergyCheckExcel.java | 52 + .../normalize/excel/EnergyProjectExcel.java | 46 + .../normalize/mapper/EnergyCheckMapper.java | 48 + .../normalize/mapper/EnergyPeopleMapper.java | 22 + .../normalize/mapper/EnergyProjectMapper.java | 101 ++ .../normalize/service/EnergyCheckService.java | 63 + .../service/EnergyPeopleService.java | 22 + .../service/EnergyProjectService.java | 168 ++ .../service/impl/EnergyCheckServiceImpl.java | 193 +++ .../service/impl/EnergyPeopleServiceImpl.java | 51 + .../impl/EnergyProjectServiceImpl.java | 442 +++++ .../com/thing/eq/task/DeleteFileTask.java | 77 + .../com/thing/eq/task/EqRemindRecordTask.java | 167 ++ .../java/com/thing/eq/task/PlanTimeTask.java | 280 ++++ .../eq/tree/controller/DeviceController.java | 158 ++ .../com/thing/eq/utils/DateSplitUtils.java | 431 +++++ .../com/thing/eq/utils/SerialNumberUnit.java | 74 + .../mapper/checkresult/CheckResultMapper.xml | 50 + .../resources/mapper/eqbxwx/EqBxMapper.xml | 108 ++ .../resources/mapper/eqbxwx/EqWxMapper.xml | 28 + .../mapper/eqbxwx/EqWxPlanMapper.xml | 103 ++ .../mapper/eqbxwx/EqWxReplacementMapper.xml | 13 + .../mapper/eqby/EqByDetailMapper.xml | 35 + .../main/resources/mapper/eqby/EqByMapper.xml | 59 + .../resources/mapper/eqby/EqByPlanMapper.xml | 91 + .../mapper/eqby/EqByPlanPartMapper.xml | 38 + .../mapper/eqby/EqByTemplateDetailMapper.xml | 33 + .../mapper/eqby/EqByTemplateMapper.xml | 43 + .../mapper/eqcheck/EqCheckSettingMapper.xml | 22 + .../eqcheck/EqCheckStandardDetailMapper.xml | 20 + .../mapper/eqcheck/EqCheckStandardMapper.xml | 25 + .../eqcheck/EqPatrolCheckPlanMapper.xml | 73 + .../eqcheck/EqPatrolCheckRecordMapper.xml | 66 + .../mapper/eqcheck/EqSpotCheckPlanMapper.xml | 65 + .../eqcheck/EqSpotCheckRecordMapper.xml | 64 + .../eqfilemanage/EqFileDeleteMapper.xml | 15 + .../eqfilemanage/EqFileManageMapper.xml | 52 + .../mapper/eqmanager/EqAttacmentMapper.xml | 34 + .../mapper/eqmanager/EqScrapMapper.xml | 50 + .../eqmanager/EqThingsRelationMapper.xml | 38 + .../eqmanager/IotThingBaseInfoMapper.xml | 204 +++ .../mapper/eqmanager/IotThingsMapper.xml | 60 + .../eqpalnrecord/EqPlanRecordMapper.xml | 40 + .../eqpartrecord/EqPartRecordMapper.xml | 35 + .../EqPlanRemindRecordMapper.xml | 33 + .../mapper/equsergroup/EqUserGroupMapper.xml | 39 + .../main/resources/mapper/file/FileMapper.xml | 33 + .../mapper/monitor/DeviceMonitorMapper.xml | 24 + .../mapper/normailze/EnergyCheckMapper.xml | 62 + .../mapper/normailze/EnergyPeopleMapper.xml | 22 + .../mapper/normailze/EnergyProjectMapper.xml | 69 + modules/filter-rule/pom.xml | 25 + .../cache/FilterRuleCacheInitializer.java | 33 + .../rule/controller/FilterLogController.java | 61 + .../rule/controller/FilterRuleController.java | 140 ++ .../thing/filter/rule/dto/FilterLogDTO.java | 58 + .../thing/filter/rule/dto/FilterRuleDTO.java | 50 + .../filter/rule/dto/FilterRuleDetailDTO.java | 56 + .../filter/rule/dto/LogSourceInfoValue.java | 60 + .../filter/rule/entity/FilterLogEntity.java | 346 ++++ .../rule/entity/FilterRuleDetailEntity.java | 68 + .../filter/rule/entity/FilterRuleEntity.java | 47 + .../filter/rule/enumeration/DataType.java | 30 + .../filter/rule/enumeration/LogStatus.java | 26 + .../filter/rule/event/FilterSuccessEvent.java | 27 + .../listener/FilterLogSaveEventListener.java | 383 +++++ .../filter/rule/mapper/FilterLogMapper.java | 20 + .../rule/mapper/FilterRuleDetailMapper.java | 16 + .../filter/rule/mapper/FilterRuleMapper.java | 16 + .../filter/rule/service/FilterLogService.java | 24 + .../rule/service/FilterRuleDetailService.java | 18 + .../rule/service/FilterRuleService.java | 38 + .../service/impl/FilterLogServiceImpl.java | 80 + .../impl/FilterRuleDetailServiceImpl.java | 38 + .../service/impl/FilterRuleServiceImpl.java | 234 +++ .../main/resources/mapper/FilterLogMapper.xml | 19 + modules/fix/pom.xml | 27 + .../fix/controller/FixRelationController.java | 27 + .../com/thing/fix/entity/RelationNode.java | 35 + .../com/thing/fix/mapper/BaseFixMapper.java | 29 + .../thing/fix/mapper/FixRelationMapper.java | 23 + .../com/thing/fix/service/BaseFixService.java | 36 + .../thing/fix/service/FixRelationService.java | 12 + .../fix/service/impl/BaseFixServiceImpl.java | 65 + .../service/impl/FixRelationServiceImpl.java | 101 ++ .../main/resources/mapper/BaseFixMapper.xml | 25 + .../resources/mapper/FixRelationMapper.xml | 39 + modules/mock/pom.xml | 37 + .../controller/MockDataConfigController.java | 134 ++ .../controller/MockDataLogController.java | 91 + .../com/thing/mock/dto/MockDataConfigDTO.java | 59 + .../com/thing/mock/dto/MockDataLogDTO.java | 55 + .../com/thing/mock/dto/ReMockRequest.java | 50 + .../mock/entity/MockDataConfigEntity.java | 67 + .../thing/mock/entity/MockDataLogEntity.java | 54 + .../com/thing/mock/excel/MockConfigExcel.java | 86 + .../com/thing/mock/handler/MockHandler.java | 180 ++ .../mock/mapper/MockDataConfigMapper.java | 16 + .../thing/mock/mapper/MockDataLogMapper.java | 16 + .../mock/service/MockDataConfigService.java | 37 + .../mock/service/MockDataLogService.java | 15 + .../impl/MockDataConfigServiceImpl.java | 181 ++ .../service/impl/MockDataLogServiceImpl.java | 70 + .../com/thing/mock/task/MockDataTask.java | 60 + modules/msg/pom.xml | 74 + .../cache/controller/MsgCacheController.java | 99 ++ .../com/thing/msg/cache/dto/MsgCacheDTO.java | 46 + .../msg/cache/entity/MsgCacheEntity.java | 39 + .../msg/cache/mapper/MsgCacheMapper.java | 16 + .../msg/cache/service/MsgCacheService.java | 21 + .../service/impl/MsgCacheServiceImpl.java | 204 +++ .../history/controller/MsgHisController.java | 92 + .../com/thing/msg/history/dto/MsgHisDTO.java | 62 + .../msg/history/entity/MsgHisEntity.java | 77 + .../msg/history/mapper/MsgHisMapper.java | 19 + .../msg/history/service/MsgHisService.java | 27 + .../service/impl/MsgHisServiceImpl.java | 191 +++ .../controller/MsgSysParamsController.java | 110 ++ .../thing/msg/param/dto/MsgSysParamsDTO.java | 61 + .../msg/param/entity/MsgParamsEntity.java | 45 + .../msg/param/mapper/MsgSysParamsMapper.java | 39 + .../msg/param/redis/MsgSysParamsRedis.java | 54 + .../param/service/MsgSysParamsService.java | 61 + .../service/impl/MsgSysParamsServiceImpl.java | 162 ++ .../push/controller/MsgPushController.java | 180 ++ .../com/thing/msg/push/dto/CustomPushDTO.java | 18 + .../thing/msg/push/dto/DingTalkParams.java | 42 + .../com/thing/msg/push/dto/MailAllParams.java | 17 + .../com/thing/msg/push/dto/MailParams.java | 24 + .../com/thing/msg/push/dto/MsgPushDTO.java | 68 + .../com/thing/msg/push/dto/SmsParams.java | 31 + .../thing/msg/push/dto/WeChatComParams.java | 34 + .../com/thing/msg/push/dto/WeChatParams.java | 37 + .../com/thing/msg/push/dto/WxParamObj.java | 20 + .../com/thing/msg/push/email/EmailConfig.java | 45 + .../thing/msg/push/email/MsgEmailUtils.java | 115 ++ .../thing/msg/push/entity/MsgPushEntity.java | 33 + .../com/thing/msg/push/factory/MsgPush.java | 15 + .../msg/push/factory/PushMsgFactory.java | 38 + .../factory/impl/DingTalkInfoService.java | 125 ++ .../push/factory/impl/MailInfoService.java | 73 + .../msg/push/factory/impl/SmsInfoService.java | 52 + .../factory/impl/WeChatComInfoService.java | 125 ++ .../push/factory/impl/WeChatInfoService.java | 303 ++++ .../msg/push/init/WeChatTokenRunner.java | 25 + .../thing/msg/push/mapper/MsgPushMapper.java | 16 + .../msg/push/service/MsgPushService.java | 48 + .../push/service/impl/MsgPushServiceImpl.java | 499 ++++++ .../msg/push/sms/AbstractSmsService.java | 24 + .../thing/msg/push/sms/AliyunSmsService.java | 114 ++ .../thing/msg/push/sms/QcloudSmsService.java | 73 + .../thing/msg/push/sms/QiniuSmsService.java | 74 + .../com/thing/msg/push/sms/SmsConfig.java | 75 + .../com/thing/msg/push/sms/SmsFactory.java | 27 + .../controller/MsgTemplateController.java | 100 ++ .../msg/template/dto/MsgTemplateDTO.java | 56 + .../template/entity/MsgTemplateEntity.java | 44 + .../template/mapper/MsgTemplateMapper.java | 16 + .../template/service/MsgTemplateService.java | 17 + .../service/impl/MsgTemplateServiceImpl.java | 49 + .../controller/MsgUserController.java | 106 ++ .../thing/msg/userinfo/dto/MsgUserDTO.java | 55 + .../msg/userinfo/dto/SysUserInfoDTO.java | 41 + .../msg/userinfo/entity/MsgUserEntity.java | 52 + .../msg/userinfo/excel/MsgUserExcel.java | 41 + .../msg/userinfo/mapper/MsgUserMapper.java | 15 + .../msg/userinfo/service/MsgUserService.java | 19 + .../service/impl/MsgUserServiceImpl.java | 47 + .../main/resources/mapper/MsgHisMapper.xml | 36 + .../main/resources/mapper/MsgUserMapper.xml | 19 + modules/pom.xml | 36 + modules/publicorg/pom.xml | 41 + .../PublicBenchmarkingProjectController.java | 125 ++ .../dto/PublicBenchmarkingProjectDTO.java | 85 + .../dto/ThingSequentialInfoParamDTO.java | 33 + .../dto/ThingSequentialParamDTO.java | 29 + .../PublicBenchmarkingProjectEntity.java | 130 ++ .../excel/PublicBenchmarkingProjectExcel.java | 67 + .../PublicBenchmarkingProjectMapper.java | 23 + .../PublicBenchmarkingProjectService.java | 25 + .../PublicBenchmarkingProjectServiceImpl.java | 78 + .../PublicEnergySavingReportController.java | 132 ++ .../dto/PublicEnergySavingReportDTO.java | 113 ++ .../dto/PublicOrgEnergyDataDTO.java | 71 + .../dto/PublicOrgEnergyTotalDataDTO.java | 28 + .../PublicEnergySavingReportEntity.java | 134 ++ .../excel/PublicEnergySavingReportExcel.java | 65 + .../PublicEnergySavingReportMapper.java | 25 + .../PublicEnergySavingReportService.java | 33 + .../PublicEnergySavingReportServiceImpl.java | 308 ++++ ...licEnergySavingReportDetailController.java | 119 ++ .../PublicEnergySavingReportDetailDTO.java | 49 + .../PublicEnergySavingReportDetailEntity.java | 50 + .../PublicEnergySavingReportDetailExcel.java | 49 + .../PublicEnergySavingReportDetailMapper.java | 24 + ...PublicEnergySavingReportDetailService.java | 23 + ...icEnergySavingReportDetailServiceImpl.java | 43 + .../controller/PublicFinancialController.java | 108 ++ .../financial/dto/PublicFinancialDTO.java | 51 + .../entity/PublicFinancialEntity.java | 67 + .../mapper/PublicFinancialMapper.java | 26 + .../service/PublicFinancialService.java | 20 + .../impl/PublicFinancialServiceImpl.java | 43 + .../PublicIndexStandardController.java | 98 ++ .../dto/PublicIndexStandardDTO.java | 53 + .../entity/PublicIndexStandardEntity.java | 58 + .../excel/PublicIndexStandardExcel.java | 53 + .../mapper/PublicIndexStandardMapper.java | 25 + .../service/PublicIndexStandardService.java | 23 + .../impl/PublicIndexStandardServiceImpl.java | 54 + .../controller/PublicNoticeController.java | 202 +++ .../publicorg/notice/dto/PublicNoticeDTO.java | 65 + .../notice/dto/PublicNoticeIsViewDTO.java | 66 + .../notice/entity/PublicNoticeEntity.java | 96 ++ .../notice/excel/PublicNoticeExcel.java | 57 + .../notice/mapper/PublicNoticeMapper.java | 28 + .../notice/service/PublicNoticeService.java | 25 + .../service/impl/PublicNoticeServiceImpl.java | 104 ++ .../PublicNoticeViewRecordController.java | 119 ++ .../dto/PublicNoticeViewRecordDTO.java | 38 + .../entity/PublicNoticeViewRecordEntity.java | 28 + .../excel/PublicNoticeViewRecordExcel.java | 38 + .../mapper/PublicNoticeViewRecordMapper.java | 22 + .../PublicNoticeViewRecordService.java | 19 + .../PublicNoticeViewRecordServiceImpl.java | 39 + .../org/controller/PublicOrgController.java | 78 + .../thing/publicorg/org/dto/PublicOrgDTO.java | 81 + .../publicorg/org/dto/PublicOrgInfoDTO.java | 26 + .../publicorg/org/entity/PublicOrgEntity.java | 99 ++ .../publicorg/org/excel/PublicOrgExcel.java | 74 + .../publicorg/org/mapper/PublicOrgMapper.java | 54 + .../org/service/PublicOrgService.java | 67 + .../service/impl/PublicOrgServiceImpl.java | 795 +++++++++ .../controller/PublicOrgDetailController.java | 114 ++ .../orgdetail/dto/PublicOrgDetailDTO.java | 65 + .../entity/PublicOrgDetailEntity.java | 82 + .../orgdetail/excel/PublicOrgDetailExcel.java | 65 + .../mapper/PublicOrgDetailMapper.java | 33 + .../service/PublicOrgDetailService.java | 29 + .../impl/PublicOrgDetailServiceImpl.java | 60 + .../PublicPolicyAdvocacyController.java | 133 ++ .../dto/PublicPolicyAdvocacyDTO.java | 67 + .../entity/PublicPolicyAdvocacyEntity.java | 93 + .../excel/PublicPolicyAdvocacyExcel.java | 94 ++ .../mapper/PublicPolicyAdvocacyMapper.java | 22 + .../service/PublicPolicyAdvocacyService.java | 24 + .../impl/PublicPolicyAdvocacyServiceImpl.java | 61 + ...PublicDiagnosisEnergySavingController.java | 108 ++ .../dto/PublicDiagnosisEnergySavingDTO.java | 51 + .../PublicDiagnosisEnergySavingEntity.java | 73 + .../PublicDiagnosisEnergySavingMapper.java | 24 + .../PublicDiagnosisEnergySavingService.java | 20 + ...ublicDiagnosisEnergySavingServiceImpl.java | 48 + .../PublicAlertNoticeController.java | 139 ++ .../dto/PublicAlertNoticeDTO.java | 53 + .../entity/PublicAlertNoticeEntity.java | 41 + .../excel/PublicAlertNoticeExcel.java | 44 + .../mapper/PublicAlertNoticeMapper.java | 26 + .../service/PublicAlertNoticeService.java | 23 + .../impl/PublicAlertNoticeServiceImpl.java | 51 + .../PublicAppletGovernmentController.java | 104 ++ .../controller/PublicBoardController.java | 121 ++ .../PublicChargingPileController.java | 70 + .../PublicEachUnitKanbanController.java | 97 ++ .../PublicEnergyWarningController.java | 120 ++ .../PublicMonitoringController.java | 97 ++ .../PublicPhotovoltaicController.java | 70 + .../controller/PublicReportController.java | 99 ++ .../publicboard/dto/BasePageData.java | 30 + .../dto/PublicAnnualConsumptionRatioDTO.java | 25 + .../publicboard/dto/PublicAnnualPowerDTO.java | 20 + .../dto/PublicAreaConsumptionDTO.java | 65 + .../dto/PublicCapacityGenerationDTO.java | 26 + .../dto/PublicCentralizedOfficeAreaDTO.java | 42 + .../dto/PublicChargingPileDTO.java | 26 + .../dto/PublicConsumptionAndCarbonDTO.java | 22 + .../publicboard/dto/PublicConsumptionDTO.java | 26 + .../publicboard/dto/PublicDataValueDTO.java | 19 + .../dto/PublicElectricityUnitDTO.java | 33 + .../dto/PublicEnergyConsumption.java | 22 + .../PublicEnergyConsumptionAnalyseDTO.java | 42 + .../dto/PublicEnergyConsumptionDTO.java | 31 + .../publicboard/dto/PublicEnergyDTO.java | 28 + .../dto/PublicEnergyWarning1Excel.java | 47 + .../dto/PublicEnergyWarningDataDTO.java | 163 ++ .../dto/PublicEnergyWarningExcel.java | 60 + .../dto/PublicEnergyWarningRequestDTO.java | 33 + .../PublicHourElectricityUsageTrendsDTO.java | 15 + .../dto/PublicInstalledCapacityDTO.java | 28 + .../publicboard/dto/PublicLinkDTO.java | 32 + .../publicboard/dto/PublicMonitorDTO.java | 26 + .../dto/PublicMonthlyConsumptionDTO.java | 22 + ...onthlyIntegratedEnergyUseStructureDTO.java | 28 + .../publicboard/dto/PublicNodeDTO.java | 18 + .../PublicNonCentralizedOfficeAreaDTO.java | 49 + .../dto/PublicOrgConsumptionAnalyseDTO.java | 42 + .../PublicOrgConsumptionAnalyseDataDTO.java | 29 + ...licOrgDataCenterConsumptionAnalyseDTO.java | 42 + .../PublicOrgDataEnergyConsumptionDTO.java | 31 + .../dto/PublicOrgDetailInfoDTO.java | 81 + .../PublicOrgEnergyConsumptionAnalyseDTO.java | 42 + .../PublicOrgTotalConsumptionAnalyseDTO.java | 23 + .../dto/PublicOrgWarningCountDTO.java | 36 + .../dto/PublicOrgWarningCountRatioDTO.java | 38 + .../PublicOrgYearConsumptionCompareDTO.java | 54 + .../dto/PublicPhotovoltaicGenerationDTO.java | 20 + .../publicboard/dto/PublicQuotaTypeDTO.java | 35 + .../publicboard/dto/PublicReportDataDTO.java | 321 ++++ .../publicboard/dto/PublicReportExcel.java | 114 ++ .../publicboard/dto/PublicSubNodeRespDTO.java | 20 + .../publicboard/dto/PublicTotalCarbonDTO.java | 23 + .../dto/PublicTotalConsumptionAnalyseDTO.java | 23 + .../publicboard/dto/PublicTotalDataDTO.java | 152 ++ .../dto/PublicTypeEnergyUseYearDTO.java | 26 + .../publicboard/dto/PublicTypeUseDTO.java | 17 + .../dto/PublicUnitBuildConsumptionDTO.java | 55 + .../dto/PublicUnitConsumptionDTO.java | 32 + .../publicboard/dto/PublicUnitNamePile.java | 32 + .../dto/PublicUnitOfTheYearDTO.java | 42 + .../publicboard/dto/PublicUnitsDTO.java | 26 + .../PublicAppletGovernmentService.java | 29 + .../service/PublicBoardService.java | 33 + .../service/PublicChargingPileService.java | 33 + .../service/PublicEachUnitKanbanService.java | 50 + .../service/PublicEnergyWarningService.java | 32 + .../service/PublicMonitoringService.java | 49 + .../service/PublicPhotovoltaicService.java | 37 + .../service/PublicReportService.java | 24 + ...licAppletGovernmentServiceServiceImpl.java | 589 +++++++ .../service/impl/PublicBoardServiceImpl.java | 644 +++++++ .../impl/PublicChargingPileServiceImpl.java | 280 ++++ .../impl/PublicEachUnitKanbanServiceImpl.java | 502 ++++++ .../impl/PublicEnergyWarningServiceImpl.java | 322 ++++ .../impl/PublicMonitoringServiceImpl.java | 304 ++++ .../impl/PublicPhotovoltaicServiceImpl.java | 79 + .../service/impl/PublicReportServiceImpl.java | 333 ++++ .../controller/PublicOrgLimitController.java | 118 ++ .../publicorglimit/dto/PublicOrgLimitDTO.java | 45 + .../entity/PublicOrgLimitEntity.java | 43 + .../excel/PublicOrgLimitExcel.java | 45 + .../mapper/PublicOrgLimitMapper.java | 24 + .../service/PublicOrgLimitService.java | 24 + .../impl/PublicOrgLimitServiceImpl.java | 55 + .../PublicBenchmarkingProjectMapper.xml | 57 + .../PublicEnergySavingReportMapper.xml | 43 + .../PublicEnergySavingReportDetailMapper.xml | 29 + .../financial/PublicFinancialMapper.xml | 39 + .../PublicIndexStandardMapper.xml | 34 + .../publicorg/notice/PublicNoticeMapper.xml | 74 + .../PublicNoticeViewRecordMapper.xml | 21 + .../mapper/publicorg/org/PublicOrgMapper.xml | 188 +++ .../orgdetail/PublicOrgDetailMapper.xml | 63 + .../PublicPolicyAdvocacyMapper.xml | 40 + .../PublicDiagnosisEnergySavingMapper.xml | 38 + .../PublicAlertNoticeMapper.xml | 30 + .../publicorglimit/PublicOrgLimitMapper.xml | 26 + modules/quartz/pom.xml | 113 ++ .../com/thing/quartz/calc/task/CalcTask.java | 36 + .../quartz/listener/PeakEventListener.java | 132 ++ .../task/controller/IotTaskController.java | 109 ++ .../com/thing/quartz/task/dto/IotTaskDTO.java | 80 + .../quartz/task/dto/MonthHourMinObject.java | 14 + .../thing/quartz/task/dto/TaskDictDTO.java | 26 + .../quartz/task/entity/IotTaskEntity.java | 68 + .../quartz/task/mapper/IotTaskMapper.java | 16 + .../quartz/task/service/IotTaskService.java | 44 + .../task/service/impl/IotTaskServiceImpl.java | 230 +++ .../thing/quartz/task/task/CustomTask.java | 112 ++ .../task/task/DefaultTaskBeanManager.java | 101 ++ .../com/thing/quartz/task/task/TaskBean.java | 26 + .../quartz/task/task/TaskBeanManager.java | 34 + .../thing/quartz/task/task/TestTaskBean.java | 23 + .../timetask/config/ScheduleConfig.java | 60 + .../controller/ScheduleJobController.java | 111 ++ .../controller/ScheduleJobLogController.java | 52 + .../quartz/timetask/dto/ScheduleJobDTO.java | 61 + .../timetask/dto/ScheduleJobLogDTO.java | 50 + .../timetask/entity/ScheduleJobEntity.java | 50 + .../timetask/entity/ScheduleJobLogEntity.java | 63 + .../timetask/init/JobCommandLineRunner.java | 46 + .../timetask/mapper/ScheduleJobLogMapper.java | 17 + .../timetask/mapper/ScheduleJobMapper.java | 24 + .../timetask/service/AlarmJobService.java | 12 + .../service/CalculationJobService.java | 45 + .../service/ScheduleJobLogService.java | 24 + .../timetask/service/ScheduleJobService.java | 59 + .../service/impl/AlarmJobServiceImpl.java | 226 +++ .../impl/CalculationJobServiceImpl.java | 309 ++++ .../impl/ScheduleJobLogServiceImpl.java | 41 + .../service/impl/ScheduleJobServiceImpl.java | 112 ++ .../thing/quartz/timetask/task/AlarmTask.java | 27 + .../com/thing/quartz/timetask/task/ITask.java | 22 + .../quartz/timetask/task/PushMsgTask.java | 29 + .../quartz/timetask/task/RepeatPushTask.java | 37 + .../thing/quartz/timetask/task/TestTask.java | 29 + .../quartz/timetask/task/ThingStatusTask.java | 81 + .../timetask/task/WeChatTokenRefreshTask.java | 33 + .../quartz/timetask/utils/ScheduleJob.java | 79 + .../quartz/timetask/utils/ScheduleUtils.java | 155 ++ .../resources/mapper/task/IotTaskMapper.xml | 6 + .../mapper/timetask/ScheduleJobLogMapper.xml | 7 + .../mapper/timetask/ScheduleJobMapper.xml | 14 + modules/report-analysis/pom.xml | 51 + .../CarbonEnergyDictRelationController.java | 113 ++ .../CarbonEnergyPriceController.java | 168 ++ .../CarbonEnergyVarietyController.java | 134 ++ .../CarbonPeakConfigController.java | 137 ++ .../controller/CarbonTsKvController.java | 39 + .../controller/MeterReadController.java | 47 + .../TransformerBizConfigController.java | 105 ++ .../dto/CarbonEnergyDictRelationDTO.java | 88 + .../config/dto/CarbonEnergyPriceDTO.java | 108 ++ .../dto/CarbonEnergyPriceReportReqDTO.java | 66 + .../config/dto/CarbonEnergyVarietyDTO.java | 53 + .../config/dto/CarbonEnergyVarietyReqDTO.java | 32 + .../config/dto/CarbonPeakConfigDTO.java | 61 + .../dto/CarbonThingEnergyDictReqDTO.java | 35 + .../config/dto/CarbonTimeLabelData.java | 205 +++ .../carbon/config/dto/CarbonTsKvDTO.java | 40 + .../carbon/config/dto/MeterReadReqDTO.java | 30 + .../carbon/config/dto/MeterReadRespDTO.java | 41 + .../config/dto/TransformerBizConfigDTO.java | 68 + .../CarbonEnergyDictRelationEntity.java | 95 ++ .../entity/CarbonEnergyPriceEntity.java | 62 + .../config/entity/CarbonEnergyPriceInfo.java | 18 + .../entity/CarbonEnergyVarietyEntity.java | 45 + .../config/entity/CarbonPeakConfigEntity.java | 69 + .../config/entity/CarbonTsKvEntity.java | 67 + .../excel/CarbonEnergyVarietyExcel.java | 52 + .../excel/CarbonThingEnergyDictExcel.java | 158 ++ .../CarbonThingEnergyDictVerticalExcel.java | 205 +++ .../carbon/config/excel/MeterReadExcel.java | 102 ++ .../CarbonEnergyDictRelationMapper.java | 22 + .../mapper/CarbonEnergyPriceMapper.java | 25 + .../mapper/CarbonEnergyVarietyMapper.java | 17 + .../config/mapper/CarbonPeakConfigMapper.java | 20 + .../config/mapper/CarbonTsKvMapper.java | 23 + .../CarbonEnergyDictRelationService.java | 35 + .../service/CarbonEnergyPriceService.java | 48 + .../service/CarbonEnergyVarietyService.java | 31 + .../service/CarbonPeakConfigService.java | 34 + .../config/service/CarbonTsKvService.java | 18 + .../config/service/MeterReadService.java | 20 + .../service/TransformerBizConfigService.java | 30 + .../CarbonEnergyDictRelationServiceImpl.java | 322 ++++ .../impl/CarbonEnergyPriceServiceImpl.java | 934 +++++++++++ .../impl/CarbonEnergyVarietyServiceImpl.java | 127 ++ .../impl/CarbonPeakConfigServiceImpl.java | 213 +++ .../service/impl/CarbonTsKvServiceImpl.java | 327 ++++ .../service/impl/MeterReadServiceImpl.java | 185 ++ .../impl/TransformerBizConfigServiceImpl.java | 104 ++ .../controller/EnergyEffReportController.java | 80 + .../controller/EnergyUsageController.java | 233 +++ .../dto/AnalysisDataReqNewDTO.java | 20 + .../dto/AnalysisThingAttrValueRespDTO.java | 58 + .../carbon/energyrepory/dto/EnergyAlarm.java | 31 + .../energyrepory/dto/EnergyLoadAgg.java | 42 + .../energyrepory/dto/EnergyLoadRate.java | 27 + .../energyrepory/dto/EnergyLoadRealTime.java | 33 + .../carbon/energyrepory/dto/EnergyLoss.java | 38 + .../energyrepory/dto/EnergyNightUsageAgg.java | 34 + .../energyrepory/dto/EnergyTimeLabelData.java | 81 + .../dto/EnergyTransformerEff.java | 27 + .../energyrepory/dto/EnergyUsageAgg.java | 56 + .../dto/EnergyUsageLabelData.java | 168 ++ .../energyrepory/dto/EnergyUsageReqDTO.java | 43 + .../dto/EnergyUsageStatisticRequest.java | 60 + .../dto/EnergyUsageSummaryDTO.java | 77 + .../energyrepory/dto/PeakValleyDosageReq.java | 67 + .../carbon/energyrepory/dto/ReportParam.java | 49 + .../dto/TransformerAnalysisParam.java | 49 + .../excel/EnergyUsageStatisticExcel.java | 269 +++ .../EnergyUsageStatisticVerticalExcel.java | 197 +++ .../service/DataSelectService.java | 26 + .../service/EnergyEffService.java | 22 + .../service/EnergyUsageCommonService.java | 31 + .../service/EnergyUsageFlowService.java | 16 + .../service/EnergyUsageOverViewService.java | 26 + .../service/EnergyUsageService.java | 29 + .../service/impl/DataSelectServiceImpl.java | 94 ++ .../service/impl/EnergyEffServiceImpl.java | 766 +++++++++ .../impl/EnergyUsageCommonServiceImpl.java | 309 ++++ .../impl/EnergyUsageFlowServiceImpl.java | 290 ++++ .../impl/EnergyUsageOverViewServiceImpl.java | 355 ++++ .../service/impl/EnergyUsageServiceImpl.java | 661 ++++++++ .../thread/EnergyUsageExecutor.java | 25 + .../controller/EnergyBalanceController.java | 87 + .../controller/PeakValleyController.java | 184 ++ .../carbon/peakvalley/dto/AttrParam.java | 14 + .../carbon/peakvalley/dto/AttrPriceInfo.java | 32 + .../thing/carbon/peakvalley/dto/CostReq.java | 22 + .../peakvalley/dto/CostStatisticsReq.java | 38 + .../peakvalley/dto/EnergyBalanceReq.java | 37 + .../carbon/peakvalley/dto/EnergyReq.java | 15 + .../peakvalley/dto/PeakValleyAnalysisReq.java | 40 + .../peakvalley/dto/PeakValleyExportParam.java | 75 + .../peakvalley/dto/PeakValleyParam.java | 87 + .../peakvalley/dto/PeakValleyPriceInfo.java | 58 + .../dto/PeakValleyPriceStatisticsInfo.java | 53 + .../peakvalley/dto/PeakValleyReportReq.java | 45 + .../peakvalley/dto/ThingAttrDosageReq.java | 56 + .../carbon/peakvalley/dto/ThingParam.java | 21 + .../excel/PeakValleyDosageExcel.java | 160 ++ .../excel/PeakValleyDosageVerticalExcel.java | 192 +++ .../peakvalley/excel/PeakValleyExcel.java | 112 ++ .../excel/PeakValleyPriceVerticalExcel.java | 195 +++ .../excel/PricePeakValleyDosageExcel.java | 160 ++ .../excel/PricePeakValleyExcel.java | 113 ++ .../peakvalley/service/AnalysisService.java | 31 + .../service/EnergyBalanceService.java | 14 + .../peakvalley/service/PeakValleyService.java | 29 + .../service/impl/AnalysisServiceImpl.java | 176 ++ .../impl/EnergyBalanceServiceImpl.java | 133 ++ .../service/impl/PeakValleyServiceImpl.java | 826 +++++++++ .../controller/AppletController.java | 57 + .../xiaochengxu/dto/AttibuteValueDTO.java | 25 + .../carbon/xiaochengxu/dto/DataResultDTO.java | 59 + .../carbon/xiaochengxu/dto/ThingInfoDTO.java | 28 + .../xiaochengxu/service/AppletService.java | 18 + .../service/impl/AppletServiceImpl.java | 167 ++ .../config/CarbonEnergyDictRelationMapper.xml | 40 + .../mapper/config/CarbonEnergyPriceMapper.xml | 59 + .../config/CarbonEnergyVarietyMapper.xml | 6 + .../mapper/config/CarbonPeakConfigMapper.xml | 32 + .../mapper/config/CarbonTsKvMapper.xml | 38 + modules/thing/pom.xml | 169 ++ .../cache/controller/CacheTsKvController.java | 149 ++ .../thing/cache/dto/BatchTsKvModifyForm.java | 39 + .../com/thing/cache/dto/CacheTsKvDTO.java | 63 + .../com/thing/cache/dto/TsKvDeleteForm.java | 41 + .../com/thing/cache/dto/TsKvModifyForm.java | 29 + .../thing/cache/entity/CacheTsKvEntity.java | 47 + .../cache/excel/CacheTsKvLastestExcel.java | 25 + .../cache/excel/TsKvModifyFormExcel.java | 27 + .../cache/service/TsKvHandleService.java | 39 + .../service/impl/TsKvHandleServiceImpl.java | 256 +++ .../java/com/thing/config/CacheConfig.java | 17 + .../com/thing/config/CustomKeyGenerator.java | 35 + .../com/thing/config/RelationProperties.java | 41 + .../config/RestTemplateConfiguration.java | 69 + .../thing/config/UnsignedLongSerializer.java | 16 + .../IotDeviceControlController.java | 167 ++ .../IotDeviceControlLogController.java | 74 + .../com/thing/control/dto/ControlParam.java | 26 + .../thing/control/dto/ControlSelected.java | 44 + .../control/dto/IotDeviceControlDTO.java | 107 ++ .../control/dto/IotDeviceControlJson.java | 53 + .../control/dto/IotDeviceControlLogDTO.java | 51 + .../control/dto/IotDeviceControlLogPage.java | 53 + .../control/dto/IotDeviceControlPage.java | 59 + .../entity/IotDeviceControlEntity.java | 93 + .../entity/IotDeviceControlLogEntity.java | 39 + .../excel/IotDeviceControlLogExcel.java | 34 + .../mapper/IotDeviceControlLogMapper.java | 26 + .../mapper/IotDeviceControlMapper.java | 41 + .../thing/control/service/ControlService.java | 19 + .../service/IotDeviceControlLogService.java | 45 + .../service/IotDeviceControlService.java | 84 + .../service/impl/ControlServiceImpl.java | 99 ++ .../impl/IotDeviceControlLogServiceImpl.java | 194 +++ .../impl/IotDeviceControlServiceImpl.java | 346 ++++ .../controller/IotDashboardController.java | 161 ++ .../IotDashboardElementController.java | 154 ++ .../IotDashboardGroupController.java | 94 ++ .../thing/dashboard/dto/IotAttrRspDTO.java | 27 + .../dashboard/dto/IotDashBoardRspDTO.java | 24 + .../thing/dashboard/dto/IotDashboardDTO.java | 100 ++ .../dto/IotDashboardElementAttrValueDTO.java | 40 + .../dashboard/dto/IotDashboardElementDTO.java | 93 + .../dashboard/dto/IotDashboardGroupDTO.java | 91 + .../dashboard/dto/IotDashboardSvgDTO.java | 29 + .../java/com/thing/dashboard/dto/SortDTO.java | 24 + .../entity/IotDashboardElementEntity.java | 89 + .../dashboard/entity/IotDashboardEntity.java | 73 + .../entity/IotDashboardGroupEntity.java | 58 + .../entity/IotDashboardSvgEntity.java | 34 + .../excel/IotDashboardElementExcel.java | 23 + .../dashboard/excel/IotDashboardExcel.java | 47 + .../excel/IotDashboardGroupExcel.java | 39 + .../mapper/IotDashboardElementMapper.java | 17 + .../mapper/IotDashboardGroupMapper.java | 17 + .../dashboard/mapper/IotDashboardMapper.java | 17 + .../mapper/IotDashboardSvgMapper.java | 18 + .../service/IotDashboardElementService.java | 46 + .../service/IotDashboardGroupService.java | 43 + .../service/IotDashboardService.java | 60 + .../service/IotDashboardSvgService.java | 36 + .../impl/IotDashboardElementServiceImpl.java | 160 ++ .../impl/IotDashboardGroupServiceImpl.java | 227 +++ .../service/impl/IotDashboardServiceImpl.java | 245 +++ .../impl/IotDashboardSvgServiceImpl.java | 72 + .../controller/AnalysisDataController.java | 179 ++ .../dto/AnalysisDataPageReqDTO.java | 30 + .../analysisdata/dto/AnalysisDataReqDTO.java | 88 + .../analysisdata/dto/AnalysisDataRespDTO.java | 36 + .../dto/MonitoringDataReqDTO.java | 83 + .../analysisdata/dto/MonitoringTimeDTO.java | 43 + .../analysisdata/dto/ThingEnergyDictData.java | 95 ++ .../analysisdata/excel/AnalysisDataExcel.java | 34 + .../service/AnalysisDataService.java | 30 + .../service/impl/AnalysisDataServiceImpl.java | 1034 ++++++++++++ .../board/controller/BoardController.java | 57 + .../thing/device/board/dto/BoardTsKvDTO.java | 51 + .../device/board/service/BoardService.java | 40 + .../board/service/impl/BoardServiceImpl.java | 266 +++ .../IotDeviceManagementController.java | 131 ++ .../dto/DeviceManagementParam.java | 15 + .../dto/DeviceManagementReq.java | 18 + .../dto/IotDeviceManagementDTO.java | 58 + .../entity/IotDeviceManagementEntity.java | 87 + .../excel/IotDeviceManagementExcel.java | 50 + .../mapper/IotDeviceManagementMapper.java | 16 + .../service/IotDeviceManagementService.java | 17 + .../impl/IotDeviceManagementServiceImpl.java | 80 + .../controller/IotDataEntryController.java | 128 ++ .../device/entry/dto/IotDataEntryDTO.java | 53 + .../entry/entity/IotDataEntryEntity.java | 51 + .../device/entry/excel/IotDataEntryExcel.java | 35 + .../entry/excel/IotDataEntryImportExcel.java | 24 + .../entry/mapper/IotDataEntryMapper.java | 16 + .../entry/service/IotDataEntryService.java | 43 + .../service/impl/IotDataEntryServiceImpl.java | 149 ++ .../IotThingMenuConfigController.java | 107 ++ .../menu/dto/IotThingMenuConfigDTO.java | 90 + .../menu/entity/IotThingMenuConfigEntity.java | 40 + .../menu/mapper/IotThingMenuConfigMapper.java | 15 + .../service/IotThingMenuConfigService.java | 14 + .../impl/IotThingMenuConfigServiceImpl.java | 51 + .../controller/IotThingSourceController.java | 137 ++ .../source/dto/IotThingSourceAddChildDTO.java | 17 + .../dto/IotThingSourceAttrExtendDTO.java | 48 + .../source/dto/IotThingSourceAttrRespDTO.java | 27 + .../device/source/dto/IotThingSourceDTO.java | 123 ++ .../source/dto/IotThingSourceGetAttrDTO.java | 21 + .../source/dto/IotThingSourceListDTO.java | 84 + .../source/dto/IotThingSourcePidDTO.java | 30 + .../source/dto/IotThingSourceRelationDTO.java | 42 + .../source/dto/IotThingSourceReqDTO.java | 37 + .../source/entity/IotThingSourceEntity.java | 86 + .../source/mapper/IotThingSourceMapper.java | 36 + .../source/param/IotThingSourceParamDTO.java | 128 ++ .../source/service/IotThingSourceService.java | 44 + .../impl/IotThingSourceServiceImpl.java | 531 ++++++ .../source/thread/ThingSourceExecutor.java | 23 + .../thing/event/TenantDetailSavedEvent.java | 28 + .../event/ThingRelationDetailSaveEvent.java | 30 + .../thing/event/ThingRelationImportEvent.java | 25 + .../TransportExtendCalculationController.java | 154 ++ .../controller/TransportExtendController.java | 115 ++ .../TransportExtendMsgController.java | 89 + .../dto/TransportExtendCalculationDTO.java | 75 + .../dto/TransportExtendCalculationImport.java | 55 + .../thing/extend/dto/TransportExtendDTO.java | 78 + .../extend/dto/TransportExtendDictDTO.java | 29 + .../extend/dto/TransportExtendMsgDTO.java | 55 + .../TransportExtendCalculationEntity.java | 51 + .../extend/entity/TransportExtendEntity.java | 44 + .../entity/TransportExtendMsgEntity.java | 77 + .../extend/excel/TransportExtendMsgExcel.java | 25 + .../extend/excel/TransportModbusExcel.java | 35 + .../extend/excel/TransportMqttExcel.java | 21 + .../TransportExtendCalculationMapper.java | 16 + .../extend/mapper/TransportExtendMapper.java | 17 + .../mapper/TransportExtendMsgMapper.java | 41 + .../TransportExtendCalculationService.java | 80 + .../service/TransportExtendMsgService.java | 60 + .../service/TransportExtendService.java | 52 + ...TransportExtendCalculationServiceImpl.java | 419 +++++ .../impl/TransportExtendMsgServiceImpl.java | 155 ++ .../impl/TransportExtendServiceImpl.java | 155 ++ .../extension/DefaultExtensionManager.java | 226 +++ .../java/com/thing/extension/Extension.java | 57 + .../extension/ExtensionJsonConverter.java | 91 + .../com/thing/extension/ExtensionManager.java | 70 + .../thing/extension/ExtensionProvider.java | 43 + .../thing/extension/ExtensionPublishMsg.java | 50 + .../thing/extension/ExtensionRelation.java | 50 + .../com/thing/extension/ExtensionType.java | 40 + .../com/thing/extension/ExtensionUtil.java | 213 +++ .../modbus/ModbusConnectOptions.java | 18 + .../extension/modbus/ModbusExtension.java | 216 +++ .../modbus/ModbusExtensionProvider.java | 75 + .../modbus/ModbusExtensionRelation.java | 62 + .../extension/modbus/ModbusFunction.java | 46 + .../thing/extension/modbus/ModbusTsKv.java | 26 + .../thing/extension/modbus/ModbusUtils.java | 145 ++ .../thing/extension/mqtt/MqttExtension.java | 303 ++++ .../extension/mqtt/MqttExtensionProvider.java | 102 ++ .../extension/mqtt/MqttExtensionRelation.java | 35 + .../thing/extension/mqtt/TopicMatcher.java | 84 + .../listener/QueueDeviceEventListener.java | 204 +++ .../IotEnterpriseDashboardController.java | 142 ++ .../screen/dto/IotEnterpriseDashboardDTO.java | 58 + .../entity/IotEnterpriseDashboardEntity.java | 48 + .../mapper/IotEnterpriseDashboardMapper.java | 16 + .../IotEnterpriseDashboardService.java | 34 + .../IotEnterpriseDashboardServiceImpl.java | 146 ++ .../thing/screen/util/TenantSubsetUtilS.java | 71 + .../sys/biz/aspect/LogOperationAspect.java | 115 ++ .../sys/biz/controller/SysDeptController.java | 86 + .../biz/controller/SysDictDataController.java | 117 ++ .../biz/controller/SysDictTypeController.java | 112 ++ .../controller/SysIndustryTypeController.java | 109 ++ .../sys/biz/controller/SysMenuController.java | 183 ++ .../biz/controller/SysOnlineController.java | 60 + .../biz/controller/SysParamsController.java | 140 ++ .../sys/biz/controller/SysPostController.java | 117 ++ .../biz/controller/SysRegionController.java | 119 ++ .../sys/biz/controller/SysRoleController.java | 123 ++ .../sys/biz/controller/SysUserController.java | 242 +++ .../biz/controller/SysUserMenuController.java | 114 ++ .../sys/biz/controller/SystemController.java | 54 + .../java/com/thing/sys/biz/dto/BindDTO.java | 21 + .../com/thing/sys/biz/dto/CtlUserDTO.java | 18 + .../com/thing/sys/biz/dto/HomeNavReq.java | 18 + .../com/thing/sys/biz/dto/PasswordDTO.java | 33 + .../com/thing/sys/biz/dto/SysDeptDTO.java | 62 + .../com/thing/sys/biz/dto/SysDictDataDTO.java | 66 + .../com/thing/sys/biz/dto/SysDictTypeDTO.java | 62 + .../thing/sys/biz/dto/SysDictTypeListDTO.java | 41 + .../thing/sys/biz/dto/SysIndustryTypeDTO.java | 41 + .../com/thing/sys/biz/dto/SysMenuDTO.java | 89 + .../com/thing/sys/biz/dto/SysParamsDTO.java | 60 + .../com/thing/sys/biz/dto/SysPostDTO.java | 41 + .../com/thing/sys/biz/dto/SysRegionDTO.java | 67 + .../com/thing/sys/biz/dto/SysRoleDTO.java | 64 + .../com/thing/sys/biz/dto/SysUserDTO.java | 129 ++ .../com/thing/sys/biz/dto/SysUserMenuDTO.java | 48 + .../thing/sys/biz/dto/SysUserMenuPageDTO.java | 28 + .../java/com/thing/sys/biz/dto/SystemDTO.java | 44 + .../com/thing/sys/biz/entity/DictData.java | 23 + .../com/thing/sys/biz/entity/DictType.java | 22 + .../thing/sys/biz/entity/SysDeptEntity.java | 46 + .../sys/biz/entity/SysDictDataEntity.java | 43 + .../sys/biz/entity/SysDictTypeEntity.java | 39 + .../sys/biz/entity/SysIndustryTypeEntity.java | 64 + .../sys/biz/entity/SysLanguageEntity.java | 41 + .../thing/sys/biz/entity/SysMenuEntity.java | 77 + .../thing/sys/biz/entity/SysOnlineEntity.java | 44 + .../thing/sys/biz/entity/SysParamsEntity.java | 42 + .../thing/sys/biz/entity/SysPostEntity.java | 37 + .../thing/sys/biz/entity/SysRegionEntity.java | 52 + .../biz/entity/SysRoleDataScopeEntity.java | 34 + .../thing/sys/biz/entity/SysRoleEntity.java | 38 + .../sys/biz/entity/SysRoleMenuEntity.java | 33 + .../sys/biz/entity/SysRoleUserEntity.java | 32 + .../thing/sys/biz/entity/SysUserEntity.java | 88 + .../sys/biz/entity/SysUserMenuEntity.java | 47 + .../sys/biz/entity/SysUserPostEntity.java | 30 + .../sys/biz/entity/SysUserTokenEntity.java | 45 + .../thing/sys/biz/excel/SysParamsExcel.java | 23 + .../com/thing/sys/biz/excel/SysUserExcel.java | 45 + .../thing/sys/biz/excel/SysUserMenuExcel.java | 35 + .../thing/sys/biz/mapper/SysDeptMapper.java | 50 + .../sys/biz/mapper/SysDictDataMapper.java | 35 + .../sys/biz/mapper/SysDictTypeMapper.java | 26 + .../sys/biz/mapper/SysIndustryTypeMapper.java | 23 + .../sys/biz/mapper/SysLanguageMapper.java | 26 + .../thing/sys/biz/mapper/SysMenuMapper.java | 75 + .../thing/sys/biz/mapper/SysParamsMapper.java | 38 + .../thing/sys/biz/mapper/SysPostMapper.java | 15 + .../thing/sys/biz/mapper/SysRegionMapper.java | 28 + .../biz/mapper/SysRoleDataScopeMapper.java | 38 + .../thing/sys/biz/mapper/SysRoleMapper.java | 20 + .../sys/biz/mapper/SysRoleMenuMapper.java | 39 + .../sys/biz/mapper/SysRoleUserMapper.java | 49 + .../thing/sys/biz/mapper/SysUserMapper.java | 47 + .../sys/biz/mapper/SysUserMenuMapper.java | 21 + .../sys/biz/mapper/SysUserPostMapper.java | 34 + .../sys/biz/mapper/SysUserTokenMapper.java | 31 + .../java/com/thing/sys/biz/region/Region.java | 32 + .../com/thing/sys/biz/region/RegionCity.java | 24 + .../thing/sys/biz/region/RegionProvince.java | 24 + .../thing/sys/biz/service/SysDeptService.java | 87 + .../sys/biz/service/SysDictDataService.java | 40 + .../sys/biz/service/SysDictTypeService.java | 49 + .../biz/service/SysIndustryTypeService.java | 26 + .../sys/biz/service/SysLanguageService.java | 32 + .../thing/sys/biz/service/SysMenuService.java | 60 + .../sys/biz/service/SysParamsService.java | 52 + .../thing/sys/biz/service/SysPostService.java | 22 + .../sys/biz/service/SysRegionService.java | 34 + .../biz/service/SysRoleDataScopeService.java | 41 + .../sys/biz/service/SysRoleMenuService.java | 38 + .../thing/sys/biz/service/SysRoleService.java | 33 + .../sys/biz/service/SysRoleUserService.java | 49 + .../sys/biz/service/SysUserMenuService.java | 31 + .../sys/biz/service/SysUserPostService.java | 39 + .../thing/sys/biz/service/SysUserService.java | 82 + .../sys/biz/service/SysUserTokenService.java | 36 + .../biz/service/impl/SysDeptServiceImpl.java | 231 +++ .../service/impl/SysDictDataServiceImpl.java | 115 ++ .../service/impl/SysDictTypeServiceImpl.java | 140 ++ .../impl/SysIndustryTypeServiceImpl.java | 58 + .../service/impl/SysLanguageServiceImpl.java | 57 + .../biz/service/impl/SysMenuServiceImpl.java | 247 +++ .../service/impl/SysParamsServiceImpl.java | 158 ++ .../biz/service/impl/SysPostServiceImpl.java | 74 + .../service/impl/SysRegionServiceImpl.java | 178 ++ .../impl/SysRoleDataScopeServiceImpl.java | 82 + .../service/impl/SysRoleMenuServiceImpl.java | 71 + .../biz/service/impl/SysRoleServiceImpl.java | 157 ++ .../service/impl/SysRoleUserServiceImpl.java | 90 + .../service/impl/SysUserMenuServiceImpl.java | 113 ++ .../service/impl/SysUserPostServiceImpl.java | 66 + .../biz/service/impl/SysUserServiceImpl.java | 384 +++++ .../service/impl/SysUserTokenServiceImpl.java | 119 ++ .../thing/sys/biz/utils/HttpClientUtil.java | 83 + .../log/controller/SysLogErrorController.java | 70 + .../log/controller/SysLogLoginController.java | 77 + .../controller/SysLogOperationController.java | 72 + .../com/thing/sys/log/dto/SysLogErrorDTO.java | 41 + .../com/thing/sys/log/dto/SysLogLoginDTO.java | 48 + .../thing/sys/log/dto/SysLogOperationDTO.java | 59 + .../sys/log/entity/SysLogErrorEntity.java | 59 + .../sys/log/entity/SysLogLoginEntity.java | 57 + .../sys/log/entity/SysLogOperationEntity.java | 73 + .../log/enumeration/LoginOperationEnum.java | 28 + .../sys/log/enumeration/LoginStatusEnum.java | 32 + .../log/enumeration/OperationStatusEnum.java | 28 + .../thing/sys/log/excel/SysLogErrorExcel.java | 32 + .../thing/sys/log/excel/SysLogLoginExcel.java | 34 + .../sys/log/excel/SysLogOperationExcel.java | 45 + .../sys/log/handler/SysExceptionHandler.java | 85 + .../sys/log/mapper/SysLogErrorMapper.java | 16 + .../sys/log/mapper/SysLogLoginMapper.java | 16 + .../sys/log/mapper/SysLogOperationMapper.java | 16 + .../sys/log/service/SysLogErrorService.java | 25 + .../sys/log/service/SysLogLoginService.java | 24 + .../log/service/SysLogOperationService.java | 24 + .../service/impl/SysLogErrorServiceImpl.java | 57 + .../service/impl/SysLogLoginServiceImpl.java | 66 + .../impl/SysLogOperationServiceImpl.java | 64 + .../message/controller/MailLogController.java | 63 + .../controller/MailTemplateController.java | 131 ++ .../sys/message/controller/SmsController.java | 119 ++ .../controller/SysSmsLogController.java | 58 + .../thing/sys/message/dto/SysMailLogDTO.java | 54 + .../sys/message/dto/SysMailTemplateDTO.java | 53 + .../com/thing/sys/message/dto/SysSmsDTO.java | 44 + .../thing/sys/message/dto/SysSmsLogDTO.java | 60 + .../thing/sys/message/email/EmailConfig.java | 68 + .../thing/sys/message/email/EmailUtils.java | 174 ++ .../sys/message/entity/SysMailLogEntity.java | 56 + .../message/entity/SysMailTemplateEntity.java | 37 + .../sys/message/entity/SysSmsEntity.java | 41 + .../sys/message/entity/SysSmsLogEntity.java | 62 + .../sys/message/mapper/SysMailLogMapper.java | 15 + .../message/mapper/SysMailTemplateMapper.java | 17 + .../sys/message/mapper/SysSmsLogMapper.java | 15 + .../sys/message/mapper/SysSmsMapper.java | 15 + .../message/service/SysMailLogService.java | 31 + .../service/SysMailTemplateService.java | 25 + .../sys/message/service/SysSmsLogService.java | 25 + .../sys/message/service/SysSmsService.java | 24 + .../service/impl/SysMailLogServiceImpl.java | 54 + .../impl/SysMailTemplateServiceImpl.java | 55 + .../service/impl/SysSmsLogServiceImpl.java | 68 + .../service/impl/SysSmsServiceImpl.java | 77 + .../sys/message/sms/AbstractSmsService.java | 36 + .../sys/message/sms/AliyunSmsService.java | 103 ++ .../sys/message/sms/QcloudSmsService.java | 62 + .../com/thing/sys/message/sms/SmsConfig.java | 67 + .../com/thing/sys/message/sms/SmsFactory.java | 34 + .../controller/SysNoticeController.java | 143 ++ .../thing/sys/notice/dto/SysNoticeDTO.java | 59 + .../sys/notice/entity/SysNoticeEntity.java | 89 + .../notice/entity/SysNoticeUserEntity.java | 38 + .../notice/enums/NoticeReadStatusEnum.java | 29 + .../sys/notice/enums/NoticeStatusEnum.java | 29 + .../sys/notice/enums/ReceiverTypeEnum.java | 29 + .../sys/notice/mapper/SysNoticeMapper.java | 27 + .../notice/mapper/SysNoticeUserMapper.java | 25 + .../sys/notice/service/SysNoticeService.java | 27 + .../notice/service/SysNoticeUserService.java | 34 + .../service/impl/SysNoticeServiceImpl.java | 154 ++ .../impl/SysNoticeUserServiceImpl.java | 53 + .../cloud/AbstractCloudStorageService.java | 98 ++ .../oss/cloud/AliyunCloudStorageService.java | 88 + .../sys/oss/cloud/CloudStorageConfig.java | 138 ++ .../oss/cloud/FastDFSCloudStorageService.java | 90 + .../thing/sys/oss/cloud/FastDFSImport.java | 19 + .../oss/cloud/LocalCloudStorageService.java | 85 + .../oss/cloud/MinioCloudStorageService.java | 156 ++ .../com/thing/sys/oss/cloud/OSSFactory.java | 68 + .../sys/oss/controller/SysOssController.java | 291 ++++ .../thing/sys/oss/entity/SysOssEntity.java | 30 + .../com/thing/sys/oss/entity/UpLoadDao.java | 22 + .../thing/sys/oss/mapper/SysOssMapper.java | 15 + .../thing/sys/oss/service/SysOssService.java | 26 + .../oss/service/impl/SysOssServiceImpl.java | 85 + .../sys/security/aspect/DataFilterAspect.java | 126 ++ .../sys/security/context/TenantContext.java | 62 + .../sys/security/context/UserContext.java | 94 ++ .../security/controller/LoginController.java | 219 +++ .../sys/security/customrealm/Oauth2Realm.java | 129 ++ .../customrealm/ShiroCustomConfig.java | 36 + .../thing/sys/security/domain/LoginDTO.java | 36 + .../sys/security/domain/SecurityUser.java | 60 + .../sys/security/domain/SysAuthInfo.java | 30 + .../sys/security/domain/SysAuthPrincipal.java | 77 + .../thing/sys/security/domain/UserDetail.java | 56 + .../sys/security/service/ShiroService.java | 36 + .../service/impl/ShiroServiceImpl.java | 65 + .../controller/SysTenantController.java | 245 +++ .../controller/SysTenantDetailController.java | 328 ++++ .../controller/SysTenantGroupController.java | 65 + .../controller/SysTenantRoleController.java | 140 ++ .../dto/RegionIndustryTenantInfoDTO.java | 30 + .../thing/sys/tenant/dto/SysTenantDTO.java | 117 ++ .../sys/tenant/dto/SysTenantDetailDTO.java | 74 + .../tenant/dto/SysTenantDetailMenuDTO.java | 89 + .../sys/tenant/dto/SysTenantGroupDTO.java | 30 + .../sys/tenant/dto/SysTenantGroupForm.java | 29 + .../sys/tenant/dto/SysTenantGroupTreeDTO.java | 41 + .../sys/tenant/dto/SysTenantUserDTO.java | 58 + .../sys/tenant/dto/TenantDetailForm.java | 85 + .../thing/sys/tenant/dto/UpdateStatusDTO.java | 26 + .../tenant/entity/SysTenantDetailEntity.java | 92 + .../sys/tenant/entity/SysTenantEntity.java | 99 ++ .../tenant/entity/SysTenantGroupEntity.java | 41 + .../tenant/excel/SysTenantDetailExcel.java | 43 + .../tenant/mapper/SysTenantDetailMapper.java | 20 + .../tenant/mapper/SysTenantGroupMapper.java | 16 + .../sys/tenant/mapper/SysTenantMapper.java | 58 + .../service/SysTenantDetailService.java | 102 ++ .../tenant/service/SysTenantGroupService.java | 65 + .../tenant/service/SysTenantRoleService.java | 44 + .../sys/tenant/service/SysTenantService.java | 114 ++ .../impl/SysTenantDetailServiceImpl.java | 715 ++++++++ .../impl/SysTenantGroupServiceImpl.java | 193 +++ .../impl/SysTenantRoleServiceImpl.java | 151 ++ .../service/impl/SysTenantServiceImpl.java | 448 +++++ .../api/controller/IotThingApiController.java | 219 +++ .../controller/IotThingApiLogController.java | 92 + .../thing/thing/api/dto/ApiEntityAttrDTO.java | 103 ++ .../thing/api/dto/ApiEntityRelationDTO.java | 49 + .../com/thing/thing/api/dto/ApiTimeDTO.java | 187 +++ .../thing/thing/api/dto/IotThingApiDTO.java | 89 + .../thing/api/dto/IotThingApiDebugDTO.java | 45 + .../thing/api/dto/IotThingApiLogDTO.java | 56 + .../thing/api/entity/IotThingApiEntity.java | 90 + .../api/entity/IotThingApiLogEntity.java | 60 + .../thing/api/excel/IotThingApiExcel.java | 54 + .../api/mapper/IotThingApiLogMapper.java | 16 + .../thing/api/mapper/IotThingApiMapper.java | 19 + .../api/service/IotThingApiLogService.java | 19 + .../thing/api/service/IotThingApiService.java | 80 + .../impl/IotThingApiLogServiceImpl.java | 57 + .../service/impl/IotThingApiServiceImpl.java | 873 ++++++++++ .../bizconfig/dto/IotThingBizConfigDTO.java | 148 ++ .../dto/IotThingBizConfigItemDTO.java | 49 + .../entity/IotThingBizConfigEntity.java | 47 + .../entity/IotThingBizConfigItemEntity.java | 42 + .../mapper/IotThingBizConfigItemMapper.java | 21 + .../mapper/IotThingBizConfigMapper.java | 15 + .../service/IotThingBizConfigItemService.java | 20 + .../service/IotThingBizConfigService.java | 37 + .../IotThingBizConfigItemServiceImpl.java | 58 + .../impl/IotThingBizConfigServiceImpl.java | 168 ++ .../thing/cache/service/CacheCallback.java | 53 + .../thing/thing/cache/service/CacheInit.java | 180 ++ .../thing/thing/cache/service/ThingCache.java | 169 ++ .../cache/service/ThingModelCacheService.java | 20 + .../impl/ThingModelCacheServiceImpl.java | 65 + .../com/thing/thing/common/dto/BasicsDTO.java | 18 + .../ThingManageContextController.java | 37 + .../service/ThingManageContextService.java | 146 ++ .../impl/ThingManageContextServiceImpl.java | 330 ++++ .../controller/IotThingDictController.java | 172 ++ .../thing/thing/dict/dto/IotThingDictDTO.java | 74 + .../dict/dto/IotThingDictRelationListDTO.java | 70 + .../thing/dict/entity/IotThingDictEntity.java | 99 ++ .../thing/dict/excel/IotThingDictExcel.java | 51 + .../thing/dict/mapper/IotThingDictMapper.java | 16 + .../dict/service/IotThingDictService.java | 60 + .../service/impl/IotThingDictServiceImpl.java | 387 +++++ .../IotThingDictRelationController.java | 223 +++ .../dto/IotThingDictRelationDTO.java | 79 + .../entity/IotThingDictRelationEntity.java | 121 ++ .../excel/IotThingDictRelationExcel.java | 44 + .../mapper/IotThingDictRelationMapper.java | 16 + .../param/IotThingDictRelationParamDTO.java | 98 ++ .../service/IotThingDictRelationService.java | 75 + .../impl/IotThingDictRelationServiceImpl.java | 585 +++++++ .../controller/IotThingEntityController.java | 249 +++ .../thing/entity/dto/ApiEntityReqDTO.java | 43 + .../thing/thing/entity/dto/DataApiRspDTO.java | 20 + .../entity/dto/IotBasicsThingEntityDTO.java | 56 + .../thing/entity/dto/IotThingEntityDTO.java | 29 + .../entity/dto/IotThingEntityDictDTO.java | 23 + .../entity/dto/IotThingEntityInfoDTO.java | 48 + .../thing/entity/dto/IotThingViewDTO.java | 100 ++ .../entity/dto/IotThingViewSourceDTO.java | 37 + .../entity/dto/old/ApiEntityAttrArrDTO.java | 32 + .../entity/dto/old/ApiEntityAttrDTO.java | 31 + .../entity/dto/old/ApiEntityAttrsDTO.java | 26 + .../thing/entity/dto/old/ApiEntityDTO.java | 54 + .../entity/dto/old/ApiEntityParamDTO.java | 56 + .../entity/dto/old/ApiEntitySearchDTO.java | 21 + .../thing/entity/entity/IotThingEntity.java | 101 ++ .../thing/entity/excel/IotThingViewExcel.java | 60 + .../entity/mapper/IotThingEntityMapper.java | 23 + .../entity/param/DistributeEntityDTO.java | 30 + .../thing/entity/param/IotSyncEntityDTO.java | 31 + .../entity/param/IotThingEntityParamDTO.java | 57 + .../thing/entity/param/IotThingStatusDTO.java | 17 + .../entity/service/IotThingEntityService.java | 106 ++ .../impl/IotThingEntityServiceImpl.java | 982 +++++++++++ .../controller/CommonConfigController.java | 115 ++ .../thing/thing/ext/dto/CommonConfigDTO.java | 72 + .../thing/ext/entity/CommonConfigEntity.java | 49 + .../thing/ext/mapper/CommonConfigMapper.java | 18 + .../ext/service/CommonConfigService.java | 20 + .../service/impl/CommonConfigServiceImpl.java | 45 + .../controller/IotGroupInfoController.java | 265 +++ .../IotGroupRelationController.java | 147 ++ .../thing/group/dto/IotGroupEntityDTO.java | 30 + .../group/dto/IotGroupEntityListDTO.java | 52 + .../thing/group/dto/IotGroupInfoDTO.java | 67 + .../group/dto/IotGroupInfoFormatDTO.java | 30 + .../thing/group/dto/IotGroupRelationDTO.java | 50 + .../group/dto/IotGroupRelationDelDTO.java | 31 + .../com/thing/thing/group/dto/IotSortDTO.java | 17 + .../group/entity/IotGroupInfoEntity.java | 65 + .../group/entity/IotGroupRelationEntity.java | 42 + .../thing/group/excel/IotGroupInfoExcel.java | 33 + .../group/excel/IotGroupRelationExcel.java | 37 + .../group/mapper/IotGroupInfoMapper.java | 22 + .../group/mapper/IotGroupRelationMapper.java | 33 + .../group/service/IotGroupInfoService.java | 102 ++ .../service/IotGroupRelationService.java | 31 + .../service/impl/IotGroupInfoServiceImpl.java | 518 ++++++ .../impl/IotGroupRelationServiceImpl.java | 250 +++ .../controller/IotThingModelController.java | 176 ++ .../thing/model/dto/IotThingModelDTO.java | 51 + .../thing/model/dto/IotThingModelExcel.java | 34 + .../thing/thing/model/dto/ModelDetailDTO.java | 28 + .../model/entity/IotThingModelEntity.java | 67 + .../model/mapper/IotThingModelMapper.java | 16 + .../model/param/DistributeModelParam.java | 42 + .../model/service/IotThingModelService.java | 58 + .../impl/IotThingModelServiceImpl.java | 445 +++++ .../IotThingRelationDetailController.java | 161 ++ .../detail/dto/IotThingRelationDetailDTO.java | 58 + .../dto/RelationDetailBatchSaveDTO.java | 33 + .../relation/detail/dto/ThingRelationDTO.java | 68 + .../relation/detail/dto/ThingTreeDTO.java | 113 ++ .../entity/IotThingRelationDetailEntity.java | 79 + .../mapper/IotThingRelationDetailMapper.java | 26 + .../param/IotThingRelationDetailParamDTO.java | 69 + .../IotThingRelationDetailService.java | 76 + .../IotThingRelationDetailServiceImpl.java | 927 ++++++++++ .../IotThingRelationRootController.java | 121 ++ .../root/dto/IotThingRelationRootDTO.java | 66 + .../root/dto/IotThingRelationRootListDTO.java | 54 + .../relation/root/dto/ThingSortTreeDTO.java | 40 + .../entity/IotThingRelationRootEntity.java | 63 + .../mapper/IotThingRelationRootMapper.java | 16 + .../service/IotThingRelationRootService.java | 69 + .../impl/IotThingRelationRootServiceImpl.java | 378 +++++ .../thing/tskv/controller/TsKvController.java | 58 + .../com/thing/thing/tskv/dto/ThingAttr.java | 75 + .../thing/thing/tskv/dto/ThingAttrDTO.java | 51 + .../com/thing/thing/tskv/dto/TsKvDTO.java | 48 + .../thing/thing/tskv/service/TskvService.java | 342 ++++ .../tskv/service/impl/TskvServiceImpl.java | 651 +++++++ .../main/java/com/thing/util/BeanUtil.java | 63 + .../java/com/thing/util/TenantSubsetUtil.java | 72 + .../main/java/com/thing/util/ToolUtil.java | 169 ++ .../ExtensionSocketEventListener.java | 42 + .../websocket/QueueSocketEventListener.java | 152 ++ .../websocket/WebSocketDashboardServer.java | 116 ++ .../com/thing/websocket/WebSocketServer.java | 298 ++++ .../websocket/config/WebSocketConfig.java | 52 + .../thing/websocket/data/SocketDataCache.java | 34 + .../data/WebSocketDashboardData.java | 18 + .../thing/websocket/data/WebSocketData.java | 34 + .../mapper/api/IotThingApiLogMapper.xml | 18 + .../mapper/api/IotThingApiMapper.xml | 11 + .../bizconfig/IotThingBizConfigItemMapper.xml | 34 + .../bizconfig/IotThingBizConfigMapper.xml | 18 + .../control/IotDeviceControlLogMapper.xml | 56 + .../mapper/control/IotDeviceControlMapper.xml | 87 + .../dashboard/IotDashboardElementMapper.xml | 27 + .../dashboard/IotDashboardGroupMapper.xml | 26 + .../mapper/dashboard/IotDashboardMapper.xml | 26 + .../dashboard/IotDashboardSvgMapper.xml | 12 + .../device/IotThingMenuConfigMapper.xml | 19 + .../mapper/device/IotThingSourceMapper.xml | 104 ++ .../mapper/dict/IotThingDictMapper.xml | 74 + .../dict/IotThingDictRelationMapper.xml | 303 ++++ .../mapper/ext/CommonConfigMapper.xml | 21 + .../TransportExtendCalculationMapper.xml | 17 + .../mapper/extend/TransportExtendMapper.xml | 18 + .../extend/TransportExtendMsgMapper.xml | 67 + .../mapper/group/IotGroupInfoMapper.xml | 34 + .../mapper/group/IotGroupRelationMapper.xml | 137 ++ .../mapper/model/IotThingEntityMapper.xml | 21 + .../mapper/model/IotThingModelMapper.xml | 6 + .../relation/IotThingRelationDetailMapper.xml | 107 ++ .../relation/IotThingRelationRootMapper.xml | 112 ++ .../resources/mapper/sys/SysDeptMapper.xml | 40 + .../mapper/sys/SysDictDataMapper.xml | 25 + .../mapper/sys/SysDictTypeMapper.xml | 17 + .../mapper/sys/SysIndustryTypeMapper.xml | 20 + .../mapper/sys/SysLanguageMapper.xml | 19 + .../resources/mapper/sys/SysMenuMapper.xml | 121 ++ .../resources/mapper/sys/SysParamsMapper.xml | 23 + .../resources/mapper/sys/SysPostMapper.xml | 7 + .../resources/mapper/sys/SysRegionMapper.xml | 35 + .../mapper/sys/SysRoleDataScopeMapper.xml | 28 + .../resources/mapper/sys/SysRoleMapper.xml | 9 + .../mapper/sys/SysRoleMenuMapper.xml | 27 + .../mapper/sys/SysRoleUserMapper.xml | 56 + .../mapper/sys/SysTenantDetailMapper.xml | 51 + .../mapper/sys/SysTenantGroupMapper.xml | 12 + .../resources/mapper/sys/SysTenantMapper.xml | 158 ++ .../resources/mapper/sys/SysUserMapper.xml | 87 + .../mapper/sys/SysUserMenuMapper.xml | 34 + .../mapper/sys/SysUserPostMapper.xml | 24 + .../mapper/sys/SysUserTokenMapper.xml | 26 + .../mapper/sys/message/SysMailLogMapper.xml | 7 + .../sys/message/SysMailTemplateMapper.xml | 10 + .../mapper/sys/message/SysSmsLogMapper.xml | 7 + .../mapper/sys/message/SysSmsMapper.xml | 7 + .../mapper/sys/notice/SysNoticeMapper.xml | 38 + .../mapper/sys/notice/SysNoticeUserMapper.xml | 14 + 2428 files changed, 187334 insertions(+) create mode 100644 common/cache/pom.xml create mode 100644 common/cache/src/main/java/com/thing/common/cache/config/CacheManagerProperties.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/config/LocalCacheConfig.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/config/RedisConfig.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/constants/CacheNameEnum.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidConfig.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidTask.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/RedisKeys.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/RedisUtils.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/SysParamsRedis.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/redis/aspect/RedisAspect.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/service/AbstractCacheService.java create mode 100644 common/cache/src/main/java/com/thing/common/cache/service/CacheInitializer.java create mode 100644 common/core/pom.xml create mode 100644 common/core/src/main/java/com/thing/common/core/annotation/CustomExcel.java create mode 100644 common/core/src/main/java/com/thing/common/core/annotation/CustomExcelCollection.java create mode 100644 common/core/src/main/java/com/thing/common/core/annotation/LogOperation.java create mode 100644 common/core/src/main/java/com/thing/common/core/config/AsyncEventConfig.java create mode 100644 common/core/src/main/java/com/thing/common/core/config/RestTemplateConfig.java create mode 100644 common/core/src/main/java/com/thing/common/core/constants/Constant.java create mode 100644 common/core/src/main/java/com/thing/common/core/container/GlobalContainer.java create mode 100644 common/core/src/main/java/com/thing/common/core/convert/DateConverter.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ActionType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AggType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AlarmSettingType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AlarmStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AlarmType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ApiFuncEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ApiSeparateAttr.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ApiType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ApiTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ApiTypes.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AttributeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/AttributeTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CacheEventType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CalculationStatusEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CalculationSymbol.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CarbonLifecycleEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CloudService.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ColumnRelationType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/CompanyType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ConfigTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ContainsCondition.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DataBaseType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DataTreatingMarkEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DatasetAttrTimestampType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DatasetInterValType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DatasetSeparateAttr.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DatasetStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DatasetThingRelationStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DefaultType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DeleteEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DeviceSendCmdStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DeviceTemplateMark.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DictRelationEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/DictTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/EnergyVarietyEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ExtendRelationType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/FormulaSymbols.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/GateWayStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/Granularity.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/GroupTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/IntervalPushStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/IsDefaultEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MenuTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MimeTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MinioFileContentTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MonitorPolymerizationType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MonitorTimeType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/MsgType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/OrderStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/PeakValleyEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/PermissionStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/Protocol.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ProtocolPrefix.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/PushReceiveStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/PushStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/QueryChannel.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/QueueEventType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/QueueOriginType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/RegionLeafEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/RegionLevelEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/RepeatPushStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ReportTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/RoleType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/RsaKeyType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ScheduleStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ShowStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SignEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SignTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SmsService.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SocketEventType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SourceStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SubscribeStatusEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SuperAdminEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SuperTenantEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SyncUpdateEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/SysTenantEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TaskTime.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TaskTimeType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TaskType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TaskWeek.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TbTimeout.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TemplateMark.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TenantGroupEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TenantSaveType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingAttrType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingEnableStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingExistType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingRealType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingSortType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingStatStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/ThingStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TimeHorizonEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TimeType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TreeNodeStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/TriggerTypeEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/UserStatusEnum.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/UsernameType.java create mode 100644 common/core/src/main/java/com/thing/common/core/enumeration/menuStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/AuthParam.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/CacheAlarmActionEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/CacheAlarmEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/CacheCalculationEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/CacheEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/ExtensionSocketEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/QueueAlarmEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/QueueCacheEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/QueueCalculationEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/QueueDeviceEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/QueueSocketEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/SocketClientEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/SocketEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/event/SocketTelemetryEvent.java create mode 100644 common/core/src/main/java/com/thing/common/core/exception/ErrorCode.java create mode 100644 common/core/src/main/java/com/thing/common/core/exception/ExceptionUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/exception/GlobalExceptionHandler.java create mode 100644 common/core/src/main/java/com/thing/common/core/exception/SysException.java create mode 100644 common/core/src/main/java/com/thing/common/core/expression/functions/AggFunction.java create mode 100644 common/core/src/main/java/com/thing/common/core/expression/package-info.java create mode 100644 common/core/src/main/java/com/thing/common/core/expression/parser/AggFormulaParser.java create mode 100644 common/core/src/main/java/com/thing/common/core/initializer/AbstractAppInitializer.java create mode 100644 common/core/src/main/java/com/thing/common/core/initializer/AppInitializeExecutor.java create mode 100644 common/core/src/main/java/com/thing/common/core/message/MessageData.java create mode 100644 common/core/src/main/java/com/thing/common/core/rest/dto/ApiRequest.java create mode 100644 common/core/src/main/java/com/thing/common/core/rest/dto/ApiResponse.java create mode 100644 common/core/src/main/java/com/thing/common/core/rest/service/ApiService.java create mode 100644 common/core/src/main/java/com/thing/common/core/rest/service/ApiServiceImpl.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/AggUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/BizUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/CompareUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/ConvertUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/DataTypeUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/DateTimeUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/EncryptUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/FormulaUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/HttpContextUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/IpUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/JacksonUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/JsonProcessingUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/JsonUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/JwtUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/KeyGeneratorUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/MapUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/MessageUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/PageUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/RandomUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/RegexUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/RestTemplateUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/ResultCommonUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/SpringContextUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/SqlUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/TokenGenerator.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/TreeUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/excel/ExcelExportUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/excel/ExcelUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/export/BaseExportService.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/export/ExcelEntityGenerator.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportService.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportUtil.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/params/SortTreeNode.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/params/SplitedTimeParam.java create mode 100644 common/core/src/main/java/com/thing/common/core/utils/params/TreeNode.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/AssertUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/ValidatorUtils.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/AddGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/AliyunGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/DefaultGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/FastDFSGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/Group.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/GroupNameGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/LocalGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/MinioGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/QcloudGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/QiniuGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/UpdateGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/validator/group/WxOpenIdGroup.java create mode 100644 common/core/src/main/java/com/thing/common/core/web/constants/HttpStatus.java create mode 100644 common/core/src/main/java/com/thing/common/core/web/response/PageData.java create mode 100644 common/core/src/main/java/com/thing/common/core/web/response/Result.java create mode 100644 common/core/src/main/resources/i18n/messages.properties create mode 100644 common/core/src/main/resources/i18n/messages_en_US.properties create mode 100644 common/core/src/main/resources/i18n/messages_zh_CN.properties create mode 100644 common/core/src/main/resources/i18n/messages_zh_TW.properties create mode 100644 common/core/src/main/resources/i18n/validation.properties create mode 100644 common/core/src/main/resources/i18n/validation_en_US.properties create mode 100644 common/core/src/main/resources/i18n/validation_zh_CN.properties create mode 100644 common/core/src/main/resources/i18n/validation_zh_TW.properties create mode 100644 common/core/src/main/resources/keys/rsa_private.pem create mode 100644 common/core/src/main/resources/keys/rsa_public.pem create mode 100644 common/data/pom.xml create mode 100644 common/data/src/main/java/com/thing/common/data/dto/AttrCache.java create mode 100644 common/data/src/main/java/com/thing/common/data/dto/QueueMsgDTO.java create mode 100644 common/data/src/main/java/com/thing/common/data/dto/TelemetryDeleteDTO.java create mode 100644 common/data/src/main/java/com/thing/common/data/dto/TelemetrySaveDTO.java create mode 100644 common/data/src/main/java/com/thing/common/data/dto/ThingAttrParam.java create mode 100644 common/data/src/main/java/com/thing/common/data/dto/TsKvReqParam.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/AbstractQueueEvent.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/DataFilterEvent.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/EventPublisher.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/PublisherExecutor.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/QueueConsumerEvent.java create mode 100644 common/data/src/main/java/com/thing/common/data/event/QueueEvent.java create mode 100644 common/data/src/main/java/com/thing/common/data/tskv/TsKvBaseEntity.java create mode 100644 common/data/src/main/java/com/thing/common/data/tskv/TsKvDTO.java create mode 100644 common/data/src/main/java/com/thing/common/data/tskv/TsKvEntity.java create mode 100644 common/data/src/main/proto/queue.proto create mode 100644 common/orm/pom.xml create mode 100644 common/orm/src/main/java/com/thing/common/orm/annotation/DataFilter.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/config/MyBatisFlexConfig.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/dto/BaseDTO.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/dto/BaseThingDTO.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/dto/UserDetail.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/entity/BaseDateEntity.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/entity/BaseEntity.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/entity/BaseInfoEntity.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/entity/BaseTenantEntity.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/handler/MybatisExceptionHandler.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/listener/AuthInfoReceiver.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/listener/EntityInsertListener.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/listener/EntityUpdateListener.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/mapper/PowerBaseMapper.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/service/IBaseService.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/service/impl/BaseServiceImpl.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/utils/IdGenerator.java create mode 100644 common/orm/src/main/java/com/thing/common/orm/utils/SnowflakeIdWorker.java create mode 100644 common/pom.xml create mode 100644 common/queue/pom.xml create mode 100644 common/queue/src/main/java/com/thing/queue/QueueAdmin.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueCallback.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueConsumer.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueMsg.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueMsgDecoder.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueMsgHeaders.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueMsgMetadata.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueProducer.java create mode 100644 common/queue/src/main/java/com/thing/queue/QueueThreadFactory.java create mode 100644 common/queue/src/main/java/com/thing/queue/cluster/ClusterService.java create mode 100644 common/queue/src/main/java/com/thing/queue/cluster/ClusterServiceImpl.java create mode 100644 common/queue/src/main/java/com/thing/queue/common/AbstractQueueConsumerTemplate.java create mode 100644 common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueConsumer.java create mode 100644 common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueProducer.java create mode 100644 common/queue/src/main/java/com/thing/queue/memory/InMemoryStorage.java create mode 100644 common/queue/src/main/java/com/thing/queue/memoryto/InMemoryQueueService.java create mode 100644 common/queue/src/main/java/com/thing/queue/memoryto/InMemoryStorage.java create mode 100644 common/queue/src/main/java/com/thing/queue/memoryto/MemoryConsumerService.java create mode 100644 common/queue/src/main/java/com/thing/queue/memoryto/QueueProducer.java create mode 100644 common/queue/src/main/java/com/thing/queue/memoryto/impl/InMemoryQueueServiceImpl.java create mode 100644 common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsg.java create mode 100644 common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsgHeaders.java create mode 100644 common/queue/src/main/java/com/thing/queue/message/ProtoQueueMsg.java create mode 100644 common/queue/src/main/java/com/thing/queue/message/ServiceQueue.java create mode 100644 common/queue/src/main/java/com/thing/queue/message/ServiceType.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/HashPartitionServiceImpl.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/PartitionChangeEvent.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/PartitionResetEvent.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/PartitionService.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfo.java create mode 100644 common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfoKey.java create mode 100644 common/queue/src/main/java/com/thing/queue/provider/CoreQueueFactory.java create mode 100644 common/queue/src/main/java/com/thing/queue/provider/CoreQueueProducerProvider.java create mode 100644 common/queue/src/main/java/com/thing/queue/provider/InMemoryCoreQueueFactory.java create mode 100644 common/queue/src/main/java/com/thing/queue/provider/QueueProducerProvider.java create mode 100644 common/queue/src/main/java/com/thing/queue/provider/RabbitMqCoreQueueFactory.java create mode 100644 common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqAdmin.java create mode 100644 common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqConsumerTemplate.java create mode 100644 common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqProducerTemplate.java create mode 100644 common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqQueueArguments.java create mode 100644 common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqSettings.java create mode 100644 common/queue/src/main/java/com/thing/queue/setting/CoreQueueSetting.java create mode 100644 common/queue/src/main/java/com/thing/queue/util/Topics.java create mode 100644 common/queue/src/main/proto/queue.proto create mode 100644 common/script/pom.xml create mode 100644 common/script/src/main/java/com/thing/CustomScriptException.java create mode 100644 common/script/src/main/java/com/thing/ScriptCreateService.java create mode 100644 common/script/src/main/java/com/thing/ScriptCreateServiceImpl.java create mode 100644 common/script/src/main/java/com/thing/ScriptExecutionTask.java create mode 100644 common/script/src/main/java/com/thing/ScriptLanguage.java create mode 100644 common/script/src/main/java/com/thing/ScriptLog.java create mode 100644 common/script/src/main/java/com/thing/ScriptMsg.java create mode 100644 common/script/src/main/java/com/thing/api/AbstractScriptEngine.java create mode 100644 common/script/src/main/java/com/thing/api/AbstractScriptInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/FormulaInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/FunctionScriptFactory.java create mode 100644 common/script/src/main/java/com/thing/api/JavaInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/ScriptEngine.java create mode 100644 common/script/src/main/java/com/thing/api/ScriptInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/aviator/AviatorExecutionTask.java create mode 100644 common/script/src/main/java/com/thing/api/aviator/AviatorInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/aviator/AviatorScript.java create mode 100644 common/script/src/main/java/com/thing/api/aviator/AviatorScriptEngine.java create mode 100644 common/script/src/main/java/com/thing/api/aviator/DefaultAviatorInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/DefaultTbelInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbDate.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbJson.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbUtils.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbelExecutionTask.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbelInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbelScript.java create mode 100644 common/script/src/main/java/com/thing/api/mvel/TbelScriptEngine.java create mode 100644 common/script/src/main/java/com/thing/api/nashorn/DefaultNashornInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/nashorn/NashornExecutionTask.java create mode 100644 common/script/src/main/java/com/thing/api/nashorn/NashornInvokeService.java create mode 100644 common/script/src/main/java/com/thing/api/nashorn/NashornScript.java create mode 100644 common/script/src/main/java/com/thing/api/nashorn/NashornScriptEngine.java create mode 100644 common/script/src/main/java/com/thing/modules/cache/bean/ScriptCache.java create mode 100644 common/script/src/main/java/com/thing/modules/cache/runner/ScriptCacheInitializer.java create mode 100644 common/script/src/main/java/com/thing/modules/cache/service/ScriptCacheService.java create mode 100644 common/script/src/main/java/com/thing/modules/cache/service/impl/ScriptCacheServiceImpl.java create mode 100644 common/script/src/main/java/com/thing/modules/controller/ScriptInfoController.java create mode 100644 common/script/src/main/java/com/thing/modules/controller/ScriptLogController.java create mode 100644 common/script/src/main/java/com/thing/modules/dto/QueryMsg.java create mode 100644 common/script/src/main/java/com/thing/modules/dto/ScriptInfoDTO.java create mode 100644 common/script/src/main/java/com/thing/modules/dto/ScriptLogDTO.java create mode 100644 common/script/src/main/java/com/thing/modules/dto/ScriptTypeDTO.java create mode 100644 common/script/src/main/java/com/thing/modules/entity/ScriptInfoEntity.java create mode 100644 common/script/src/main/java/com/thing/modules/entity/ScriptLogEntity.java create mode 100644 common/script/src/main/java/com/thing/modules/excel/ScriptInfoExcel.java create mode 100644 common/script/src/main/java/com/thing/modules/excel/ScriptLogExcel.java create mode 100644 common/script/src/main/java/com/thing/modules/mapper/ScriptInfoMapper.java create mode 100644 common/script/src/main/java/com/thing/modules/mapper/ScriptLogMapper.java create mode 100644 common/script/src/main/java/com/thing/modules/service/ScriptInfoService.java create mode 100644 common/script/src/main/java/com/thing/modules/service/ScriptLogService.java create mode 100644 common/script/src/main/java/com/thing/modules/service/impl/ScriptInfoServiceImpl.java create mode 100644 common/script/src/main/java/com/thing/modules/service/impl/ScriptLogServiceImpl.java create mode 100644 common/script/src/main/resources/script/ScriptInfoMapper.xml create mode 100644 common/script/src/main/resources/script/ScriptLogMapper.xml create mode 100644 common/security/pom.xml create mode 100644 common/security/src/main/java/com/thing/cache/SecurityCache.java create mode 100644 common/security/src/main/java/com/thing/cache/SecurityCacheManager.java create mode 100644 common/security/src/main/java/com/thing/config/FilterConfig.java create mode 100644 common/security/src/main/java/com/thing/config/ShiroConfig.java create mode 100644 common/security/src/main/java/com/thing/config/WebMvcConfig.java create mode 100644 common/security/src/main/java/com/thing/oauth2/AbstractOauth2Realm.java create mode 100644 common/security/src/main/java/com/thing/oauth2/Oauth2Filter.java create mode 100644 common/security/src/main/java/com/thing/oauth2/Oauth2Token.java create mode 100644 common/security/src/main/java/com/thing/password/BCrypt.java create mode 100644 common/security/src/main/java/com/thing/password/BCryptPasswordEncoder.java create mode 100644 common/security/src/main/java/com/thing/password/PasswordEncoder.java create mode 100644 common/security/src/main/java/com/thing/password/PasswordUtils.java create mode 100644 common/security/src/main/java/com/thing/xss/SqlFilter.java create mode 100644 common/security/src/main/java/com/thing/xss/XssFilter.java create mode 100644 common/security/src/main/java/com/thing/xss/XssHttpServletRequestWrapper.java create mode 100644 common/security/src/main/java/com/thing/xss/XssUtils.java create mode 100644 common/transport/pom.xml create mode 100644 common/transport/src/main/java/com/thing/transport/api/AbstractTransportContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/SessionMetaData.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/TransportService.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/TransportServiceCallback.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/TransportServiceImpl.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/adaptor/AdaptorException.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/adaptor/JsonConverter.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/session/SessionContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/session/TransportSessionUtil.java create mode 100644 common/transport/src/main/java/com/thing/transport/api/tools/RateLimits.java create mode 100644 common/transport/src/main/java/com/thing/transport/http/HttpTransportContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/http/TelemetryController.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerConnectController.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerMsgController.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerConnectDTO.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerMsgDTO.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerConnectEntity.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerMsgEntity.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerConnectExcel.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerMsgExcel.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerConnectMapper.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerMsgMapper.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerConnectService.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerMsgService.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerConnectServiceImpl.java create mode 100644 common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerMsgServiceImpl.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTopics.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportHandler.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportServerInitializer.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportService.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/JsonMqttAdaptor.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/MqttTransportAdaptor.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractDeviceAwareSessionContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractMqttDeviceAwareSessionContext.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/session/DeviceSessionCtx.java create mode 100644 common/transport/src/main/java/com/thing/transport/mqtt/broker/session/MqttTopicMatcher.java create mode 100644 common/transport/src/main/java/com/thing/transport/util/ConvertSendUtil.java create mode 100644 common/tskv/pom.xml create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/NativeSQLTool.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/QueryWrapperUtil.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvCk.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestCk.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestMy.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestPg.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvMy.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvPg.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKv.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKvLatest.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/CalcLogSaveEvent.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/FilterLogSaveEvent.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/PeakEvent.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEvent.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvCkMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestCkMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestMyMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestPgMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvMyMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvPgMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvCkMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvLatestCkMapper.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/AggType.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/AmExecutor.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/DBExecutor.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/DatabaseType.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/HhExecutor.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/LatestNativeSQL.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/TsKvNativeSQL.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/TsKvService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/TsKvServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestBaseService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvBaseService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgServiceImpl.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkService.java create mode 100644 common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkServiceImpl.java create mode 100644 common/util/pom.xml create mode 100644 common/util/src/main/java/com/thing/common/util/AfterStartUp.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/AbstractListeningExecutor.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/CallbackService.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/ForkJoinWorkerThreadFactory.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/ListeningExecutor.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/TBExecutors.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/ThingExecutors.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/ThingForkJoinWorkerThreadFactory.java create mode 100644 common/util/src/main/java/com/thing/common/util/thread/ThingThreadFactory.java create mode 100644 common/util/src/main/java/com/thing/common/util/time/DaXiaUtils.java create mode 100644 modules/alarm/pom.xml create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmConfigController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDictController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDisposeLogController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleActionController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleEntityController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleLogController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleSettingController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDeleteParam.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDictDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDisposeLogDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmJson.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmProcessingUserDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCache.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCacheDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDetailDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionPageDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleEntityDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleLogDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleReportDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingPageDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmConfigEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictData.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictType.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDisposeLogEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleActionEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntityEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleLogEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleSettingEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmConfigMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDictMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDisposeLogMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleActionMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleEntityMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleLogMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleSettingMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmConfigService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDictService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDisposeLogService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleActionService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleEntityService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleLogService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleSettingService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmConfigServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDictServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDisposeLogServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleActionServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleEntityServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleLogServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleSettingServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheInitializer.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheServiceImpl.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/cache/AlarmEventListener.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/DefaultIntegrationContext.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/IntegrationContext.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/condition/AviatorScriptConditionEvaluator.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/condition/Condition.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/condition/ConditionEvaluator.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEvent.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEventListener.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/event/LogSaveActionEvent.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/configuration/event/MsgActionEvent.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/controller/MsgPushSettingController.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/AlarmMsgDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/MsgPushSettingDTO.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/entity/MsgPushSettingEntity.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/excel/MsgPushSettingExcel.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/mapper/MsgPushSettingMapper.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/service/MsgPushSettingService.java create mode 100644 modules/alarm/src/main/java/com/thing/alarm/msgpush/service/impl/MsgPushSettingServiceImpl.java create mode 100644 modules/alarm/src/main/resources/mapper/AlarmConfigMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmDictMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmDisposeLogMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmRuleActionMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmRuleEntityMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmRuleLogMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmRuleMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/AlarmRuleSettingMapper.xml create mode 100644 modules/alarm/src/main/resources/mapper/MsgPushSettingMapper.xml create mode 100644 modules/calculation/pom.xml create mode 100644 modules/calculation/src/main/java/com/thing/calculation/cache/CalcConfigCacheInitializer.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheInitializer.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationEventListener.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaLogController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaSettingController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationUsageDictController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationAttr.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationConditionForm.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationForm.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaAttributeDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaLogDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaSettingDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationJson.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationParam.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationResultDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationSettingCache.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialForm.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialResult.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationUsageDictRequest.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/dto/RecalculationForm.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaAttributeEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaLogEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaSettingEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaAttributeMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaLogMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaSettingMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaAttributeService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaLogService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaSettingService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationUsageDictService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaAttributeServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaLogServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaSettingServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationUsageDictServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/controller/CalcLogController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/controller/CalcTargetConfigController.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/CalcLogDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/CalcSourceConfigDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/CalcTargetConfigDTO.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/ExecuteCalcRequest.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/LogSourceInfoValue.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/ReCalcRequest.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcRequest.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcResponse.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/entity/CalcLogEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/entity/CalcSourceConfigEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/entity/CalcTargetConfigEntity.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcConfigType.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcStatus.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/excel/CalcConfigExcel.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/excel/CalcLogExcel.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/excel/CalculationTypeExcel.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/excel/MappingTypeExcel.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/handler/CalcExecuteHandler.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/handler/CalcLogSaveHandler.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/initializer/CalcHistoryLogSyncInitializer.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/listener/CalcLogSaveEventListener.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/mapper/CalcLogMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/mapper/CalcSourceConfigMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/mapper/CalcTargetConfigMapper.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/CalcLogService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/CalcSourceConfigService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/CalcTargetConfigService.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcLogServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcSourceConfigServiceImpl.java create mode 100644 modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcTargetConfigServiceImpl.java create mode 100644 modules/calculation/src/main/resources/mapper/calc/CalcLogMapper.xml create mode 100644 modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaAttributeMapper.xml create mode 100644 modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaLogMapper.xml create mode 100644 modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaSettingMapper.xml create mode 100644 modules/carbon-public/pom.xml create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/api/CertificateApi.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/api/MaterialFactorApi.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionModelApi.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionResultApi.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ApiConstant.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/CarbonFactorConstant.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/PointsConstant.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ReportSourceTypeConstant.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubCertificateController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubEngEffController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubFootprintLibController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubMaterialFactorController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsLogsController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsRuleController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionModelController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportConfigController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionResultController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubSupplierController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/IotCarbonEnterpriseAuthController.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubCertificateDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubEngEffDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubFootprintInfo.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfo.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfoOnYear.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordDispose.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordMPT.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPM.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPT.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPU.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordR.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordSimpleAgg.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorApiDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorUseDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsLogsDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsRuleDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionIdName.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionModelDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportConfigDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultReport.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDetail.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierProduct.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/IotCarbonEnterpriseAuthDTO.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateRequest.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateResponse.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubCertificateEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubEngEffEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorUseEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsLogsEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsRuleEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionModelEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportConfigEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionResultEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubSupplierEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/IotCarbonEnterpriseAuthEntity.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilter.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilterConfig.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubRequestWrapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubResponseWrapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/listener/SupplierCreateListener.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubCertificateMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubEngEffMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorUseMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsLogsMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsRuleMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionModelMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportConfigMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionResultMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubSupplierMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/IotCarbonEnterpriseAuthMapper.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubCertificateService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubEngEffService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubFootprintLibService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorUseService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsLogsService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsRuleService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionModelService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportConfigService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionResultService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubSupplierService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/IotCarbonEnterpriseAuthService.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubCertificateServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubEngEffServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubFootprintLibServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorUseServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsLogsServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsRuleServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionModelServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportConfigServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionResultServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubSupplierServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/IotCarbonEnterpriseAuthServiceImpl.java create mode 100644 modules/carbon-public/src/main/java/com/thing/carbon/pub/task/CarbonReportGenerateTask.java create mode 100644 modules/carbon-public/src/main/resources/mapper/CarbonPubProductionReportMapper.xml create mode 100644 modules/carbon-public/src/main/resources/mapper/CarbonPubSupplierMapper.xml create mode 100644 modules/carbon-track/pom.xml create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/dto/IotCarbonIndirectEnergyDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/entity/IotCarbonIndirectEnergyEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/mapper/IotCarbonIndirectEnergyMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/IotCarbonIndirectEnergyService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/impl/IotCarbonIndirectEnergyServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/controller/IotCarbonBomController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/BomCarbonReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcel.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcelImport.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomImport.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/entity/IotCarbonBomEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/mapper/IotCarbonBomMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/IotCarbonBomService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/impl/IotCarbonBomServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/controller/IotCarbonCertificateController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/CarbonStageValue.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/IotCarbonCertificateDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/SimpleProductInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/entity/IotCarbonCertificateEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/mapper/IotCarbonCertificateMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/IotCarbonCertificateService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/impl/IotCarbonCertificateServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/controller/ReconnectController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderBomDto.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderDto.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/DiscardJsonInit.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/ProductionJsonInit.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/SetpsJsonInit.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/TransportJsonInit.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/UseJsonInit.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderBomMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/OrderService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/impl/OrderServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/task/TestController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/controller/IotCarbonProductionCustomController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/dto/IotCarbonProductionCustomDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/entity/IotCarbonProductionCustomEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/mapper/IotCarbonProductionCustomMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/IotCarbonProductionCustomService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/impl/IotCarbonProductionCustomServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/IndirectEnergyEvent.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessEvent.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessStatusEvent.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionResultEvent.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/IndirectEnergyListener.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessListener.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessStatusListener.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionResultListener.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/controller/IotCarbonOutboundConfigController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/dto/IotCarbonOutboundConfigDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/entity/IotCarbonOutboundConfigEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/mapper/IotCarbonOutboundConfigMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/IotCarbonOutboundConfigService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/impl/IotCarbonOutboundConfigServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/controller/IotCarbonProductionProcessVarietyController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/dto/IotCarbonProductionProcessVarietyDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/entity/IotCarbonProductionProcessVarietyEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/mapper/IotCarbonProductionProcessVarietyMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/IotCarbonProductionProcessVarietyService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/impl/IotCarbonProductionProcessVarietyServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/controller/UsagePlanController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanFormParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/UsagePlanService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/impl/UsagePlanServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/controller/IotCarbonProductionVarietyController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/IotCarbonProductionVarietyDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormPageParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionEnergyInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionNormInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/UsageNormParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/entity/IotCarbonProductionVarietyEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/mapper/IotCarbonProductionVarietyMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/IotCarbonProductionVarietyService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/impl/IotCarbonProductionVarietyServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/controller/IotCarbonProductionRecordController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/IotCarbonProductionRecordDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/OrderParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionOverviewReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionPage.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionResult.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/QuantityDto.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/RecordReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/entity/IotCarbonProductionRecordEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/mapper/IotCarbonProductionRecordMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/IotCarbonProductionRecordService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/impl/IotCarbonProductionRecordServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/controller/IotCarbonProductionResultController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/AggCarbon.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/DisposeDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/IotCarbonProductionResultDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfoOnYear.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonDispose.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonMPT.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPM.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPT.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPU.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonR.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonSimpleAgg.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/MptDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PIndirectDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmProcessDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PtDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PuDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/entity/IotCarbonProductionResultEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/mapper/IotCarbonProductionResultMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/IotCarbonProductionResultService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/impl/IotCarbonProductionResultServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ConfigController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ModelCommunityController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/MaterialJsonBean.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProcessJsonBean.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductJsonBean.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionModelUploadDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionResultSyncDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/CertificatePointsUpdateRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorDetailRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorPageRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryCategoryListRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryTypeListRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelDetailRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelPageRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelUploadRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionResultSyncRequest.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/DefaultPostResponse.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/EncryptedResponse.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/CertificateSyncService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/MaterialFactorService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionModelService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionResultService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/BaseCarbonApiService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/CertificateSyncServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/MaterialFactorServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionModelServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionResultServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/pub/utils/CarbonPubRequestUtil.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/controller/IotCarbonRatioController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/CarBonRatioResult.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/IotCarbonRatioDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/entity/IotCarbonRatioEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/mapper/IotCarbonRatioMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/IotCarbonRatioService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/impl/IotCarbonRatioServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportConfigController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportConfigDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonResultReport.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportConfigEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportConfigMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportConfigService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportConfigServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/controller/RoutematrixController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceParam.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/RoutematrixReq.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/RoutematrixService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/impl/RoutematrixServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/controller/IotCarbonScreenController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/CarbonSummary.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycle.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycleDetail.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductInfo.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/SimpleProductionCarbon.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/IotCarbonScreenService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/impl/IotCarbonScreenServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareDetailsController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDetailsDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareDetailsEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareDetailsMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareDetailsService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareDetailsServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/controller/IotCarbonProcessStepsController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessResult.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessStepsDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/entity/IotCarbonProcessStepsEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/mapper/IotCarbonProcessStepsMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/IotCarbonProcessStepsService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/impl/IotCarbonProcessStepsServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/controller/IotCarbonProductionSyncController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/dto/IotCarbonProductionSyncDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/entity/IotCarbonProductionSyncEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/mapper/IotCarbonProductionSyncMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/IotCarbonProductionSyncService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/impl/IotCarbonProductionSyncServiceImpl.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/CarbonTrackReportGenerateTask.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/DataSharingService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/IndirectEnergyTask.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessStatusTask.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessTask.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionResultTask.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/controller/IotCarbonUseConfigController.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/IotCarbonUseConfigDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/RecalculateByTimeDTO.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/entity/IotCarbonUseConfigEntity.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/mapper/IotCarbonUseConfigMapper.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/IotCarbonUseConfigService.java create mode 100644 modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/impl/IotCarbonUseConfigServiceImpl.java create mode 100644 modules/carbon-track/src/main/resources/application-track.yml create mode 100644 modules/carbon-track/src/main/resources/discard.json create mode 100644 modules/carbon-track/src/main/resources/mapper/CkOrderBomMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/CkOrderMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonBomMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonIndirectEnergyMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonOutboundConfigMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProcessStepsMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProductionProcessVarietyMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProductionRecordMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProductionResultMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProductionSyncMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonProductionVarietyMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonRatioMapper.xml create mode 100644 modules/carbon-track/src/main/resources/mapper/IotCarbonUseConfigMapper.xml create mode 100644 modules/carbon-track/src/main/resources/production.json create mode 100644 modules/carbon-track/src/main/resources/setps.json create mode 100644 modules/carbon-track/src/main/resources/transport.json create mode 100644 modules/carbon-track/src/main/resources/use.json create mode 100644 modules/configuration/pom.xml create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/controller/IotBoardManageController.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/dto/IotBoardManageDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/entity/IotBoardManageEntity.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/excel/IotBoardManageExcel.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/mapper/IotBoardManageMapper.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/service/IotBoardManageService.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/board/service/impl/IotBoardManageServiceImpl.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/controller/IotConfigurationDesigController.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/dto/IotConfigurationDesigDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/entity/IotConfigurationDesigEntity.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/mapper/IotConfigurationDesigMapper.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/service/IotConfigurationDesigService.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/configuration/service/impl/IotConfigurationDesigServiceImpl.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/controller/IotNewSourceMaterialController.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/dto/IotNewSourceMaterialDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/entity/IotNewSourceMaterialEntity.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/mapper/IotNewSourceMaterialMapper.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/IotNewSourceMaterialService.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/impl/IotNewSourceMaterialServiceImpl.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/controller/IotSectionDetailController.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/dto/GroupIofoDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionDetailDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionGroupDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/dto/SectionGroupInfoDTO.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/entity/IotSectionDetailEntity.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/excel/IotSectionDetailExcel.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/mapper/IotSectionDetailMapper.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/service/IotSectionDetailService.java create mode 100644 modules/configuration/src/main/java/com/thing/configuration/section/service/impl/IotSectionDetailServiceImpl.java create mode 100644 modules/configuration/src/main/resources/mapper/material/IotNewSourceMaterialMapper.xml create mode 100644 modules/configuration/src/main/resources/mapper/section/IotSectionDetailMapper.xml create mode 100644 modules/dequeue/pom.xml create mode 100644 modules/dequeue/src/main/java/com/thing/queue/AbstractConsumerService.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/DefaultCoreConsumerService.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/QueueConfiguration.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueLogController.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueMsgLogController.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueLogDTO.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueMsgLogDTO.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueLogEntity.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueMsgLogEntity.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueLogExcel.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueMsgLogExcel.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueLogMapper.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueMsgLogMapper.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueLogService.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueMsgLogService.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueLogServiceImpl.java create mode 100644 modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueMsgLogServiceImpl.java create mode 100644 modules/equipment/pom.xml create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/controller/CheckResultController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/dto/CheckResultDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/entity/CheckResultEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/excel/CheckResultExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/mapper/CheckResultMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/service/CheckResultService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/checkresult/service/impl/CheckResultServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqBxController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxPlanController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxReplacementController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/BxInfoDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/CommonDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxResDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxInfoRes.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanAddDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanUpdateDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxReplacementDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/ReplaceMentUseDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/WxUseDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqBxEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxPlanEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxReplacementEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/BaseExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqBxExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxPlanExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxReplacementExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqBxMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxPlanMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxReplacementMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqBxService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxPlanService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxReplacementService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqBxServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxPlanServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxReplacementServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByDetailController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByPlanController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateDetailController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/controller/SendMailController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDetailDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartInfoDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanStatusDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDetailDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqInfo.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByDetailEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanPartEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateDetailEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByDetailExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByPlanExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateDetailExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByDetailMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanPartMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateDetailMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByDetailService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanPartService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateDetailService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByDetailServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanPartServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateDetailServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckSettingController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardDetailController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckPlanController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckPlanController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/CheckResultRes.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DataVo.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceBaseInfoDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceCheckRecordVo.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckSettingDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailResultDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanRes.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanResDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordResDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordUpdateDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanParamDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ExecutePlanDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/SepcialTsKvParam.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/StandardCheckResultDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TbDataDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ThingDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TimeValueParam.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/UpdateStatusVo.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckSettingEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardDetailEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckPlanEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckPlanEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckSettingExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardDetailExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckPlanExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordDetailExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckPlanExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckRecordExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckSettingMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardDetailMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckPlanMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckPlanMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckSettingService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardDetailService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckPlanService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckPlanService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckSettingServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardDetailServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckPlanServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckPlanServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/controller/EqFileManageController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/DeleteFileParam.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileDeleteDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageResDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageTreeDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/MoveFileParams.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileDeleteEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileManageEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/SysOssEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileDeleteExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileManageExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileDeleteMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileManageMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileDeleteService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileManageService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileDeleteServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileManageServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/EqScrapController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingBaseInfoController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingsController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqAttacmentDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqInfoDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqScrapDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqTypeNameAndIdDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingBaseInfoDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingsDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationReqDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqAttacmentEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqScrapEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingBaseInfoEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingsEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/ThingsRelationEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqAttacmentExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqInportExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqScrapExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingBaseInfoExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsPartExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqAttacmentMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqScrapMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqThingsRelationMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingBaseInfoMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingsDao.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/pojo/IotThingsPojo.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqAttacmentService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqScrapService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingBaseInfoService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingsService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/ThingsRelationService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqAttacmentServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqScrapServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqThingsRelationServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingBaseInfoServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingsServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/controller/EqPlanRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/dto/EqPlanRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/entity/EqPlanRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/excel/EqPlanRecordExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/mapper/EqPlanRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/EqPlanRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/impl/EqPlanRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/controller/EqPartRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/dto/EqPartRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/entity/EqPartRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/excel/EqPartRecordExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/mapper/EqPartRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/EqPartRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/impl/EqPartRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/controller/EqPlanRemindRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/dto/EqPlanRemindRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/entity/EqPlanRemindRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/excel/EqPlanRemindRecordExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/mapper/EqPlanRemindRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/EqPlanRemindRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/impl/EqPlanRemindRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/controller/EqUserGroupController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/dto/EqUserGroupDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/entity/EqUserGroupEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/excel/EqUserGroupExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/mapper/EqUserGroupMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/service/EqUserGroupService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/equsergroup/service/impl/EqUserGroupServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/feign/DictTypeServiceFeign.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/feign/FileServiceFeign.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/feign/IotThingsServiceFeign.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/feign/TreeService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/controller/FileController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/entiy/EqAttacmentEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/entiy/FileData.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/mapper/FileMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/service/FileService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/file/service/impl/FileServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/message/EmailMessage.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/controller/DeviceMonitorController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/controller/ReadingRecordController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/dto/DeviceMonitorDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordVO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/entity/DeviceMonitorEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/entity/ReadingRecordEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/mapper/DeviceMonitorDao.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/mapper/ReadingRecordMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/service/DeviceMonitorService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/service/ReadingRecordService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/DeviceMonitorServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/ReadingRecordServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyCheckController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyProjectController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyCheckDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyPeopleDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyProjectDTO.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyCheckEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyPeopleEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyProjectEntity.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyCheckExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyProjectExcel.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyCheckMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyPeopleMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyProjectMapper.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyCheckService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyPeopleService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyProjectService.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyCheckServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyPeopleServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyProjectServiceImpl.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/task/DeleteFileTask.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/task/EqRemindRecordTask.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/task/PlanTimeTask.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/tree/controller/DeviceController.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/utils/DateSplitUtils.java create mode 100644 modules/equipment/src/main/java/com/thing/eq/utils/SerialNumberUnit.java create mode 100644 modules/equipment/src/main/resources/mapper/checkresult/CheckResultMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqbxwx/EqBxMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqbxwx/EqWxMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqbxwx/EqWxPlanMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqbxwx/EqWxReplacementMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByDetailMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByPlanMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByPlanPartMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByTemplateDetailMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqby/EqByTemplateMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqCheckSettingMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardDetailMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckPlanMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckRecordMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckPlanMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckRecordMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileDeleteMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileManageMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqmanager/EqAttacmentMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqmanager/EqScrapMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqmanager/EqThingsRelationMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqmanager/IotThingBaseInfoMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqmanager/IotThingsMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqpalnrecord/EqPlanRecordMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqpartrecord/EqPartRecordMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/eqremindrecord/EqPlanRemindRecordMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/equsergroup/EqUserGroupMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/file/FileMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/monitor/DeviceMonitorMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/normailze/EnergyCheckMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/normailze/EnergyPeopleMapper.xml create mode 100644 modules/equipment/src/main/resources/mapper/normailze/EnergyProjectMapper.xml create mode 100644 modules/filter-rule/pom.xml create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/cache/FilterRuleCacheInitializer.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterLogController.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterRuleController.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterLogDTO.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDTO.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDetailDTO.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/dto/LogSourceInfoValue.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterLogEntity.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleDetailEntity.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleEntity.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/DataType.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/LogStatus.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/event/FilterSuccessEvent.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/listener/FilterLogSaveEventListener.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterLogMapper.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleDetailMapper.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleMapper.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterLogService.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleDetailService.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleService.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterLogServiceImpl.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleDetailServiceImpl.java create mode 100644 modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleServiceImpl.java create mode 100644 modules/filter-rule/src/main/resources/mapper/FilterLogMapper.xml create mode 100644 modules/fix/pom.xml create mode 100644 modules/fix/src/main/java/com/thing/fix/controller/FixRelationController.java create mode 100644 modules/fix/src/main/java/com/thing/fix/entity/RelationNode.java create mode 100644 modules/fix/src/main/java/com/thing/fix/mapper/BaseFixMapper.java create mode 100644 modules/fix/src/main/java/com/thing/fix/mapper/FixRelationMapper.java create mode 100644 modules/fix/src/main/java/com/thing/fix/service/BaseFixService.java create mode 100644 modules/fix/src/main/java/com/thing/fix/service/FixRelationService.java create mode 100644 modules/fix/src/main/java/com/thing/fix/service/impl/BaseFixServiceImpl.java create mode 100644 modules/fix/src/main/java/com/thing/fix/service/impl/FixRelationServiceImpl.java create mode 100644 modules/fix/src/main/resources/mapper/BaseFixMapper.xml create mode 100644 modules/fix/src/main/resources/mapper/FixRelationMapper.xml create mode 100644 modules/mock/pom.xml create mode 100644 modules/mock/src/main/java/com/thing/mock/controller/MockDataConfigController.java create mode 100644 modules/mock/src/main/java/com/thing/mock/controller/MockDataLogController.java create mode 100644 modules/mock/src/main/java/com/thing/mock/dto/MockDataConfigDTO.java create mode 100644 modules/mock/src/main/java/com/thing/mock/dto/MockDataLogDTO.java create mode 100644 modules/mock/src/main/java/com/thing/mock/dto/ReMockRequest.java create mode 100644 modules/mock/src/main/java/com/thing/mock/entity/MockDataConfigEntity.java create mode 100644 modules/mock/src/main/java/com/thing/mock/entity/MockDataLogEntity.java create mode 100644 modules/mock/src/main/java/com/thing/mock/excel/MockConfigExcel.java create mode 100644 modules/mock/src/main/java/com/thing/mock/handler/MockHandler.java create mode 100644 modules/mock/src/main/java/com/thing/mock/mapper/MockDataConfigMapper.java create mode 100644 modules/mock/src/main/java/com/thing/mock/mapper/MockDataLogMapper.java create mode 100644 modules/mock/src/main/java/com/thing/mock/service/MockDataConfigService.java create mode 100644 modules/mock/src/main/java/com/thing/mock/service/MockDataLogService.java create mode 100644 modules/mock/src/main/java/com/thing/mock/service/impl/MockDataConfigServiceImpl.java create mode 100644 modules/mock/src/main/java/com/thing/mock/service/impl/MockDataLogServiceImpl.java create mode 100644 modules/mock/src/main/java/com/thing/mock/task/MockDataTask.java create mode 100644 modules/msg/pom.xml create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/controller/MsgCacheController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/dto/MsgCacheDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/entity/MsgCacheEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/mapper/MsgCacheMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/service/MsgCacheService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/cache/service/impl/MsgCacheServiceImpl.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/controller/MsgHisController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/dto/MsgHisDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/entity/MsgHisEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/mapper/MsgHisMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/service/MsgHisService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/history/service/impl/MsgHisServiceImpl.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/controller/MsgSysParamsController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/dto/MsgSysParamsDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/entity/MsgParamsEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/mapper/MsgSysParamsMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/redis/MsgSysParamsRedis.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/service/MsgSysParamsService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/param/service/impl/MsgSysParamsServiceImpl.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/controller/MsgPushController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/CustomPushDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/DingTalkParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/MailAllParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/MailParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/MsgPushDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/SmsParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/WeChatComParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/WeChatParams.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/dto/WxParamObj.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/email/EmailConfig.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/email/MsgEmailUtils.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/entity/MsgPushEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/MsgPush.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/PushMsgFactory.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/impl/DingTalkInfoService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/impl/MailInfoService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/impl/SmsInfoService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatComInfoService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatInfoService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/init/WeChatTokenRunner.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/mapper/MsgPushMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/service/MsgPushService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/service/impl/MsgPushServiceImpl.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/AbstractSmsService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/AliyunSmsService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/QcloudSmsService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/QiniuSmsService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/SmsConfig.java create mode 100644 modules/msg/src/main/java/com/thing/msg/push/sms/SmsFactory.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/controller/MsgTemplateController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/dto/MsgTemplateDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/entity/MsgTemplateEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/mapper/MsgTemplateMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/service/MsgTemplateService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/template/service/impl/MsgTemplateServiceImpl.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/controller/MsgUserController.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/dto/MsgUserDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/dto/SysUserInfoDTO.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/entity/MsgUserEntity.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/excel/MsgUserExcel.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/mapper/MsgUserMapper.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/service/MsgUserService.java create mode 100644 modules/msg/src/main/java/com/thing/msg/userinfo/service/impl/MsgUserServiceImpl.java create mode 100644 modules/msg/src/main/resources/mapper/MsgHisMapper.xml create mode 100644 modules/msg/src/main/resources/mapper/MsgUserMapper.xml create mode 100644 modules/pom.xml create mode 100644 modules/publicorg/pom.xml create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/controller/PublicBenchmarkingProjectController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/PublicBenchmarkingProjectDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialInfoParamDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialParamDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/entity/PublicBenchmarkingProjectEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/excel/PublicBenchmarkingProjectExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/mapper/PublicBenchmarkingProjectMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/PublicBenchmarkingProjectService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/impl/PublicBenchmarkingProjectServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/controller/PublicEnergySavingReportController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicEnergySavingReportDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyTotalDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/entity/PublicEnergySavingReportEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/excel/PublicEnergySavingReportExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/mapper/PublicEnergySavingReportMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/PublicEnergySavingReportService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/impl/PublicEnergySavingReportServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/controller/PublicEnergySavingReportDetailController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/dto/PublicEnergySavingReportDetailDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/entity/PublicEnergySavingReportDetailEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/excel/PublicEnergySavingReportDetailExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/mapper/PublicEnergySavingReportDetailMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/PublicEnergySavingReportDetailService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/impl/PublicEnergySavingReportDetailServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/controller/PublicFinancialController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/dto/PublicFinancialDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/entity/PublicFinancialEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/mapper/PublicFinancialMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/service/PublicFinancialService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/financial/service/impl/PublicFinancialServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/controller/PublicIndexStandardController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/dto/PublicIndexStandardDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/entity/PublicIndexStandardEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/excel/PublicIndexStandardExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/mapper/PublicIndexStandardMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/PublicIndexStandardService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/impl/PublicIndexStandardServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/controller/PublicNoticeController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeIsViewDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/entity/PublicNoticeEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/excel/PublicNoticeExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/mapper/PublicNoticeMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/service/PublicNoticeService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/notice/service/impl/PublicNoticeServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/controller/PublicNoticeViewRecordController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/dto/PublicNoticeViewRecordDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/entity/PublicNoticeViewRecordEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/excel/PublicNoticeViewRecordExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/mapper/PublicNoticeViewRecordMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/PublicNoticeViewRecordService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/impl/PublicNoticeViewRecordServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/controller/PublicOrgController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgInfoDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/entity/PublicOrgEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/excel/PublicOrgExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/mapper/PublicOrgMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/service/PublicOrgService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/org/service/impl/PublicOrgServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/controller/PublicOrgDetailController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/dto/PublicOrgDetailDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/entity/PublicOrgDetailEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/excel/PublicOrgDetailExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/mapper/PublicOrgDetailMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/PublicOrgDetailService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/impl/PublicOrgDetailServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/controller/PublicPolicyAdvocacyController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/dto/PublicPolicyAdvocacyDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/entity/PublicPolicyAdvocacyEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/excel/PublicPolicyAdvocacyExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/mapper/PublicPolicyAdvocacyMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/PublicPolicyAdvocacyService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/impl/PublicPolicyAdvocacyServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/controller/PublicDiagnosisEnergySavingController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/dto/PublicDiagnosisEnergySavingDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/entity/PublicDiagnosisEnergySavingEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/mapper/PublicDiagnosisEnergySavingMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/PublicDiagnosisEnergySavingService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/impl/PublicDiagnosisEnergySavingServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/controller/PublicAlertNoticeController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/dto/PublicAlertNoticeDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/entity/PublicAlertNoticeEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/excel/PublicAlertNoticeExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/mapper/PublicAlertNoticeMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/PublicAlertNoticeService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/impl/PublicAlertNoticeServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicAppletGovernmentController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicBoardController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicChargingPileController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEachUnitKanbanController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEnergyWarningController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicMonitoringController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicPhotovoltaicController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicReportController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/BasePageData.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualConsumptionRatioDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualPowerDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAreaConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCapacityGenerationDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCentralizedOfficeAreaDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicChargingPileDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionAndCarbonDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicDataValueDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicElectricityUnitDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumption.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarning1Excel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningRequestDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicHourElectricityUsageTrendsDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicInstalledCapacityDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicLinkDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonitorDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyIntegratedEnergyUseStructureDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNodeDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNonCentralizedOfficeAreaDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataCenterConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataEnergyConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDetailInfoDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgEnergyConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgTotalConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountRatioDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgYearConsumptionCompareDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicPhotovoltaicGenerationDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicQuotaTypeDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicSubNodeRespDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalCarbonDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalConsumptionAnalyseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalDataDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeEnergyUseYearDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeUseDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitBuildConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitConsumptionDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitNamePile.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitOfTheYearDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitsDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicAppletGovernmentService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicBoardService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicChargingPileService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEachUnitKanbanService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEnergyWarningService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicMonitoringService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicPhotovoltaicService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicReportService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicAppletGovernmentServiceServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicBoardServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicChargingPileServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEachUnitKanbanServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEnergyWarningServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicMonitoringServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicPhotovoltaicServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicReportServiceImpl.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/controller/PublicOrgLimitController.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/dto/PublicOrgLimitDTO.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/entity/PublicOrgLimitEntity.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/excel/PublicOrgLimitExcel.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/mapper/PublicOrgLimitMapper.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/PublicOrgLimitService.java create mode 100644 modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/impl/PublicOrgLimitServiceImpl.java create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/benchmarkingproject/PublicBenchmarkingProjectMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/energysavingreport/PublicEnergySavingReportMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/energysavingreportdetail/PublicEnergySavingReportDetailMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/financial/PublicFinancialMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/indexstandard/PublicIndexStandardMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/notice/PublicNoticeMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/noticeviewrecord/PublicNoticeViewRecordMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/org/PublicOrgMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/orgdetail/PublicOrgDetailMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/policyadvocacy/PublicPolicyAdvocacyMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/publicDiagnosisEnergySaving/PublicDiagnosisEnergySavingMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/publicalertnotice/PublicAlertNoticeMapper.xml create mode 100644 modules/publicorg/src/main/resources/mapper/publicorg/publicorglimit/PublicOrgLimitMapper.xml create mode 100644 modules/quartz/pom.xml create mode 100644 modules/quartz/src/main/java/com/thing/quartz/calc/task/CalcTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/listener/PeakEventListener.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/controller/IotTaskController.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/dto/IotTaskDTO.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/dto/MonthHourMinObject.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/dto/TaskDictDTO.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/entity/IotTaskEntity.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/mapper/IotTaskMapper.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/service/IotTaskService.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/service/impl/IotTaskServiceImpl.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/task/CustomTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/task/DefaultTaskBeanManager.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBean.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBeanManager.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/task/task/TestTaskBean.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/config/ScheduleConfig.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobController.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobLogController.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobDTO.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobLogDTO.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobEntity.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobLogEntity.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/init/JobCommandLineRunner.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobLogMapper.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobMapper.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/AlarmJobService.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/CalculationJobService.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobLogService.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobService.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/AlarmJobServiceImpl.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/CalculationJobServiceImpl.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobLogServiceImpl.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobServiceImpl.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/AlarmTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/ITask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/PushMsgTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/RepeatPushTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/TestTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/ThingStatusTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/task/WeChatTokenRefreshTask.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleJob.java create mode 100644 modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleUtils.java create mode 100644 modules/quartz/src/main/resources/mapper/task/IotTaskMapper.xml create mode 100644 modules/quartz/src/main/resources/mapper/timetask/ScheduleJobLogMapper.xml create mode 100644 modules/quartz/src/main/resources/mapper/timetask/ScheduleJobMapper.xml create mode 100644 modules/report-analysis/pom.xml create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyDictRelationController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyPriceController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyVarietyController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonPeakConfigController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonTsKvController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/MeterReadController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/controller/TransformerBizConfigController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyDictRelationDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceReportReqDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyReqDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonPeakConfigDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonThingEnergyDictReqDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTimeLabelData.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTsKvDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadReqDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadRespDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/dto/TransformerBizConfigDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyDictRelationEntity.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceEntity.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceInfo.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyVarietyEntity.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonPeakConfigEntity.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonTsKvEntity.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonEnergyVarietyExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictVerticalExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/excel/MeterReadExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyDictRelationMapper.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyPriceMapper.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyVarietyMapper.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonPeakConfigMapper.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonTsKvMapper.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyDictRelationService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyPriceService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyVarietyService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonPeakConfigService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonTsKvService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/MeterReadService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/TransformerBizConfigService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyDictRelationServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyPriceServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyVarietyServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonPeakConfigServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonTsKvServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/MeterReadServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/TransformerBizConfigServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyEffReportController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyUsageController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisDataReqNewDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisThingAttrValueRespDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyAlarm.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadAgg.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRate.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRealTime.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoss.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyNightUsageAgg.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTimeLabelData.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTransformerEff.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageAgg.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageLabelData.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageReqDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageStatisticRequest.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageSummaryDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/PeakValleyDosageReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/ReportParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/TransformerAnalysisParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticVerticalExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/DataSelectService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyEffService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageCommonService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageFlowService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageOverViewService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/DataSelectServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyEffServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageCommonServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageFlowServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageOverViewServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/thread/EnergyUsageExecutor.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/EnergyBalanceController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/PeakValleyController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrPriceInfo.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostStatisticsReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyBalanceReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyAnalysisReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyExportParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceInfo.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceStatisticsInfo.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyReportReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingAttrDosageReq.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingParam.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageVerticalExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyPriceVerticalExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyDosageExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyExcel.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/AnalysisService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/EnergyBalanceService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/PeakValleyService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/AnalysisServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/EnergyBalanceServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/PeakValleyServiceImpl.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/controller/AppletController.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/AttibuteValueDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/DataResultDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/ThingInfoDTO.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/AppletService.java create mode 100644 modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/impl/AppletServiceImpl.java create mode 100644 modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyDictRelationMapper.xml create mode 100644 modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyPriceMapper.xml create mode 100644 modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyVarietyMapper.xml create mode 100644 modules/report-analysis/src/main/resources/mapper/config/CarbonPeakConfigMapper.xml create mode 100644 modules/report-analysis/src/main/resources/mapper/config/CarbonTsKvMapper.xml create mode 100644 modules/thing/pom.xml create mode 100644 modules/thing/src/main/java/com/thing/cache/controller/CacheTsKvController.java create mode 100644 modules/thing/src/main/java/com/thing/cache/dto/BatchTsKvModifyForm.java create mode 100644 modules/thing/src/main/java/com/thing/cache/dto/CacheTsKvDTO.java create mode 100644 modules/thing/src/main/java/com/thing/cache/dto/TsKvDeleteForm.java create mode 100644 modules/thing/src/main/java/com/thing/cache/dto/TsKvModifyForm.java create mode 100644 modules/thing/src/main/java/com/thing/cache/entity/CacheTsKvEntity.java create mode 100644 modules/thing/src/main/java/com/thing/cache/excel/CacheTsKvLastestExcel.java create mode 100644 modules/thing/src/main/java/com/thing/cache/excel/TsKvModifyFormExcel.java create mode 100644 modules/thing/src/main/java/com/thing/cache/service/TsKvHandleService.java create mode 100644 modules/thing/src/main/java/com/thing/cache/service/impl/TsKvHandleServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/config/CacheConfig.java create mode 100644 modules/thing/src/main/java/com/thing/config/CustomKeyGenerator.java create mode 100644 modules/thing/src/main/java/com/thing/config/RelationProperties.java create mode 100644 modules/thing/src/main/java/com/thing/config/RestTemplateConfiguration.java create mode 100644 modules/thing/src/main/java/com/thing/config/UnsignedLongSerializer.java create mode 100644 modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlController.java create mode 100644 modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlLogController.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/ControlParam.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/ControlSelected.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlDTO.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlJson.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogDTO.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogPage.java create mode 100644 modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlPage.java create mode 100644 modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlEntity.java create mode 100644 modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlLogEntity.java create mode 100644 modules/thing/src/main/java/com/thing/control/excel/IotDeviceControlLogExcel.java create mode 100644 modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlLogMapper.java create mode 100644 modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlMapper.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/ControlService.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/IotDeviceControlLogService.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/IotDeviceControlService.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/impl/ControlServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlLogServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardController.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardElementController.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardGroupController.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotAttrRspDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashBoardRspDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementAttrValueDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardGroupDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardSvgDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/dto/SortDTO.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardElementEntity.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardEntity.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardGroupEntity.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardSvgEntity.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardElementExcel.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardExcel.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardGroupExcel.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardElementMapper.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardGroupMapper.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardMapper.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardSvgMapper.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardElementService.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardGroupService.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardService.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardSvgService.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardElementServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardGroupServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardSvgServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/controller/AnalysisDataController.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataPageReqDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataReqDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataRespDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringDataReqDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringTimeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/dto/ThingEnergyDictData.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/excel/AnalysisDataExcel.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/service/AnalysisDataService.java create mode 100644 modules/thing/src/main/java/com/thing/device/analysisdata/service/impl/AnalysisDataServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/board/controller/BoardController.java create mode 100644 modules/thing/src/main/java/com/thing/device/board/dto/BoardTsKvDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/board/service/BoardService.java create mode 100644 modules/thing/src/main/java/com/thing/device/board/service/impl/BoardServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/controller/IotDeviceManagementController.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementParam.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementReq.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/dto/IotDeviceManagementDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/entity/IotDeviceManagementEntity.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/excel/IotDeviceManagementExcel.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/mapper/IotDeviceManagementMapper.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/service/IotDeviceManagementService.java create mode 100644 modules/thing/src/main/java/com/thing/device/deviceManagement/service/impl/IotDeviceManagementServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/controller/IotDataEntryController.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/dto/IotDataEntryDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/entity/IotDataEntryEntity.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryExcel.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryImportExcel.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/mapper/IotDataEntryMapper.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/service/IotDataEntryService.java create mode 100644 modules/thing/src/main/java/com/thing/device/entry/service/impl/IotDataEntryServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/controller/IotThingMenuConfigController.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/dto/IotThingMenuConfigDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/entity/IotThingMenuConfigEntity.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/mapper/IotThingMenuConfigMapper.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/service/IotThingMenuConfigService.java create mode 100644 modules/thing/src/main/java/com/thing/device/menu/service/impl/IotThingMenuConfigServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/controller/IotThingSourceController.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAddChildDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrExtendDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrRespDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceGetAttrDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceListDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourcePidDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceRelationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceReqDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/entity/IotThingSourceEntity.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/mapper/IotThingSourceMapper.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/param/IotThingSourceParamDTO.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/service/IotThingSourceService.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/service/impl/IotThingSourceServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/device/source/thread/ThingSourceExecutor.java create mode 100644 modules/thing/src/main/java/com/thing/event/TenantDetailSavedEvent.java create mode 100644 modules/thing/src/main/java/com/thing/event/ThingRelationDetailSaveEvent.java create mode 100644 modules/thing/src/main/java/com/thing/event/ThingRelationImportEvent.java create mode 100644 modules/thing/src/main/java/com/thing/extend/controller/TransportExtendCalculationController.java create mode 100644 modules/thing/src/main/java/com/thing/extend/controller/TransportExtendController.java create mode 100644 modules/thing/src/main/java/com/thing/extend/controller/TransportExtendMsgController.java create mode 100644 modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationImport.java create mode 100644 modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDTO.java create mode 100644 modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDictDTO.java create mode 100644 modules/thing/src/main/java/com/thing/extend/dto/TransportExtendMsgDTO.java create mode 100644 modules/thing/src/main/java/com/thing/extend/entity/TransportExtendCalculationEntity.java create mode 100644 modules/thing/src/main/java/com/thing/extend/entity/TransportExtendEntity.java create mode 100644 modules/thing/src/main/java/com/thing/extend/entity/TransportExtendMsgEntity.java create mode 100644 modules/thing/src/main/java/com/thing/extend/excel/TransportExtendMsgExcel.java create mode 100644 modules/thing/src/main/java/com/thing/extend/excel/TransportModbusExcel.java create mode 100644 modules/thing/src/main/java/com/thing/extend/excel/TransportMqttExcel.java create mode 100644 modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendCalculationMapper.java create mode 100644 modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMapper.java create mode 100644 modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMsgMapper.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/TransportExtendCalculationService.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/TransportExtendMsgService.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/TransportExtendService.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendCalculationServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendMsgServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/extension/DefaultExtensionManager.java create mode 100644 modules/thing/src/main/java/com/thing/extension/Extension.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionJsonConverter.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionManager.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionProvider.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionPublishMsg.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionRelation.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionType.java create mode 100644 modules/thing/src/main/java/com/thing/extension/ExtensionUtil.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusConnectOptions.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtension.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionProvider.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionRelation.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusFunction.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusTsKv.java create mode 100644 modules/thing/src/main/java/com/thing/extension/modbus/ModbusUtils.java create mode 100644 modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtension.java create mode 100644 modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionProvider.java create mode 100644 modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionRelation.java create mode 100644 modules/thing/src/main/java/com/thing/extension/mqtt/TopicMatcher.java create mode 100644 modules/thing/src/main/java/com/thing/listener/QueueDeviceEventListener.java create mode 100644 modules/thing/src/main/java/com/thing/screen/controller/IotEnterpriseDashboardController.java create mode 100644 modules/thing/src/main/java/com/thing/screen/dto/IotEnterpriseDashboardDTO.java create mode 100644 modules/thing/src/main/java/com/thing/screen/entity/IotEnterpriseDashboardEntity.java create mode 100644 modules/thing/src/main/java/com/thing/screen/mapper/IotEnterpriseDashboardMapper.java create mode 100644 modules/thing/src/main/java/com/thing/screen/service/IotEnterpriseDashboardService.java create mode 100644 modules/thing/src/main/java/com/thing/screen/service/impl/IotEnterpriseDashboardServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/screen/util/TenantSubsetUtilS.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/aspect/LogOperationAspect.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysDeptController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictDataController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictTypeController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysIndustryTypeController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysMenuController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysOnlineController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysParamsController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysPostController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysRegionController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysRoleController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserMenuController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/controller/SystemController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/BindDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/CtlUserDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/HomeNavReq.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/PasswordDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysDeptDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictDataDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeListDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysIndustryTypeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysMenuDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysParamsDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysPostDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysRegionDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysRoleDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuPageDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/dto/SystemDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/DictData.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/DictType.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysDeptEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictDataEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictTypeEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysIndustryTypeEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysLanguageEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysMenuEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysOnlineEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysParamsEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysPostEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysRegionEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleDataScopeEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleMenuEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleUserEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserMenuEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserPostEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserTokenEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/excel/SysParamsExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserMenuExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDeptMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictDataMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictTypeMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysIndustryTypeMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysLanguageMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysMenuMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysParamsMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysPostMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRegionMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleDataScopeMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMenuMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleUserMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMenuMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserPostMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserTokenMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/region/Region.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/region/RegionCity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/region/RegionProvince.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysDeptService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysDictDataService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysDictTypeService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysIndustryTypeService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysLanguageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysMenuService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysParamsService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysPostService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysRegionService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleDataScopeService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleMenuService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleUserService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysUserMenuService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysUserPostService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysUserService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/SysUserTokenService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDeptServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictDataServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictTypeServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysIndustryTypeServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysLanguageServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysMenuServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysParamsServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysPostServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRegionServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleDataScopeServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleMenuServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleUserServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserMenuServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserPostServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserTokenServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/biz/utils/HttpClientUtil.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/controller/SysLogErrorController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/controller/SysLogLoginController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/controller/SysLogOperationController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/dto/SysLogErrorDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/dto/SysLogLoginDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/dto/SysLogOperationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/entity/SysLogErrorEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/entity/SysLogLoginEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/entity/SysLogOperationEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginOperationEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginStatusEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/enumeration/OperationStatusEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/excel/SysLogErrorExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/excel/SysLogLoginExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/excel/SysLogOperationExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/handler/SysExceptionHandler.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogErrorMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogLoginMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogOperationMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/SysLogErrorService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/SysLogLoginService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/SysLogOperationService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogErrorServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogLoginServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogOperationServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/controller/MailLogController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/controller/MailTemplateController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/controller/SmsController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/controller/SysSmsLogController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/dto/SysMailLogDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/dto/SysMailTemplateDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsLogDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/email/EmailConfig.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/email/EmailUtils.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/entity/SysMailLogEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/entity/SysMailTemplateEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsLogEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailLogMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailTemplateMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsLogMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/SysMailLogService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/SysMailTemplateService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/SysSmsLogService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/SysSmsService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailLogServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailTemplateServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsLogServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/sms/AbstractSmsService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/sms/AliyunSmsService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/sms/QcloudSmsService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/sms/SmsConfig.java create mode 100644 modules/thing/src/main/java/com/thing/sys/message/sms/SmsFactory.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/controller/SysNoticeController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/dto/SysNoticeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeUserEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeReadStatusEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeStatusEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/enums/ReceiverTypeEnum.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeUserMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeUserService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeUserServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/AbstractCloudStorageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/AliyunCloudStorageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/CloudStorageConfig.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSCloudStorageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSImport.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/LocalCloudStorageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/MinioCloudStorageService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/cloud/OSSFactory.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/controller/SysOssController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/entity/SysOssEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/entity/UpLoadDao.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/mapper/SysOssMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/service/SysOssService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/oss/service/impl/SysOssServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/aspect/DataFilterAspect.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/context/TenantContext.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/context/UserContext.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/controller/LoginController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/customrealm/Oauth2Realm.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/customrealm/ShiroCustomConfig.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/domain/LoginDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/domain/SecurityUser.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthInfo.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthPrincipal.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/domain/UserDetail.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/service/ShiroService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/security/service/impl/ShiroServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantDetailController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantGroupController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantRoleController.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/RegionIndustryTenantInfoDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailMenuDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupForm.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupTreeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantUserDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/TenantDetailForm.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/dto/UpdateStatusDTO.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantDetailEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantGroupEntity.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/excel/SysTenantDetailExcel.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantDetailMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantGroupMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantMapper.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantDetailService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantGroupService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantRoleService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantService.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantDetailServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantGroupServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantRoleServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiLogController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityAttrDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityRelationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/ApiTimeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDebugDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiLogDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiLogEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/excel/IotThingApiExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiLogMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiLogService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiLogServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigItemDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigItemEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigItemMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigItemService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigItemServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/cache/service/CacheCallback.java create mode 100644 modules/thing/src/main/java/com/thing/thing/cache/service/CacheInit.java create mode 100644 modules/thing/src/main/java/com/thing/thing/cache/service/ThingCache.java create mode 100644 modules/thing/src/main/java/com/thing/thing/cache/service/ThingModelCacheService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/cache/service/impl/ThingModelCacheServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/common/dto/BasicsDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/context/controller/ThingManageContextController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/context/service/ThingManageContextService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/context/service/impl/ThingManageContextServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/controller/IotThingDictController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictRelationListDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/entity/IotThingDictEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/excel/IotThingDictExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/mapper/IotThingDictMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/service/IotThingDictService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dict/service/impl/IotThingDictServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/controller/IotThingDictRelationController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/dto/IotThingDictRelationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/entity/IotThingDictRelationEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/excel/IotThingDictRelationExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/mapper/IotThingDictRelationMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/param/IotThingDictRelationParamDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/service/IotThingDictRelationService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/dictRelation/service/impl/IotThingDictRelationServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/controller/IotThingEntityController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/ApiEntityReqDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/DataApiRspDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotBasicsThingEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDictDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityInfoDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewSourceDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrArrDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrsDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityParamDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntitySearchDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/entity/IotThingEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/excel/IotThingViewExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/mapper/IotThingEntityMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/param/DistributeEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/param/IotSyncEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/param/IotThingEntityParamDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/param/IotThingStatusDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/service/IotThingEntityService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/entity/service/impl/IotThingEntityServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/controller/CommonConfigController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/dto/CommonConfigDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/entity/CommonConfigEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/mapper/CommonConfigMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/service/CommonConfigService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/ext/service/impl/CommonConfigServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupInfoController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupRelationController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityListDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoFormatDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDelDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/dto/IotSortDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupInfoEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupRelationEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupInfoExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupRelationExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupInfoMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupRelationMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/service/IotGroupInfoService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/service/IotGroupRelationService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupInfoServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupRelationServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/controller/IotThingModelController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelExcel.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/dto/ModelDetailDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/entity/IotThingModelEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/mapper/IotThingModelMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/param/DistributeModelParam.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/service/IotThingModelService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/model/service/impl/IotThingModelServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/controller/IotThingRelationDetailController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/dto/IotThingRelationDetailDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/dto/RelationDetailBatchSaveDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingRelationDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingTreeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/entity/IotThingRelationDetailEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/mapper/IotThingRelationDetailMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/param/IotThingRelationDetailParamDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/service/IotThingRelationDetailService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/detail/service/impl/IotThingRelationDetailServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/controller/IotThingRelationRootController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootListDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/dto/ThingSortTreeDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/entity/IotThingRelationRootEntity.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/mapper/IotThingRelationRootMapper.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/service/IotThingRelationRootService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/relation/root/service/impl/IotThingRelationRootServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/controller/TsKvController.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttr.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttrDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/dto/TsKvDTO.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/service/TskvService.java create mode 100644 modules/thing/src/main/java/com/thing/thing/tskv/service/impl/TskvServiceImpl.java create mode 100644 modules/thing/src/main/java/com/thing/util/BeanUtil.java create mode 100644 modules/thing/src/main/java/com/thing/util/TenantSubsetUtil.java create mode 100644 modules/thing/src/main/java/com/thing/util/ToolUtil.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/ExtensionSocketEventListener.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/QueueSocketEventListener.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/WebSocketDashboardServer.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/WebSocketServer.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/config/WebSocketConfig.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/data/SocketDataCache.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/data/WebSocketDashboardData.java create mode 100644 modules/thing/src/main/java/com/thing/websocket/data/WebSocketData.java create mode 100644 modules/thing/src/main/resources/mapper/api/IotThingApiLogMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/api/IotThingApiMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigItemMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/control/IotDeviceControlLogMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/control/IotDeviceControlMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dashboard/IotDashboardElementMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dashboard/IotDashboardGroupMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dashboard/IotDashboardMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dashboard/IotDashboardSvgMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/device/IotThingMenuConfigMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/device/IotThingSourceMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dict/IotThingDictMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/dict/IotThingDictRelationMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/ext/CommonConfigMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/extend/TransportExtendCalculationMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/extend/TransportExtendMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/extend/TransportExtendMsgMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/group/IotGroupInfoMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/group/IotGroupRelationMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/model/IotThingEntityMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/model/IotThingModelMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/relation/IotThingRelationDetailMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/relation/IotThingRelationRootMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysDeptMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysDictDataMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysDictTypeMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysIndustryTypeMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysLanguageMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysMenuMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysParamsMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysPostMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysRegionMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysRoleDataScopeMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysRoleMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysRoleMenuMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysRoleUserMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysTenantDetailMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysTenantGroupMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysTenantMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysUserMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysUserMenuMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysUserPostMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/SysUserTokenMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/message/SysMailLogMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/message/SysMailTemplateMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/message/SysSmsLogMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/message/SysSmsMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/notice/SysNoticeMapper.xml create mode 100644 modules/thing/src/main/resources/mapper/sys/notice/SysNoticeUserMapper.xml diff --git a/common/cache/pom.xml b/common/cache/pom.xml new file mode 100644 index 0000000..81a6850 --- /dev/null +++ b/common/cache/pom.xml @@ -0,0 +1,67 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + + com.thing.common + cache + jar + ThingBI Server Common Cache + + + ${basedir}/.. + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + + + org.springframework.boot + spring-boot-starter-aop + + + + com.github.ben-manes.caffeine + caffeine + + + + org.springframework.boot + spring-boot-starter-data-redis + + + org.yaml + snakeyaml + + + + + + org.springframework + spring-context-support + + + org.springframework.boot + spring-boot-starter-aop + + + com.alibaba + fastjson + + + + org.projectlombok + lombok + + + \ No newline at end of file diff --git a/common/cache/src/main/java/com/thing/common/cache/config/CacheManagerProperties.java b/common/cache/src/main/java/com/thing/common/cache/config/CacheManagerProperties.java new file mode 100644 index 0000000..25d91b9 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/config/CacheManagerProperties.java @@ -0,0 +1,26 @@ +package com.thing.common.cache.config; + +import lombok.Data; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * @author siyang + * @date 2024-03-29 + * @description + */ +@Data +@Component +@ConfigurationProperties(prefix = "cache-manager") +public class CacheManagerProperties { + private Map specs; + + @Data + public static class CacheSpecs { + private Integer timeToLiveInMinutes; + private Integer maxSize; + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/config/LocalCacheConfig.java b/common/cache/src/main/java/com/thing/common/cache/config/LocalCacheConfig.java new file mode 100644 index 0000000..540f53e --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/config/LocalCacheConfig.java @@ -0,0 +1,49 @@ +package com.thing.common.cache.config; + +import com.github.benmanes.caffeine.cache.Caffeine; +import com.thing.common.cache.config.CacheManagerProperties.CacheSpecs; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * @author zhenghh. 2021-11-01 + */ +@Data +@Slf4j +@EnableCaching +@Configuration +@RequiredArgsConstructor +@ConditionalOnProperty(name = "spring.cache.type", havingValue = "caffeine", matchIfMissing = true) +public class LocalCacheConfig { + private final CacheManagerProperties cacheManagerProperties; + + /** 初始化缓存管理器 */ + @Bean + public CacheManager cacheManager() { + log.info("【caffeine】===初始化本地缓存管理器"); + Map specs = cacheManagerProperties.getSpecs(); + CaffeineCacheManager cacheManager = new CaffeineCacheManager(); + for (String name : specs.keySet()) { + Caffeine cacheBuilder = + Caffeine.newBuilder() + .maximumSize(specs.get(name).getMaxSize()) + .expireAfterWrite( + specs.get(name).getTimeToLiveInMinutes(), TimeUnit.MINUTES) + .recordStats(); + cacheManager.registerCustomCache(name, cacheBuilder.build()); + } + return cacheManager; + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/config/RedisConfig.java b/common/cache/src/main/java/com/thing/common/cache/config/RedisConfig.java new file mode 100644 index 0000000..a03ca95 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/config/RedisConfig.java @@ -0,0 +1,97 @@ +package com.thing.common.cache.config; + +import com.alibaba.fastjson.support.spring.GenericFastJsonRedisSerializer; +import com.thing.common.cache.config.CacheManagerProperties.CacheSpecs; + +import jakarta.annotation.Resource; + +import lombok.Data; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.*; + +import java.time.Duration; +import java.util.HashMap; +import java.util.Map; + +/** + * Redis配置 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Slf4j +@EnableCaching +@Configuration +@RequiredArgsConstructor +@ConditionalOnProperty(name = "spring.cache.type", havingValue = "redis", matchIfMissing = true) +public class RedisConfig { + @Resource private RedisConnectionFactory factory; + @Resource private CacheManagerProperties cacheManagerProperties; + + /** + * 为了方便缓存(caffeine和redis)的切换,不建议显示使用redisTemplate + * 需要用缓存的地方统一使用SpringCache注解,譬如@Cacheable + */ + @Bean + public RedisTemplate redisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(factory); + + // 设置序列化方式 + StringRedisSerializer keySerializer = new StringRedisSerializer(); + RedisSerializer valSerializer = new GenericFastJsonRedisSerializer(); + + redisTemplate.setKeySerializer(keySerializer); + redisTemplate.setValueSerializer(valSerializer); + + redisTemplate.setHashKeySerializer(keySerializer); + redisTemplate.setHashValueSerializer(valSerializer); + + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + /** + * 采用spring的cache注解时,会使用此处定义的cacheManager + * 如果在管理器中没有配置某个缓存,则会无限期存储,且不会使用json序列化 + */ + @Bean + public CacheManager cacheManager() { + log.info("【redis】===初始化redis缓存管理器"); + Map specs = cacheManagerProperties.getSpecs(); + RedisSerializer valSerializer = new GenericFastJsonRedisSerializer(); + + RedisCacheConfiguration defaultCacheConfig = + RedisCacheConfiguration.defaultCacheConfig() + .serializeKeysWith( + RedisSerializationContext.SerializationPair.fromSerializer( + new StringRedisSerializer())) + .serializeValuesWith( + RedisSerializationContext.SerializationPair.fromSerializer( + valSerializer)); + + // 创建各个缓存的配置,每个缓存可以有不同的过期时间 + Map cacheConfigurations = new HashMap<>(); + specs.forEach( + (cacheName, spec) -> + cacheConfigurations.put( + cacheName, + defaultCacheConfig.entryTtl( + Duration.ofMinutes(spec.getTimeToLiveInMinutes())))); + + return RedisCacheManager.builder(factory) + .withInitialCacheConfigurations(cacheConfigurations) + .build(); + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/constants/CacheNameEnum.java b/common/cache/src/main/java/com/thing/common/cache/constants/CacheNameEnum.java new file mode 100644 index 0000000..bb61cd3 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/constants/CacheNameEnum.java @@ -0,0 +1,160 @@ +package com.thing.common.cache.constants; + + +import lombok.Getter; + +/** + * @author mark. 2021-11-01 + */ +public class CacheNameEnum { + /** 物模型 */ + @Getter + public enum ModelField { + THING_MODEL_ID("id"), + THING_MODEL_CODE("code"), + THING_MODEL_TOKEN("token"), + THING_MODEL_GATEWAY("gateway"), + THING_MODEL_ORIGIN("origin"), + THING_MODEL_REAL_TYPE("realType"), + THING_MODEL_CREATE_DATE("create_date"), + THING_MODEL_STATUS("status"), + THING_MODEL_STATUS_TS("statusTs"); + private final String field; + ModelField(String field) { + this.field = field; + } + } + /** 物实体 */ + @Getter + public enum EntityField { + THING_ENTITY_ID("entityId"), + THING_ENTITY_CODE("entityCode"), + THING_ENTITY_NAME("entityName"), + THING_ENTITY_TENANT_CODE("tenantCode"), + THING_ENTITY_TYPE("entityType"), + THING_ENTITY_DEPT_IDS("deptIds"), + THING_ENTITY_STATUS("status"), + THING_ENTITY_DEPT_NAME("deptName"), + THING_ENTITY_REAL_TYPE("realType"), + THING_ENTITY_TAGS("tags"), + THING_ENTITY_CREATE_DATE("create_date"), + THING_ENTITY_ENABLE_STATUS("enableStatus"), + THING_ENTITY_TEMPLATE_MARK("templateMark"); + private final String field; + EntityField(String field) { + this.field = field; + } + } + + + @Getter + public enum DictField { + THING_DICT_ID("id"), + THING_DICT_ENTITY_NAME("entityName"), + THING_DICT_ENTITY_CODE("entityCode"), + THING_DICT_GROUP_NAME("groupName"), + THING_DICT_DATA_TYPE("dataType"), + THING_DICT_TEMPLATE_MARK("templateMark"), + THING_DICT_TENANT_CODE("tenantCode"), + THING_DICT_NAME("name"), + THING_DICT_CODE("code"), + THING_DICT_CREATE_DATE("create_date"), + THING_DICT_ENTITY_ID("entityId"), + THING_DICT_RELATION_SORT("sort"); + private final String field; + DictField(String field) { + this.field = field; + } + } + + @Getter + public enum RelationRootField { + THING_RELATION_ROOT_ID("id"), + THING_RELATION_ROOT_CHILDREN("children"), + THING_RELATION_ROOT_TENANT_CODE("tenantCode"), + THING_RELATION_ROOT_GROUP_NAME("groupName"), + THING_RELATION_ROOT_ENTITY_NAME("entityName"), + THING_RELATION_ROOT_ENTITY_TYPE("entityType"), + THING_RELATION_ROOT_NAME("name"), + THING_RELATION_ROOT_CREATE_DATE("create_date"), + THING_RELATION_ROOT_SORT("sort"); + + private final String field; + RelationRootField(String field) { + this.field = field; + } + + } + + @Getter + public enum RelationDetailField { + THING_RELATION_DETAIL_ID("id"), + THING_RELATION_DETAIL_ROOT_ID("rootId"), + THING_RELATION_DETAIL_FROM_ID("fromId"), + THING_RELATION_DETAIL_FROM_NAME("fromName"), + THING_RELATION_DETAIL_FROM_CODE("fromCode"), + THING_RELATION_DETAIL_ROOT_THING_ID("rootThingId"), + THING_RELATION_DETAIL_CHILDREN("children"), + THING_RELATION_DETAIL_TO_ID("toId"), + THING_RELATION_DETAIL_TO_NAME("toName"), + THING_RELATION_DETAIL_TO_CODE("toCode"), + THING_RELATION_DETAIL_CREATE_DATE("createDate"), + THING_RELATION_DETAIL_SORT("sort") + ; + private final String field; + RelationDetailField(String field) { + this.field = field; + } + } + + + /** 物计算项 */ + @Deprecated + public static final String CALCULATION_ATTRIBUTES = "calculationAttributes"; + /** 告警规则 */ + public static final String ALARM_RULE = "alarmRule"; + /** 告警物 */ + public static final String ALARM_RULE_ENTITY = "alarmRuleEntity"; + /** 告警动作 */ + public static final String ALARM_RULE_ACTION = "alarmRuleAction"; + /** 数据解析 */ + public static final String SCRIPT_INFO = "scriptInfo"; + + /** 承租人物编码 */ + public static final String AUTH_THING_CODES = "authThingCodes"; + + /** 物模型 */ + public static final String THING_MODEL = "thingModel"; + /** 物实体 */ + public static final String THING_ENTITY = "thingEntity"; + /** 物属性 */ + public final static String THING_DICT = "thingDict"; + /** 物指标 */ + public static final String THING_DICT_RELATION = "thingDictRelation"; + /** 物关系 */ + public static final String THING_ROOT_RELATION = "thingRootRelation"; + /** 物根关系 */ + public static final String THING_DETAIL_RELATION = "thingDetailRelation"; + + /** + * 新版本物计算配置 + */ + public final static String THING_CALC_CONFIG = "thingCalcConfig"; + + /** + * 过滤规则 + */ + public final static String FILTER_RULE = "filterRule"; + + /** + * 模拟数据配置 + */ + public final static String MOCK_DATA_CONFIG = "mockDataConfig"; + + /** + * 模拟数据某配置下的最新一笔数据的时间 + */ + public final static String MOCK_DATA_LATEST_TIME = "mockDataLatestTime"; + + +} diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidConfig.java b/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidConfig.java new file mode 100644 index 0000000..51f22c7 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidConfig.java @@ -0,0 +1,26 @@ +package com.thing.common.cache.redis; + +import com.thing.common.cache.config.RedisConfig; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.InitializingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +@ConditionalOnBean(RedisConfig.class) +public class LettuceConnectionValidConfig implements InitializingBean { + private final RedisConnectionFactory redisConnectionFactory; + + @Override + public void afterPropertiesSet() { + if (redisConnectionFactory instanceof LettuceConnectionFactory c) { + c.setValidateConnection(true); + } + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidTask.java b/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidTask.java new file mode 100644 index 0000000..07a0112 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/LettuceConnectionValidTask.java @@ -0,0 +1,26 @@ +package com.thing.common.cache.redis; + +import com.thing.common.cache.config.RedisConfig; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +@RequiredArgsConstructor +@ConditionalOnBean(RedisConfig.class) +public class LettuceConnectionValidTask { + private final RedisConnectionFactory redisConnectionFactory; + + @Scheduled(cron = "0/2 * * * * ?") + public void task() { + if (redisConnectionFactory instanceof LettuceConnectionFactory c) { + c.validateConnection(); + } + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/RedisKeys.java b/common/cache/src/main/java/com/thing/common/cache/redis/RedisKeys.java new file mode 100644 index 0000000..8a85ab8 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/RedisKeys.java @@ -0,0 +1,65 @@ + + +package com.thing.common.cache.redis; + +/** + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class RedisKeys { + /** + * 系统参数Key + */ + public static String getSysParamsKey(){ + return "sys:params"; + } + + /** + * 系统参数Key + */ + public static String getSysParamsKey(String paramCode){ + return "sys:params:" + paramCode; + } + + /** + * 验证码Key + */ + public static String getCaptchaKey(String uuid){ + return "sys:captcha:" + uuid; + } + + /** + * 登录用户Key + */ + public static String getSecurityUserKey(Long id){ + return "sys:security:user:" + id; + } + + /** + * 系统日志Key + */ + public static String getSysLogKey(){ + return "sys:log"; + } + + /** + * 系统资源Key + */ + public static String getSysResourceKey(){ + return "sys:resource"; + } + + /** + * 用户菜单导航Key + */ + public static String getUserMenuNavKey(Long userId){ + return "sys:user:nav:" + userId; + } + + /** + * 用户权限标识Key + */ + public static String getUserPermissionsKey(Long userId){ + return "sys:user:permissions:" + userId; + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/RedisUtils.java b/common/cache/src/main/java/com/thing/common/cache/redis/RedisUtils.java new file mode 100644 index 0000000..6869aaa --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/RedisUtils.java @@ -0,0 +1,123 @@ +package com.thing.common.cache.redis; + +import com.thing.common.cache.config.RedisConfig; +import lombok.RequiredArgsConstructor; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.data.redis.core.HashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + * Redis工具类 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +@RequiredArgsConstructor +@SuppressWarnings("unused") +@ConditionalOnBean(RedisConfig.class) +public class RedisUtils { + private final RedisTemplate redisTemplate; + + /** 默认过期时长为24小时,单位:秒 */ + public final static long DEFAULT_EXPIRE = 60 * 60 * 24L; + /** 过期时长为1小时,单位:秒 */ + public final static long HOUR_ONE_EXPIRE = (long) 60 * 60; + /** 过期时长为6小时,单位:秒 */ + public final static long HOUR_SIX_EXPIRE = 60 * 60 * 6L; + /** 不设置过期时长 */ + public final static long NOT_EXPIRE = -1L; + + public void set(String key, Object value, long expire){ + redisTemplate.opsForValue().set(key, value); + if(expire != NOT_EXPIRE){ + expire(key, expire); + } + } + + public void set(String key, Object value){ + set(key, value, DEFAULT_EXPIRE); + } + + public Object get(String key, long expire) { + Object value = redisTemplate.opsForValue().get(key); + if(expire != NOT_EXPIRE){ + expire(key, expire); + } + return value; + } + + public Object get(String key) { + return get(key, NOT_EXPIRE); + } + + public void delete(String key) { + redisTemplate.delete(key); + } + + public void delete(Collection keys) { + redisTemplate.delete(keys); + } + + public Object hGet(String key, String field) { + return redisTemplate.opsForHash().get(key, field); + } + + public Map hGetAll(String key){ + HashOperations hashOperations = redisTemplate.opsForHash(); + return hashOperations.entries(key); + } + + public void hMSet(String key, Map map){ + hMSet(key, map, DEFAULT_EXPIRE); + } + + public void hMSet(String key, Map map, long expire){ + redisTemplate.opsForHash().putAll(key, map); + + if(expire != NOT_EXPIRE){ + expire(key, expire); + } + } + + public void hSet(String key, String field, Object value) { + hSet(key, field, value, DEFAULT_EXPIRE); + } + + public void hSet(String key, String field, Object value, long expire) { + redisTemplate.opsForHash().put(key, field, value); + + if(expire != NOT_EXPIRE){ + expire(key, expire); + } + } + + public void expire(String key, long expire){ + redisTemplate.expire(key, expire, TimeUnit.SECONDS); + } + + public void hDel(String key, Object... fields){ + redisTemplate.opsForHash().delete(key, fields); + } + + public void leftPush(String key, Object value){ + leftPush(key, value, DEFAULT_EXPIRE); + } + + public void leftPush(String key, Object value, long expire){ + redisTemplate.opsForList().leftPush(key, value); + + if(expire != NOT_EXPIRE){ + expire(key, expire); + } + } + + public Object rightPop(String key){ + return redisTemplate.opsForList().rightPop(key); + } +} \ No newline at end of file diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/SysParamsRedis.java b/common/cache/src/main/java/com/thing/common/cache/redis/SysParamsRedis.java new file mode 100644 index 0000000..b50e765 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/SysParamsRedis.java @@ -0,0 +1,38 @@ +package com.thing.common.cache.redis; + +import com.thing.common.cache.config.RedisConfig; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Component; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Component +@RequiredArgsConstructor +@ConditionalOnBean(RedisConfig.class) +public class SysParamsRedis { + private final RedisUtils redisUtils; + + public void delete(Object[] paramCodes) { + String key = RedisKeys.getSysParamsKey(); + redisUtils.hDel(key, paramCodes); + } + + public void set(String paramCode, String paramValue){ + if(paramValue == null){ + return ; + } + String key = RedisKeys.getSysParamsKey(); + redisUtils.hSet(key, paramCode, paramValue); + } + + public String get(String paramCode){ + String key = RedisKeys.getSysParamsKey(); + return (String)redisUtils.hGet(key, paramCode); + } + +} \ No newline at end of file diff --git a/common/cache/src/main/java/com/thing/common/cache/redis/aspect/RedisAspect.java b/common/cache/src/main/java/com/thing/common/cache/redis/aspect/RedisAspect.java new file mode 100644 index 0000000..a3a1f78 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/redis/aspect/RedisAspect.java @@ -0,0 +1,39 @@ +package com.thing.common.cache.redis.aspect; + +import com.thing.common.cache.config.RedisConfig; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Component; + +/** + * Redis切面处理类 + * + * @author Mark sunlightcs@gmail.com + */ +@Aspect +@Component +@ConditionalOnBean(RedisConfig.class) +public class RedisAspect { + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Value("${spring.cache.type}") + private String cacheType; + + @Around("execution(* com.thing.common.cache.redis.RedisUtils.*(..))") + public Object around(ProceedingJoinPoint point) throws Throwable { + Object result = null; + if("redis".equals(cacheType)){ + try{ + result = point.proceed(); + }catch (Exception e){ + logger.error("redis error", e); + } + } + return result; + } +} diff --git a/common/cache/src/main/java/com/thing/common/cache/service/AbstractCacheService.java b/common/cache/src/main/java/com/thing/common/cache/service/AbstractCacheService.java new file mode 100644 index 0000000..a8079e0 --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/service/AbstractCacheService.java @@ -0,0 +1,17 @@ +package com.thing.common.cache.service; + +/** + * @author SiYang + * @date 2024/01/19 14:07 + * @description 缓存服务,用来初始化缓存 + * 1. 业务类数据初始化采用该初始化器 + * 2. 技术架构类依赖的初始化采用 CommandLineRunner + */ +public abstract class AbstractCacheService { + + /** 定义缓存名称 */ + public abstract String getCacheName(); + + /** 项目启动时初始化缓存 */ + public abstract void initCache(); +} diff --git a/common/cache/src/main/java/com/thing/common/cache/service/CacheInitializer.java b/common/cache/src/main/java/com/thing/common/cache/service/CacheInitializer.java new file mode 100644 index 0000000..af704aa --- /dev/null +++ b/common/cache/src/main/java/com/thing/common/cache/service/CacheInitializer.java @@ -0,0 +1,40 @@ +package com.thing.common.cache.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationListener; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +/** + * @author SiYang + * @date 2024/01/24 23:20 + * @description 缓存初始化器 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CacheInitializer implements ApplicationListener { + private final List cacheInitializers; + + /** 容器初始化时缓存预热 */ + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + // 确保事件来源是容器本身,避免处理容器中的其他刷新事件 + if (event.getSource() instanceof ConfigurableApplicationContext) { + if (CollectionUtils.isEmpty(cacheInitializers)) { + return; + } + // 初始化缓存 + cacheInitializers.forEach( + e -> { + log.info("初始化缓存: {}", e.getCacheName()); + e.initCache(); + }); + } + } +} diff --git a/common/core/pom.xml b/common/core/pom.xml new file mode 100644 index 0000000..3df9441 --- /dev/null +++ b/common/core/pom.xml @@ -0,0 +1,129 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + + com.thing.common + core + jar + ThingBI Server Common Core + + + ${basedir}/.. + + + + org.springframework.boot + spring-boot-starter-web + + + + + + org.springframework.boot + spring-boot-starter-validation + + + + + cn.hutool + hutool-all + + + com.fasterxml.jackson.core + jackson-databind + + + + com.alibaba + fastjson + + + + + com.google.guava + guava + + + + + org.yaml + snakeyaml + + + + commons-io + commons-io + + + + + org.apache.commons + commons-lang3 + + + + com.github.jsonzou + jmockdata + + + + cn.afterturn + easypoi-base + + + cn.afterturn + easypoi-web + + + cn.afterturn + easypoi-annotation + + + jakarta.validation + jakarta.validation-api + + + jakarta.annotation + jakarta.annotation-api + + + jakarta.servlet + jakarta.servlet-api + + + javax.annotation + javax.annotation-api + + + org.projectlombok + lombok + + + joda-time + joda-time + + + com.thing.common + data + + + org.bouncycastle + bcprov-jdk18on + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + + \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/annotation/CustomExcel.java b/common/core/src/main/java/com/thing/common/core/annotation/CustomExcel.java new file mode 100644 index 0000000..97e59d9 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/annotation/CustomExcel.java @@ -0,0 +1,75 @@ +package com.thing.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Author: SiYang + * Date: 2023/09/26 10:19 + * Description: + * easyPoi并没有给出动态表头的注解方案, + * 想要使用动态表头,只能自己构建 ExcelExportEntity 这个对象,比较繁琐 + * 我们可以使用自定义注解的方式,使得构建该对象的过程自动化 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomExcel { + + /** + * 表头唯一标识 + * 这里只是给出概念,通常使用 {@link #keyGenerator} 来定义 + */ + String key() default ""; + + /** + * 指定列名,有些列名是固定的,不需要生成 + */ + String name() default ""; + + /** + * 表头key生成器,不指定的话默认使用 {@link #columnNameGenerator} 的结果 + */ + String keyGenerator() default ""; + + /** + * 指定生成策略,生成策略指的是数据类内部自定义的方法 + * 被CustomExcel注解的属性可以定义多种生成策略 + * 该方法用来指定生成策略 + */ + String columnNameGenerator() default ""; + + /** + * 列的序号 + */ + int orderNum() default 0; + + /** + * 列序号生成器,需要自定义生成方法,这里的值为方法名称 + * 注:顺序的体现只需要orderNum有大小关系,而不一定要连续 + */ + String orderGenerator() default ""; + + /** + * 是否需要合并列 + * 该字段映射到easyPoi的ExcelExportEntity对象中似乎无效 + * easyPoi的合并功能还是一如既往地令人失望,所以合并还是得靠自己实现 + */ + boolean needMerge() default false; + + /** + * 标题分组 + */ + String groupName() default ""; + + /** + * 指定生成title的方法 + * 该方法如果返回的值相同,那么就表示这些列的标题需要合并 + */ + String groupGenerator() default ""; + + int width() default 10; + + int height() default 10; +} diff --git a/common/core/src/main/java/com/thing/common/core/annotation/CustomExcelCollection.java b/common/core/src/main/java/com/thing/common/core/annotation/CustomExcelCollection.java new file mode 100644 index 0000000..b46a3ec --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/annotation/CustomExcelCollection.java @@ -0,0 +1,17 @@ +package com.thing.common.core.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Author: SiYang + * Date: 2023/09/26 14:49 + * Description: 处理内嵌list的注解 + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomExcelCollection { + +} diff --git a/common/core/src/main/java/com/thing/common/core/annotation/LogOperation.java b/common/core/src/main/java/com/thing/common/core/annotation/LogOperation.java new file mode 100644 index 0000000..bc0c231 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/annotation/LogOperation.java @@ -0,0 +1,21 @@ + + +package com.thing.common.core.annotation; + +import java.lang.annotation.*; + +/** + * 操作日志注解 + * + * @author Mark sunlightcs@gmail.com + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogOperation { + + String value() default ""; + + String type() default ""; + +} diff --git a/common/core/src/main/java/com/thing/common/core/config/AsyncEventConfig.java b/common/core/src/main/java/com/thing/common/core/config/AsyncEventConfig.java new file mode 100644 index 0000000..1d88d04 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/config/AsyncEventConfig.java @@ -0,0 +1,41 @@ +package com.thing.common.core.config; + +import org.springframework.beans.factory.BeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.SimpleApplicationEventMulticaster; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.concurrent.ThreadPoolExecutor; + +/** + * Author: SiYang + * Date: 2023/12/01 14:31 + * Description: 异步事件配置 + */ +@Configuration +public class AsyncEventConfig { + + @Bean + public SimpleApplicationEventMulticaster applicationEventMulticaster(BeanFactory beanFactory) { + SimpleApplicationEventMulticaster applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory); + // 设置线程池, 不设置的话默认没有线程池,没有线程池就不会异步调度事件 + applicationEventMulticaster.setTaskExecutor(eventExecutor()); + return applicationEventMulticaster; + } + + @Bean + public TaskExecutor eventExecutor() { + ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + threadPoolTaskExecutor.setCorePoolSize(20); + threadPoolTaskExecutor.setMaxPoolSize(50); + threadPoolTaskExecutor.setQueueCapacity(30); + threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + threadPoolTaskExecutor.setThreadNamePrefix("eventExecutor-"); + threadPoolTaskExecutor.setWaitForTasksToCompleteOnShutdown(true); + threadPoolTaskExecutor.setAwaitTerminationSeconds(5); + threadPoolTaskExecutor.initialize(); + return threadPoolTaskExecutor; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/config/RestTemplateConfig.java b/common/core/src/main/java/com/thing/common/core/config/RestTemplateConfig.java new file mode 100644 index 0000000..2ee5e91 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/config/RestTemplateConfig.java @@ -0,0 +1,20 @@ +package com.thing.common.core.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * @author zy + * @version 1.0 + * @date 2022/1/7 0007 14:43:22 + */ +@Configuration +public class RestTemplateConfig { + + @Bean + RestTemplate restTemplate(){ + return new RestTemplate(); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/constants/Constant.java b/common/core/src/main/java/com/thing/common/core/constants/Constant.java new file mode 100644 index 0000000..72a1458 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/constants/Constant.java @@ -0,0 +1,429 @@ + + +package com.thing.common.core.constants; + +import com.google.common.collect.ImmutableMap; +import com.thing.common.core.utils.DateTimeUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * 常量 + * + * @author Mark sunlightcs@gmail.com + */ +public interface Constant { + String API_BASE = "api_v1"; + /** + * 成功 + */ + int SUCCESS = 1; + /** + * 失败 + */ + int FAIL = 0; + /** + * OK + */ + String OK = "OK"; + /** + * 用户标识 + */ + String USER_KEY = "userId"; + + String AUTH = "auth"; + /** + * 数据集主键 + */ + String DATASET_ID = "datasetId"; + + String API_ID = "apiId"; + /** + * 扩展计算主键 + */ + String TRANSPORT_EXTEND_ID = "transportExtendId"; + /** + * 用户标识 + */ + String DEVICE_KEY = "deviceId"; + /** + * 看板标识 + */ + String DASHBOARD_KEY = "dashboardId"; + /** + * 菜单根节点标识 + */ + Long MENU_ROOT = 0L; + /** + * 部门根节点标识 + */ + Long DEPT_ROOT = 0L; + /** + * 数据字典根节点标识 + */ + Long DICT_ROOT = 0L; + /** + * 部门根节点标识 + */ + Integer DEPT_SORT_ROOT = 0; + /** + * 告警字典根节点标识 + */ + Long ALARM_DICT_TOOT = 0L; + /** + * 升序 + */ + String ASC = "asc"; + /** + * 降序 + */ + String DESC = "desc"; + /** + * 创建时间字段名 + */ + String CREATE_DATE = "create_date"; + + /** + * 创建时间字段名 + */ + String ID = "id"; + + /** + * 数据权限过滤 + */ + String SQL_FILTER = "sqlFilter"; + + /** + * 当前页码 + */ + String PAGE = "page"; + /** + * 每页显示记录数 + */ + String LIMIT = "limit"; + String BEGIN_TIME = "beginTime"; + String END_TIME = "endTime"; + /** + * 排序字段 + */ + String ORDER_FIELD = "orderField"; + /** + * 排序方式 + */ + String ORDER = "order"; + /** + * token header + */ + String TOKEN_HEADER = "token"; + + /** + * 云存储配置KEY + */ + String CLOUD_STORAGE_CONFIG_KEY = "CLOUD_STORAGE_CONFIG_KEY"; + /** + * 短信配置KEY + */ + String SMS_CONFIG_KEY = "SMS_CONFIG_KEY"; + /** + * 邮件配置KEY + */ + String MAIL_CONFIG_KEY = "MAIL_CONFIG_KEY"; + /** + * 微信配置KEY + */ + String WECHAT_CONFIG_KEY = "WECHAT_CONFIG_KEY"; + /** + * 钉钉配置KEY + */ + String DINGTALK_CONFIG_KEY = "DINGTALK_CONFIG_KEY"; + /** + * 企业微信配置KEY + */ + String WECHAT_COM_CONFIG_KEY = "WECHAT_COM_CONFIG_KEY"; + /** + * 代码生成参数KEY + */ + String DEV_TOOLS_PARAM_KEY = "DEV_TOOLS_PARAM_KEY"; + + /** + * 租户编码 + */ + String TENANT_CODE = "tenantCode"; + + + /** + * 取SQL结果集的第一条 + */ + String SQL_FIRST = "limit 1"; + + /** + * tb中默认的设备类型 + */ + String DEFAULT_DEVICE_TYPE = "default"; + + /** + * 默认数据源 + */ + String DEFAULT_SOURCE = "defaultSource"; + + /** + * 物编码 + */ + String THING_CODE = "thingCode"; + + /** + * 物编码 + */ + String ENTITY_ID = "entityId"; + + /** + * tb中additionalInfo字段的覆盖活动时间 + */ + String TB_OVERWRITE_ACTIVITY_TIME = "overwriteActivityTime"; + + + /** + * tb中additionalInfo字段的描述 + */ + String TB_DESCRIPTION = "description"; + + /** + * tb中additionalInfo字段的描述 + */ + String TB_GATEWAY = "gateway"; + + /** + * 公司id + */ + String COMPANY_ID = "companyId"; + + String PROTOCOL_SEPARATOR = "://"; + + String COLON = ":"; + + /** + * 获取token + */ + String API_GET_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"; + /** + * 获取用户列表 + */ + String API_GET_USERLIST = "https://api.weixin.qq.com/cgi-bin/user/get"; + /** + * 获取用户信息 + */ + String API_GET_USERINFO = "https://api.weixin.qq.com/cgi-bin/user/info"; + /** + * 发送模板消息 + */ + String API_SEND_MSG = "https://api.weixin.qq.com/cgi-bin/message/template/send"; + /** + * 获取分组用户 + */ + String API_GET_GROUPLIST = "https://api.weixin.qq.com/cgi-bin/tags/get"; + /** + * 获取模板列表 + */ + String API_GET_TEMPLATELIST = "https://api.weixin.qq.com/cgi-bin/template/get_all_private_template"; + + + String PUBLISHER_TYPE_SMS = "短信服务_platform:"; + String PUBLISHER_TYPE_WECHAT = "微信公众号服务"; + String PUBLISHER_TYPE_DINGTALK = "钉钉机器人服务"; + String PUBLISHER_TYPE_WECHATCOM = "企业微信机器人服务"; + String PUBLISHER_TYPE_ALARM = "告警触发推送服务"; + + /** + * 企业物的初始分配数量 + */ + Long INITIAL_AUTH_NUM = 1L; + + /** + * 设备在线离线tb共享属性key + */ + String THING_ONLINE_STATUS = "thing_online_status"; + + /** + * 计算结果时间 + */ + String RESULT_TIME = "time"; + /** + * 计算结果值 + */ + String RESULT_VALUE = "value"; + + /** + * 最新时间 + */ + String LAST_TIME = "lastTime"; + /** + * 最新结果值 + */ + String LAST_VALUE = "lastValue"; + + /** + * 公司的ID + */ + String CONSUMER_PID = "consumerPid"; + //集中办公区 + String CENTRALIZED_OFFICE_AREA = "centralized_office_area"; + //独立办公区的党政机关 + String INDEPENDENT_OFFICE_AREA = "independent_office_area"; + //中小学公共机构 + String PRIMARY_SECONDARY_SCHOOLS = "primary_secondary_schools"; + //高等教育机构 + String HIGHER_EDUCATION = "higher_education"; + //医疗卫生机构 + String MEDICAL_HYGIENE = "medical_hygiene"; + //场馆类公共机构 + String VENUE_CATEGORY = "venue_category"; + + //三级医院 + String TERTIARY_HOSPITAL = "tertiary_hospital"; + //二级医院 + String SECONDARY_HOSPITAL = "secondary_Hospital"; + + //演出类-剧院、音乐厅等 + String PERFORMANCE_VENUES = "performance_venues"; + //非演出类-博物馆、纪念馆、文化馆、美术馆、科技馆等 + String NON_PERFORMANCE_VENUES_ONE = "non_performance_venues_one"; + //非演出类-图书馆、档案馆等 + String NON_PERFORMANCE_VENUES_TWO = "non_performance_venues_two"; + //承办赛事类-体育场等 + String UNDERTAKING_EVENTS = "undertaking_events"; + //非承办赛事类-体育中心、训练中心等 + String NON_UNDERTAKING_EVENTS = "non_undertaking_events"; + + //>90000 + String BUILD_A = "build_a"; + //45001~90000 + String BUILD_B = "build_b"; + //≤45000 + String BUILD_C = "build_c"; + //>25000 + String BUILD_D = "build_d"; + //11001~25000 + String BUILD_E = "build_e"; + //≤11000 + String BUILD_F = "build_f"; + //≥5000 + String BUILD_G = "build_g"; + //<5000 + String BUILD_H = "build_h"; + //---------/ + String BUILD_I = "build_i"; + //>6300 + String NUMBER_A = "number_a"; + //3000~6300 + String NUMBER_B = "number_b"; + //≤3000 + String NUMBER_C = "number_c"; + //>1700 + String NUMBER_D = "number_d"; + //600~1700 + String NUMBER_E = "number_e"; + //≤600 + String NUMBER_F = "number_f"; + //----/ + String NUMBER_G = "number_g"; + + //单位建筑面积能耗 + String ENERGY_PERBULID ="energy_perbulid"; + //人均建筑能耗 + String ENERGY_PERPERSON = "energy_perperson"; + //食堂人均能耗 + String ENERGY_PERCAFETERIA_PERSON ="energy_percafeteria_person"; + //数据中心电能使用率 + String ENERGY_UTILIZATION_EFFICIENCY = "energy_utilization_efficiency"; + + String ATTR_GROUP = "ATTR_GROUP"; + + Integer INDEX_0=0; + + Integer INDEX_1=1; + + Integer INDEX_2=2; + + Integer INDEX_3=3; + + + Integer COUNT_0=0; + Integer COUNT_1=1; + Integer COUNT_2=2; + + Integer LIST_LENGTH=2; + + Long DEFAULT_TAG_GROUP = 1567752318059458562L; + + Map EMUNTIMEMAP = ImmutableMap.builder() + .put("1", (long) (30*60*1000)) + .put("2", (long)(60*60*1000)) + .put("3", (long)(5*60*60*1000)) + .put("4", (long)(12*60*60*1000)) + .put("5", (long)(24*60*60*1000)) + .put("6", DateTimeUtils.firstDayOfWeekMilli()) + .put("7", (long)(7*24*60*60*1000)) + .put("8", (30L*24*60*60*1000)) + .put("9", (3*30L*24*60*60*1000)) + .put("10",(6*30L*24*60*60*1000)) + .put("11",(365L*24*60*60*1000)) + .build(); + + + + Map EMUNSTRTIMEMAP = ImmutableMap.builder() + .put("0","0,0,30") + .put("1","0,1,0") + .put("2","0,5,0") + .put("3","0,12,0") + .put("4","0,24,0") + .put("5","7,0,0") + .put("6","30,0,0") + .put("7","90,0,0") + .build(); + + Map EMUNSTRINGTIMEMAP = ImmutableMap.builder() + .put("30分钟","{\"day\":\"0\",\"hour\":\"0\",\"minute\":\"30\"}") + .put("1小时","{\"day\":\"0\",\"hour\":\"1\",\"minute\":\"0\"}") + .put("5小时","{\"day\":\"0\",\"hour\":\"5\",\"minute\":\"0\"}") + .put("12小时","{\"day\":\"0\",\"hour\":\"12\",\"minute\":\"0\"}") + .put("1天","{\"day\":\"1\",\"hour\":\"0\",\"minute\":\"0\"}") + .put("7天","{\"day\":\"7\",\"hour\":\"0\",\"minute\":\"0\"}") + .put("30天","{\"day\":\"30\",\"hour\":\"0\",\"minute\":\"0\"}") + .put("3月","{\"day\":\"90\",\"hour\":\"0\",\"minute\":\"0\"}") + .put("6月","{\"day\":\"180\",\"hour\":\"0\",\"minute\":\"0\"}") + .put("1年","{\"day\":\"365\",\"hour\":\"0\",\"minute\":\"0\"}") + .build(); + + + Map STATUS_MAP = ImmutableMap.builder() + .put("0","离网") + .put("1","空闲") + .put("3","占用(充电中)") + .put("2", "占用(未充电)") + .put("4", "占用(预约锁定)") + .put("255", "故障").build(); + + + Map CARTYPES = ImmutableMap.builder() + .put(1, "公共") + .put(50, "个人") + .put(100, "公交") + .put(101, "环卫") + .put(102, "物流") + .put(103, "出租车") + .put(255, "其他").build(); + + + + + Long ONE_FIVE_MINUTE = 300000L; + Long ONE_HOUR = 3600000L; + Long ONE_DAY = 86400000L; + Long FIVE_DAY = 2592000000L; + Long THREE_DAY = 2592000000L; + Long ONE_YEAR = 2592000000L; + + +} diff --git a/common/core/src/main/java/com/thing/common/core/container/GlobalContainer.java b/common/core/src/main/java/com/thing/common/core/container/GlobalContainer.java new file mode 100644 index 0000000..e70a092 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/container/GlobalContainer.java @@ -0,0 +1,39 @@ +package com.thing.common.core.container; + +import cn.hutool.core.util.ObjectUtil; + +import java.util.concurrent.ConcurrentHashMap; + +public class GlobalContainer { + + private ConcurrentHashMap container = new ConcurrentHashMap<>(); + + private static GlobalContainer instance; + + private GlobalContainer(){} + + public static synchronized GlobalContainer getInstance() { + + if(ObjectUtil.isNull(instance)) { + instance = new GlobalContainer(); + } + return instance; + + } + + + public void put(String key, Object object){ + container.put(key, object); + } + + public Object get(String key){ + return container.get(key); + } + + public Object remove(String key){ return container.remove(key); } + + @Override + public String toString() { + return super.toString(); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/convert/DateConverter.java b/common/core/src/main/java/com/thing/common/core/convert/DateConverter.java new file mode 100644 index 0000000..a56977f --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/convert/DateConverter.java @@ -0,0 +1,73 @@ + + +package com.thing.common.core.convert; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 日期转换 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +public class DateConverter implements Converter { + private static final Logger logger = LoggerFactory.getLogger(DateConverter.class); + private static List formatList = new ArrayList<>(5); + static { + formatList.add("yyyy-MM"); + formatList.add("yyyy-MM-dd"); + formatList.add("yyyy-MM-dd hh:mm"); + formatList.add("yyyy-MM-dd hh:mm:ss"); + formatList.add("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + } + + @Override + public Date convert(String source) { + String value = source.trim(); + if (StringUtils.isEmpty(value)) { + return null; + } + + if(source.matches("^\\d{4}-\\d{1,2}$")){ + return parseDate(source, formatList.get(0)); + }else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}$")){ + return parseDate(source, formatList.get(1)); + }else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}$")){ + return parseDate(source, formatList.get(2)); + }else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2} {1}\\d{1,2}:\\d{1,2}:\\d{1,2}$")){ + return parseDate(source, formatList.get(3)); + }else if(source.matches("^\\d{4}-\\d{1,2}-\\d{1,2}.*T.*\\d{1,2}:\\d{1,2}:\\d{1,2}.*..*$")){ + return parseDate(source, formatList.get(4)); + } else { + throw new IllegalArgumentException("Invalid boolean value '" + source + "'"); + } + } + + /** + * 格式化日期 + * @param dateStr String 字符型日期 + * @param format String 格式 + * @return Date 日期 + */ + public Date parseDate(String dateStr, String format) { + Date date = null; + try { + DateFormat dateFormat = new SimpleDateFormat(format); + date = dateFormat.parse(dateStr); + } catch (Exception e) { + logger.error("Formatted date with date: {} and format : {} ", dateStr, format); + } + return date; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ActionType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ActionType.java new file mode 100644 index 0000000..44adc0a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ActionType.java @@ -0,0 +1,30 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 动作类型 + */ +@Getter +@AllArgsConstructor +public enum ActionType { + /** + * 消息 + */ + MSG("0"), + /** + * 控制 + */ + CONTROL("1"), + /** + * 日志新增 + */ + LOG_SAVE("99"), + /** + * 日志修改 + */ + LOG_UPDATE("100"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AggType.java b/common/core/src/main/java/com/thing/common/core/enumeration/AggType.java new file mode 100644 index 0000000..808dd7c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AggType.java @@ -0,0 +1,36 @@ +package com.thing.common.core.enumeration; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +public enum AggType { + AVG, SUM, MAX, MIN, COUNT; + + /** + * 得到方法名称 + * @param prefix 前缀 + * @param suffix 后缀 + * @return 方法名称 + */ + public static String getMethodName(String prefix,String suffix ,AggType agg) { + if(Arrays.stream(AggType.values()).noneMatch(s -> StringUtils.equals(s.name(), agg.name()))){ + throw new IllegalArgumentException("Unsupported agg type: " + agg); + } + return prefix+agg+suffix; + } + + public static AggType match(String func){ + if(StringUtils.isBlank(func)){ + return AggType.MAX; + } + for (AggType value : AggType.values()) { + if(StringUtils.endsWithIgnoreCase(value.name(),func)){ + return value; + } + } + return AggType.MAX; + } + + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AlarmSettingType.java b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmSettingType.java new file mode 100644 index 0000000..762244e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmSettingType.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 告警选择 + */ +@Getter +@AllArgsConstructor +public enum AlarmSettingType { + /** + * 严重程度 + */ + LEVEL("alarm_level"), + /** + * 告警类型 + */ + TYPE("alarm_type"), + /** + * 告警名称 + */ + NAME("alarm_name"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AlarmStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmStatus.java new file mode 100644 index 0000000..d40763c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmStatus.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 告警状态 + */ +@Getter +@AllArgsConstructor +public enum AlarmStatus { + /** + * 待处理 + */ + PENDING("0"), + /** + * 处理中 + */ + PROCESSING("1"), + /** + * 已处理 + */ + PROCESSED("2"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AlarmType.java b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmType.java new file mode 100644 index 0000000..23ccda7 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AlarmType.java @@ -0,0 +1,35 @@ +package com.thing.common.core.enumeration; + +import com.thing.common.core.exception.SysException; + +import java.util.Arrays; +import java.util.Objects; + +public enum AlarmType { + /** + * 峰平谷 + */ + PEAK("1"), + /** + * 夜间 + */ + NIGHT("2"); + + private String value; + + AlarmType(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public static AlarmType match(String val) { + return Arrays.stream(AlarmType.values()) + .filter(item -> Objects.equals(item.getValue(), val)) + .findFirst() + .orElseThrow(() -> new SysException("type类型不正确:" + val)); + } + + } \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ApiFuncEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/ApiFuncEnum.java new file mode 100644 index 0000000..14545e6 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ApiFuncEnum.java @@ -0,0 +1,61 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * API查询类型 + */ +@Getter +@AllArgsConstructor +public enum ApiFuncEnum { + /** + * 求和 + */ + SUM("sum", "求和"), + /** + * 平均 + */ + AVG("avg", "平均数"), + /** + * 最大值 + */ + MAX("max", "最大值"), + /** + * 最小值 + */ + MIN("min", "最小值"), + + /** + * 统计 + */ + COUNT("count", "统计"), + /** + * 空 + */ + BLANK("blank", "空"); + + private final String value; + + private final String name; + + + public static String getName(String value) { + if (StringUtils.isBlank(value)) { + return value; + } + for (ApiFuncEnum apiFuncEnum : ApiFuncEnum.values()) { + if (StringUtils.equalsAnyIgnoreCase(apiFuncEnum.getValue(), value)) { + return apiFuncEnum.getName(); + } + } + return value; + } + + public static boolean noneMatch(String type) { + return Arrays.stream(ApiFuncEnum.values()).noneMatch(s -> StringUtils.equals(s.getValue(), type)); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ApiSeparateAttr.java b/common/core/src/main/java/com/thing/common/core/enumeration/ApiSeparateAttr.java new file mode 100644 index 0000000..e57c10e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ApiSeparateAttr.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集设备分属性 + */ +@Getter +@AllArgsConstructor +public enum ApiSeparateAttr { + /** + * 无属性 + */ + NONE("none") , + /** + * 分属性 + */ + SEPARATE("split"), + /** + * 不分属性 + */ + NO_SEPARATE("all"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ApiType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ApiType.java new file mode 100644 index 0000000..78a819d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ApiType.java @@ -0,0 +1,46 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * API类型 + */ +@Getter +@AllArgsConstructor +public enum ApiType { + /** + * 列表 + */ + LIST("list"), + /** + * 分 + */ + TYPE("type"), + /** + * 秒 + */ + TAG("tag"), + /** + * 搜索 + */ + SEARCH("search"), + /** + * 关系 + */ + RELATION("relation"), + /** + * 组 + */ + GROUP("group"); + + private final String value; + + public static boolean matchName(String name) { + return Arrays.stream(ApiType.values()) + .anyMatch(type -> StringUtils.equals(type.getValue(), StringUtils.trim(name))); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypeEnum.java new file mode 100644 index 0000000..e65f934 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypeEnum.java @@ -0,0 +1,61 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * API查询类型 + */ +@Getter +@AllArgsConstructor +public enum ApiTypeEnum { + /** + * 列表 + */ + LIST("list", "列表"), + /** + * 类型 + */ + TYPE("type", "类型"), + /** + * 标签 + */ + TAG("tag", "标签"), + /** + * 搜索 + */ + SEARCH("search", "搜索"), + + /** + * 关系 + */ + RELATION("relation", "关系"), + + /** + * 组 + */ + GROUP("group", "物组"); + + private final String value; + + private final String name; + + public static String getName(String value) { + if (StringUtils.isBlank(value)) { + return value; + } + for (ApiTypeEnum groupTypeEnum : ApiTypeEnum.values()) { + if (StringUtils.equalsAnyIgnoreCase(groupTypeEnum.getValue(), value)) { + return groupTypeEnum.getName(); + } + } + return value; + } + + public static boolean noneMatch(String type) { + return Arrays.stream(ApiTypeEnum.values()).noneMatch(s -> StringUtils.equals(s.getValue(), type)); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypes.java b/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypes.java new file mode 100644 index 0000000..a151222 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ApiTypes.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集是否开启 + */ +@Getter +@AllArgsConstructor +public enum ApiTypes { + /** + * 组态设计 + */ + CONFIG("0"), + /** + * 超级API + */ + API("1"); + + private final String type; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AttributeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/AttributeEnum.java new file mode 100644 index 0000000..9960a8e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AttributeEnum.java @@ -0,0 +1,99 @@ +package com.thing.common.core.enumeration; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 首页物属性 + */ +public enum AttributeEnum { + /** + * 气电比 + */ + AC100, + /** + * 单位m³气体价格 + */ + AC101, + /** + * 压力 + */ + D6, + /** + * 露点温度 + */ + D5, + /** + * 瞬时流量 + */ + D1, + /** + * 累计流量、用气量 + */ + D2, + /** + * 瞬时功率 + */ + A16, + /** + * 日均流量 + */ + AC106, + /** + * 日均功率 + */ + AC108, + /** + * 累计电量、用电量 + */ + A29, + /** + * 碳排量 + */ + CO2, + /** + * C相电压 + */ + A3, + /** + * A相电压 + */ + A1, + /** + * B相电压 + */ + A2, + /** + * C相电流 + */ + A9, + /** + * A相电流 + */ + A8, + /** + * B相电流 + */ + A10, + /** + * 频率 + */ + A25, + /** + * 密度 + */ + AC120, + /** + * 手工录入温度 + */ + AC121, + /** + * 手工录入节约用电量 + */ + AC122; + + public static List getAllEnums() { + return Arrays.stream(AttributeEnum.values()).map(AttributeEnum::name).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/AttributeTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/AttributeTypeEnum.java new file mode 100644 index 0000000..4bbce4c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/AttributeTypeEnum.java @@ -0,0 +1,225 @@ +package com.thing.common.core.enumeration; + +import com.thing.common.core.utils.DateTimeUtils; +import org.apache.commons.lang3.StringUtils; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public enum AttributeTypeEnum { + /** 刻 */ + am, + /** 小时 */ + hh, + /** 天 */ + dd, + /** 月 */ + mm, + /** 周 */ + week, + /** 年 */ + yy; + + public static AttributeTypeEnum match(String attributeType) { + return Arrays.stream(AttributeTypeEnum.values()) + .filter(item -> Objects.equals(item.name(), attributeType)) + .findFirst() + .orElse(null); + } + + public static String matchEndAdapter(String attribute,String attributeType) { + Optional optional = Arrays.stream(AttributeTypeEnum.values()) + .filter(item -> StringUtils.endsWithIgnoreCase(attribute, item.name())) + .findFirst(); + if(optional.isEmpty() || StringUtils.isBlank(attributeType)){ + return attribute; + } + return attribute.replaceAll(optional.get().name(), "")+attributeType; + } + + + /** + * 获取给定时间所在的被指定范围类型的时间区间 + * + * @param time 给定的时间 + * @return + * 举例1: + * 给定时间2023-09-11 12:00:00 以及时间范围类型 mm, + * 返回 2023-01-01 00:00:00 ~ 2023-12-01 00:00:00这段时间 + * 举例2: + * 给定时间2023-09-11 12:00:00 以及时间范围类型 dd, + * 返回 2023-09-01 00:00:00 ~ 2023-09-30 00:00:00这段时间 + */ + public List getTimeRange(LocalDateTime time) { + time = time.withHour(0).withMinute(0).withSecond(0).withNano(0); + switch (this) { + case am: + LocalDateTime beginTimeOfMin = LocalDateTime.of(time.toLocalDate(), LocalTime.MIN); + LocalDateTime endTimeOfMin = LocalDateTime.of(time.toLocalDate(), LocalTime.MAX); + return Stream.iterate(beginTimeOfMin, d -> d.plusMinutes(15)) + .limit((ChronoUnit.MINUTES.between(beginTimeOfMin, endTimeOfMin) + 1) / 15) + .collect(Collectors.toList()); + case hh: + LocalDateTime beginTimeOfHour = LocalDateTime.of(time.toLocalDate(), LocalTime.MIN); + LocalDateTime endTimeOfHour = LocalDateTime.of(time.toLocalDate(), LocalTime.MAX); + return Stream.iterate(beginTimeOfHour, d -> d.plusHours(1)) + .limit(ChronoUnit.HOURS.between(beginTimeOfHour, endTimeOfHour) + 1) + .collect(Collectors.toList()); + case dd: + LocalDateTime beginTimeOfMouth = time.with(TemporalAdjusters.firstDayOfMonth()); + LocalDateTime endTimeOfMouth = time.with(TemporalAdjusters.lastDayOfMonth()); + return Stream.iterate(beginTimeOfMouth, d -> d.plusDays(1)) + .limit(ChronoUnit.DAYS.between(beginTimeOfMouth, endTimeOfMouth) + 1) + .collect(Collectors.toList()); + case mm: + LocalDateTime beginTimeOfYear = time.with(TemporalAdjusters.firstDayOfYear()); + LocalDateTime endTimeOfYear = time.with(TemporalAdjusters.lastDayOfYear()); + return Stream.iterate(beginTimeOfYear, d -> d.plusMonths(1)) + .limit(ChronoUnit.MONTHS.between(beginTimeOfYear, endTimeOfYear) + 1) + .collect(Collectors.toList()); + case yy: + return Collections.singletonList(time.with(TemporalAdjusters.firstDayOfYear())); + default: + return Collections.emptyList(); + } + } + + + /** + * 获取给定时间所在的被指定范围类型的时间区间 + * + */ + public static List getTimeRange(LocalDateTime startTime,LocalDateTime endTime) { + startTime = startTime.withHour(0).withMinute(0).withSecond(0).withNano(0); + endTime = endTime.withHour(0).withMinute(0).withSecond(0).withNano(0); + return Stream.iterate(startTime, d -> d.plusDays(1)) + .limit(ChronoUnit.DAYS.between(startTime, endTime) + 1) + .collect(Collectors.toList()); + } + + /** + * 获取上一个阶段的时间范围: + * 1. 若当前时间类型是am或者hh,则上一个阶段的时间就是昨天的am或hh + * 2. 若当前时间类型是dd,则上一个阶段的时间就是上个月的dd + * 3. 若当前时间类型是mm, 则上一个阶段的时间就是去年的mm + * + * @param time 当前时间 + * @return 上一个阶段的时间范围 + */ + public List getPreviousTimeRange(LocalDateTime time) { + switch (this) { + case am: + case hh: + return getTimeRange(time.minusDays(1)); + case dd: + return getTimeRange(time.minusMonths(1)); + case mm: + return getTimeRange(time.minusYears(1)); + default: + return Collections.emptyList(); + } + } + + public List getTimeRangeWithPattern(LocalDateTime time, DateTimeFormatter pattern) { + return getTimeRange(time).stream().map(t -> t.format(pattern)).collect(Collectors.toList()); + } + + public List getTimeRangeWithPattern(LocalDateTime time) { + return getTimeRangeWithPattern(time, DateTimeUtils.DATE_TIME_PATTERN); + } + + public List getTimestampList(LocalDateTime time) { + List timeRange = getTimeRange(time); + return timeRange.stream().map(DateTimeUtils::parse).collect(Collectors.toList()); + } + + public List getPreviousTimestampList(LocalDateTime time) { + List timeRange = getPreviousTimeRange(time); + return timeRange.stream().map(DateTimeUtils::parse).collect(Collectors.toList()); + } + + @SuppressWarnings("Duplicates") + public AttributeTypeEnum next() { + return switch (this) { + case am -> hh; + case hh -> dd; + case dd -> mm; + case mm -> yy; + default -> null; + }; + } + + @SuppressWarnings("Duplicates") + public AttributeTypeEnum previous() { + return switch (this) { + case yy -> mm; + case mm -> dd; + case dd -> hh; + case hh -> am; + default -> null; + }; + } + + /** + * 获取下一级时间 + * 若当前是dd的2023-09-12 12:00:00 则下一级时间为mm的2023-09-01 00:00:00 + * 若当前是mm的2023-09-12 12:00:00 则下一级时间为yy的2023-01-01 00:00:00 + */ + public LocalDateTime nextAttributeTypeTime(LocalDateTime time) { + time = time.withHour(0).withMinute(0).withSecond(0).withNano(0); + return switch (this) { + case dd -> time.with(TemporalAdjusters.firstDayOfMonth()); + case mm -> time.with(TemporalAdjusters.firstDayOfYear()); + default -> null; + }; + } + + /** + * 获取当前时间的标准格式 + * 若当前是dd的2023-09-12 12:00:00 则标准格式为dd的2023-09-12 00:00:00 + * 若当前是mm的2023-09-12 12:00:00 则标准格式为dd的2023-09-01 00:00:00 + * 若当前是yy的2023-09-12 12:00:00 则标准格式为dd的2023-01-01 00:00:00 + */ + public LocalDateTime convertStandardTime(LocalDateTime time) { + time = time.withHour(0).withMinute(0).withSecond(0).withNano(0); + return switch (this) { + case dd -> time; + case mm -> time.with(TemporalAdjusters.firstDayOfMonth()); + case yy -> time.with(TemporalAdjusters.firstDayOfYear()); + default -> null; + }; + } + + public String nextAttributeTypeTime(String timeStr) { + LocalDateTime time = DateTimeUtils.parseDateTime(timeStr); + return Objects.requireNonNull(nextAttributeTypeTime(time)) + .format(DateTimeUtils.DATE_TIME_PATTERN); + } + + public String convertStandardTime(String timeStr) { + LocalDateTime time = DateTimeUtils.parseDateTime(timeStr); + return Objects.requireNonNull(convertStandardTime(time)) + .format(DateTimeUtils.DATE_TIME_PATTERN); + } + + public boolean isCurrentType(String attributeType) { + return Objects.equals(this.name(), attributeType); + } + + public static String removeSuffix(String attrCode) { + if (attrCode.endsWith("am") + || attrCode.endsWith("hh") + || attrCode.endsWith("dd") + || attrCode.endsWith("mm") + || attrCode.endsWith("yy")) { + return attrCode.substring(0, attrCode.length() - 2); + } + return attrCode; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CacheEventType.java b/common/core/src/main/java/com/thing/common/core/enumeration/CacheEventType.java new file mode 100644 index 0000000..8d4a3ac --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CacheEventType.java @@ -0,0 +1,16 @@ +package com.thing.common.core.enumeration; + +public enum CacheEventType { + /** + * 计算缓存 + */ + CALCULATION_CACHE, + /** + * 告警缓存 + */ + ALARM_CACHE, + /** + * 告警动作缓存 + */ + ALARM_ACTION_CACHE +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CalculationStatusEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/CalculationStatusEnum.java new file mode 100644 index 0000000..e9349ff --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CalculationStatusEnum.java @@ -0,0 +1,35 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 计算数据类型 + */ +@Getter +@AllArgsConstructor +public enum CalculationStatusEnum { + /** + * 数据缺失 + */ + MISS("0"), + /** + * 默认值 + */ + DEFAULT("1"), + /** + * 计算失败 + */ + ERROR("2"), + /** + * 计算成功 + */ + SUCCESS("3"), + /** + * 推送tb失败 + */ + FAIL("4"); + + private final String value; + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CalculationSymbol.java b/common/core/src/main/java/com/thing/common/core/enumeration/CalculationSymbol.java new file mode 100644 index 0000000..97d73ff --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CalculationSymbol.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.Getter; + +/** 计算符号 */ +public enum CalculationSymbol { + /** 大于 */ + greater(">"), + /** 小于 */ + less("<"), + /** 大于等于 */ + greaterEqual(">="), + /** 小于等于 */ + lessEqual("<="); + + @Getter + private final String name; + + CalculationSymbol(String name) { + this.name = name; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CarbonLifecycleEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/CarbonLifecycleEnum.java new file mode 100644 index 0000000..88666e3 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CarbonLifecycleEnum.java @@ -0,0 +1,35 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/29 14:46 + * @description 默认碳足迹生命周期枚举 + */ +@Getter +@AllArgsConstructor +public enum CarbonLifecycleEnum { + MATERIAL_PURCHASE_TRANSPORT("原材料获取", 1), + PRODUCT_MANUFACTURE("生产制造", 2), + PRODUCT_TRANSPORT("运输", 3), + PRODUCT_USAGE("产品使用", 4), + DISPOSE("废弃处理", 5); + + /** 描述 */ + private final String desc; + + /** 生命周期阶段 */ + private final Integer stage; + + public static CarbonLifecycleEnum match(Integer stage) { + return Arrays.stream(CarbonLifecycleEnum.values()) + .filter(item -> Objects.equals(item.getStage(), stage)) + .findFirst() + .orElse(null); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CloudService.java b/common/core/src/main/java/com/thing/common/core/enumeration/CloudService.java new file mode 100644 index 0000000..ec2bb31 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CloudService.java @@ -0,0 +1,49 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 云服务商 + */ +@Getter +@AllArgsConstructor +public enum CloudService { + /** + * 七牛云 + */ + QINIU(1), + /** + * 阿里云 + */ + ALIYUN(2), + /** + * 腾讯云 + */ + QCLOUD(3), + /** + * FASTDFS + */ + FASTDFS(4), + /** + * 本地 + */ + LOCAL(5), + /** + * MinIO + */ + MINIO(6); + + private final int value; + + public static CloudService match(int value) { + return Arrays.stream(CloudService.values()) + .filter(item -> Objects.equals(item.getValue(), value)) + .findFirst() + .orElse(null); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ColumnRelationType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ColumnRelationType.java new file mode 100644 index 0000000..45f0127 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ColumnRelationType.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 字段关联类型 + */ +@Getter +@AllArgsConstructor +public enum ColumnRelationType { + /** + * 映射 + */ + MAPPING("0"), + /** + * 相等 + */ + EQUAL("1"); + + private final String type; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/CompanyType.java b/common/core/src/main/java/com/thing/common/core/enumeration/CompanyType.java new file mode 100644 index 0000000..cba4256 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/CompanyType.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 企业类型 + */ +@Getter +@AllArgsConstructor +public enum CompanyType { + /** + * 企业角色 + */ + COMPANY("0"), + /** + * 用户角色 + */ + TENANT("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ConfigTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/ConfigTypeEnum.java new file mode 100644 index 0000000..a752b90 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ConfigTypeEnum.java @@ -0,0 +1,68 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 数据处理类型 + */ +@Getter +@AllArgsConstructor +public enum ConfigTypeEnum { + /** + * 监控 + */ + MONITORING("1"), + /** + * 用量 + */ + USAGE("2"), + + /** + * 数据报表 + */ + DATA_REPORT("3"), + + /** + * 设备管理 + */ + DEVICE_MANAGEMENT("4"), + + /** + * 用能统计报表 + */ + ENERGY("5"), + + /** + * 碳排统计报表 + */ + CARBON("6"), + + /** + * 碳排放监控 + */ + CARBON_EMISSIONS("11"), + + /** + * 企业数据维护 + */ + CORPORATE_DATA("12"), + + /** + * 碳排分析设置 + */ + CARBON_ANALYSIS("13"); + + private final String value; + + public static ConfigTypeEnum match(String configType) { + return Arrays.stream(ConfigTypeEnum.values()) + .filter(item -> Objects.equals(item.getValue(), configType)) + .findFirst() + .orElse(null); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ContainsCondition.java b/common/core/src/main/java/com/thing/common/core/enumeration/ContainsCondition.java new file mode 100644 index 0000000..ff82fdd --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ContainsCondition.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 树检索是否包含本身节点的情况 + */ +@Getter +@AllArgsConstructor +public enum ContainsCondition { + /** + * 包含 + */ + CONTAIN("0"), + /** + * 不包含 + */ + REMOVE("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DataBaseType.java b/common/core/src/main/java/com/thing/common/core/enumeration/DataBaseType.java new file mode 100644 index 0000000..18f44a1 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DataBaseType.java @@ -0,0 +1,28 @@ +package com.thing.common.core.enumeration; + +import java.util.Objects; + +public enum DataBaseType { + + MYSQL,POSTGRES,CLICKHOUSE,TIDB; + + /** + * 匹配 :统计 函数 + * @param dataBaseType + * @param agg + * @return + */ + public static String statisticalAggFun(DataBaseType dataBaseType, AggType agg) { + if(Objects.requireNonNull(agg) == AggType.COUNT){ + return agg+ "(val)"; + } + if (Objects.requireNonNull(dataBaseType) == DataBaseType.MYSQL || Objects.requireNonNull(dataBaseType) == DataBaseType.TIDB || Objects.requireNonNull(dataBaseType) == DataBaseType.POSTGRES) { + return "ROUND(" +agg+ "(CAST(val AS DECIMAL)), 2)"; + } + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + return "ROUND(" +agg+ "(CAST(val AS Float64)), 2)"; + } + throw new IllegalArgumentException("Unsupported database type: " + dataBaseType); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DataTreatingMarkEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/DataTreatingMarkEnum.java new file mode 100644 index 0000000..0c13674 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DataTreatingMarkEnum.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据处理类型 + */ +@Getter +@AllArgsConstructor +public enum DataTreatingMarkEnum { + /** + * 处理 + */ + DISPOSE("1"), + /** + * 未处理 + */ + UNTREATED("0"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DatasetAttrTimestampType.java b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetAttrTimestampType.java new file mode 100644 index 0000000..da29e00 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetAttrTimestampType.java @@ -0,0 +1,55 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集物关系上下级状态 + */ +@Getter +@AllArgsConstructor +public enum DatasetAttrTimestampType { + /** + * 无值 + */ + NONE("none") , + /** + * 最近一天 + */ + LASTESTDAY("1"), + /** + * 最近一周 + */ + LASTESTWEEK("2"), + /** + * 最近一月 + */ + LASTESTMONTH("3"), + /** + * 最近一年 + */ + LASTESTYEAR("4"), + /** + * 任意时间 + */ + ANYTIME("5"), + + /** + * 最新值 + */ + LASTEST("last"), + /** + * 最近一天 老项目在两个地方定义了”最近一天“这个枚举, 这里做个兼容吧 + */ + NEAREST("nearest"), + /** + * 区间 + */ + RANGE("range"), + /** + * 时间段 + */ + INTERVAL("interval"); + + private final String type; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DatasetInterValType.java b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetInterValType.java new file mode 100644 index 0000000..f32032b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetInterValType.java @@ -0,0 +1,31 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集状态 + */ +@Getter +@AllArgsConstructor +public enum DatasetInterValType { + /** + * 最新值 + */ + DAY("day"), + /** + * 最近一天 + */ + WEEK("week"), + /** + * 最近一周 + */ + MONTH("month"), + + /** + * 时间段 + */ + YEAR("year"); + + private final String type; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DatasetSeparateAttr.java b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetSeparateAttr.java new file mode 100644 index 0000000..1f85bc4 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetSeparateAttr.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集设备分属性 + */ +@Getter +@AllArgsConstructor +public enum DatasetSeparateAttr { + /** + * 分属性 + */ + SEPARATE("0"), + /** + * 不分属性 + */ + NO_SEPARATE("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DatasetStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetStatus.java new file mode 100644 index 0000000..12ecbd7 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetStatus.java @@ -0,0 +1,24 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集是否开启 + */ +@Getter +@AllArgsConstructor +public enum DatasetStatus { + /** + * 开启 + */ + START("1"), + /** + * 关闭 + */ + STOP("0"); + + private final String status; +} + + diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DatasetThingRelationStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetThingRelationStatus.java new file mode 100644 index 0000000..3fc0e12 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DatasetThingRelationStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集物关系上下级状态 + */ +@Getter +@AllArgsConstructor +public enum DatasetThingRelationStatus { + /** + * 向上级 + */ + PU, + + /** + * 向下级 + */ + PD; + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DefaultType.java b/common/core/src/main/java/com/thing/common/core/enumeration/DefaultType.java new file mode 100644 index 0000000..1abddc7 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DefaultType.java @@ -0,0 +1,39 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum DefaultType { + /** + * 物类型 + */ + THING("默认物类型"), + /** + * 组类型 + */ + GROUP("默认组类型"), + /** + * 字典类型 + */ + DICT("默认字典类型"), + + /** + * 关系类型 + */ + RELATION("默认关系类型"), + + /** + * 设备类型 + */ + EQUIPMENT("设备类型"), + + /** + * 备件类型 + */ + EQUIPMENT_PART("备件类型"); + + + private final String value; +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DeleteEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/DeleteEnum.java new file mode 100644 index 0000000..0dcf5b5 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DeleteEnum.java @@ -0,0 +1,27 @@ +package com.thing.common.core.enumeration; + +/** + * 删除标记枚举 + * + * @author Mark sunlightcs@gmail.com + */ +public enum DeleteEnum { + /** + * 是(删除) + */ + YES(1), + /** + * 否(未删除) + */ + NO(0); + + private int value; + + DeleteEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DeviceSendCmdStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/DeviceSendCmdStatus.java new file mode 100644 index 0000000..d942279 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DeviceSendCmdStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备指令下发状态 + */ +@Getter +@AllArgsConstructor +public enum DeviceSendCmdStatus { + /** + * 失败 + */ + FAILURE("0"), + /** + * 成功 + */ + SUCCESS("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DeviceTemplateMark.java b/common/core/src/main/java/com/thing/common/core/enumeration/DeviceTemplateMark.java new file mode 100644 index 0000000..558a398 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DeviceTemplateMark.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备是否是模板 + */ +@Getter +@AllArgsConstructor +public enum DeviceTemplateMark { + /** + * 模板 + */ + TEMPLATE("0"), + /** + * 设备 + */ + DEVICE("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DictRelationEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/DictRelationEnum.java new file mode 100644 index 0000000..634eb44 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DictRelationEnum.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 物关系类型 + */ +@Getter +@AllArgsConstructor +public enum DictRelationEnum { + /** + * 物 + */ + FROM_TYPE_THING("thing"), + /** + * 模板 + */ + FROM_TYPE_TEMPLATE("template"), + /** + * 关系 + */ + FROM_TYPE_RELATION("relation"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/DictTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/DictTypeEnum.java new file mode 100644 index 0000000..154aea6 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/DictTypeEnum.java @@ -0,0 +1,60 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +/** + * 物关系类型 + */ +@Getter +@AllArgsConstructor +public enum DictTypeEnum { + + /** + * 默认 + */ + DEFAULTTYPE("默认","default",1), + /** + * 瞬时 + */ + INSTANT("瞬时","instant",2), + /** + * 递增 + */ + INCREASE("递增","increase",3), + /** + * 区间 + */ + REGION("区间","region",4), + /** + * 控制 + */ + CONTROL("控制","control",5) , + /** + * 告警 + */ + ALARM("告警","alarm",6); + + + private final String name; + private final String type; + private final Integer sort; + + public static String match(String type) { + Optional first = Arrays.stream(DictTypeEnum.values()) + .filter(dictTypeEnum -> StringUtils.equals(type, dictTypeEnum.getType())) + .findFirst(); + if(first.isPresent()){ + return first.get().getName(); + } + return ""; + } + + + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/EnergyVarietyEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/EnergyVarietyEnum.java new file mode 100644 index 0000000..df0aeac --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/EnergyVarietyEnum.java @@ -0,0 +1,23 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum EnergyVarietyEnum { + A("A", "电"), + C("C", "天然气"); + private final String prefix; + private final String name; + + public static EnergyVarietyEnum getByPrefix(String prefix) { + return Arrays.stream(EnergyVarietyEnum.values()) + .filter(item -> Objects.equals(item.prefix, prefix)) + .findFirst() + .orElse(null); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ExtendRelationType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ExtendRelationType.java new file mode 100644 index 0000000..0bfed6b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ExtendRelationType.java @@ -0,0 +1,41 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; + +/** + * 类型 + */ +@Getter +@AllArgsConstructor +public enum ExtendRelationType { + /** + * 读、写、读写 + */ + READ("读"), + WRITE("写") + /*, + ALL("读写")*/; + + private final String name; + + public static List getRead() { + return List.of(READ.name()/*, ALL.name()*/); + } + + public static List getWrite() { + return List.of(WRITE.name()/*, ALL.name()*/); + } + + public static String getByName(String name) { + return Arrays.stream(ExtendRelationType.values()) + .filter(type -> StringUtils.equals(type.getName(), StringUtils.trim(name))) + .findFirst() + .orElseThrow(() -> new RuntimeException(name + "未匹配")) + .name(); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/FormulaSymbols.java b/common/core/src/main/java/com/thing/common/core/enumeration/FormulaSymbols.java new file mode 100644 index 0000000..ad60f90 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/FormulaSymbols.java @@ -0,0 +1,31 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据集状态 + */ +@Getter +@AllArgsConstructor +public enum FormulaSymbols { + /** + * 加号 + */ + PLUS("+"), + /** + * 减号 + */ + MINUS("-"), + /** + * 乘号 + */ + MULTIPLE("*"), + + /** + * 除号 + */ + DIVISION("/"); + + private final String symbols; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/GateWayStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/GateWayStatus.java new file mode 100644 index 0000000..790eaa7 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/GateWayStatus.java @@ -0,0 +1,29 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 物是否为网关 + */ +@Getter +@AllArgsConstructor +public enum GateWayStatus { + /** + * 网关 + */ + GATE_WAY("1"), + /** + * 非网关--即设备 + */ + NO_GATE_WAY("0"); + + private final String value; + + public static boolean noneMatch(String status){ + return Arrays.stream(GateWayStatus.values()).noneMatch(s-> StringUtils.equals(s.getValue(),status)); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/Granularity.java b/common/core/src/main/java/com/thing/common/core/enumeration/Granularity.java new file mode 100644 index 0000000..0a29ddf --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/Granularity.java @@ -0,0 +1,50 @@ +package com.thing.common.core.enumeration; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * tb api支持的时间粒度,请不要随便新增,若要新增其他方式请@author + */ +@Getter +@AllArgsConstructor +public enum Granularity { + // 以小时计 + HOUR("hh"), + + // 以天计 + DAY("dd"), + + // 以月计 + MONTH("mm"); + + private final String suffix; + + public static List> mapList() { + List> list = Lists.newArrayList(); + for (Granularity granularity : Granularity.values()) { + Map map = Maps.newHashMap(); + map.put("code", granularity.getSuffix()); + map.put("granularity", granularity); + list.add(map); + } + return list; + } + + public static Granularity get(String suffix) { + if (StringUtils.isBlank(suffix)) { + return null; + } + return Arrays.stream(Granularity.values()) + .filter(item -> StringUtils.equals(item.suffix, suffix)) + .findFirst() + .orElse(null); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/GroupTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/GroupTypeEnum.java new file mode 100644 index 0000000..78670f5 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/GroupTypeEnum.java @@ -0,0 +1,70 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 组关系类型 + */ +@Getter +@AllArgsConstructor +public enum GroupTypeEnum { + /** + * 物 + */ + THING("thing", "物", "默认实体组", "物实体组"), + /** + * 属性 + */ + ATTR("attr", "属性", "默认属性组", "物属性组"), + /** + * 标签 + */ + TAG("tag", "标签", "默认标签组", "物标签组"), + /** + * 素材 + */ + MATERIAL("material", "素材", "默认素材组", "素材组"), + + /** + * 部件 + */ + SECTION("section", "部件", "默认部件组", "部件组"), + /** + * 关系 + */ + THING_RELATION("relation", "物关系", "默认物关系组", "物关系组"), + /** + * 关系 TODO 这个已经不存在了 + */ + THING_TYPE("type", "物类型", "默认物类型组", "物类型组"); + + + private final String value; + + private final String name; + + private final String group; + + private final String custom; + + public static String getDefaultGroupName(String value) { + if (StringUtils.isBlank(value)) { + return value; + } + for (GroupTypeEnum groupTypeEnum : GroupTypeEnum.values()) { + if (StringUtils.equalsAnyIgnoreCase(groupTypeEnum.getValue(), value)) { + return groupTypeEnum.getGroup(); + } + } + return value; + } + + public static boolean noneMatch(String type){ + return Arrays.stream(GroupTypeEnum.values()).noneMatch(s-> StringUtils.equals(s.getValue(),type)); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/IntervalPushStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/IntervalPushStatus.java new file mode 100644 index 0000000..08fff69 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/IntervalPushStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 开启关闭间隔推送 + */ +@Getter +@AllArgsConstructor +public enum IntervalPushStatus { + /** + * 开启 + */ + ON("0"), + /** + * 关闭 + */ + OFF("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/IsDefaultEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/IsDefaultEnum.java new file mode 100644 index 0000000..ab1b0ea --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/IsDefaultEnum.java @@ -0,0 +1,21 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 字典存储类型 + */ +@Getter +@AllArgsConstructor +public enum IsDefaultEnum { + /** + * 系统字典 + */ + Y(0), + /** + * 自定义字典 + */ + N(1); + private final Integer value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MenuTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/MenuTypeEnum.java new file mode 100644 index 0000000..d295c2b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MenuTypeEnum.java @@ -0,0 +1,28 @@ +package com.thing.common.core.enumeration; + +/** + * 菜单类型枚举 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public enum MenuTypeEnum { + /** + * 菜单 + */ + MENU(0), + /** + * 按钮 + */ + BUTTON(1); + + private int value; + + MenuTypeEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MimeTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/MimeTypeEnum.java new file mode 100644 index 0000000..c604c57 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MimeTypeEnum.java @@ -0,0 +1,64 @@ +package com.thing.common.core.enumeration; + +import cn.hutool.core.collection.CollectionUtil; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * MimeType找不全,还是用后缀吧... + * + * @author zhenghh. 2022-01-14 + **/ +public enum MimeTypeEnum { + + /** + * 图片格式 + */ + jpg, jpeg, png, gif, bmp, ico, jfif, webp, tif, tiff, tga, svg, + + /** + * office + */ + doc, docx, xls, xlsx, xlsm, ppt, pptx, csv, tsv, dotm, xlt, xltm, dot, dotx, xlam, xla, wps, dps, et, ett, wpt, odt, ods, ots, odp, otp, six, ott, fodt, fods, + /** + * 3d模型 + * 3ds, 3dm, 3mf + */ + obj, stl, ply, gltf, glb, off, fbx, dae, wrl, ifc, brep, step, iges, fcstd, bim, + /** + * 文档 + */ + pdf, ofd, rtf; + + /** + * 获取所有图片格式 + * + * @return list + */ + public static List getImageMimeType() { + return CollectionUtil.newArrayList(jpg.name(), jpeg.name(), png.name(), gif.name(), bmp.name(), ico.name(), + jfif.name(), webp.name(), tif.name(), tiff.name(), tga.name(), svg.name()); + } + + /** + * 获取所有文件格式 + * + * @return list + */ + public static List getAllMimeType() { + List list = CollectionUtil.newArrayList(Arrays.stream(MimeTypeEnum.values()).map(Enum::name).collect(Collectors.toList())); + list.add("3ds"); + list.add("3dm"); + list.add("3mf"); + return list; + } + + public static void main(String[] args) { + System.out.println(FilenameUtils.isExtension(StringUtils.lowerCase("D://1.3ds"), MimeTypeEnum.getAllMimeType())); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MinioFileContentTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/MinioFileContentTypeEnum.java new file mode 100644 index 0000000..da9530d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MinioFileContentTypeEnum.java @@ -0,0 +1,353 @@ +package com.thing.common.core.enumeration; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +@Getter +@AllArgsConstructor +public enum MinioFileContentTypeEnum { + /** + * contentType + */ + TYPE1(".*", "application/octet-stream"), + TYPE2(".001", "application/x-001"), + TYPE3(".301", "application/x-301"), + TYPE4(".323", "text/h323"), + TYPE5(".906", "application/x-906"), + TYPE6(".907", "drawing/907"), + TYPE7(".a11", "application/x-a11"), + TYPE8(".acp", "audio/x-mei-aac"), + TYPE9(".ai", "application/postscript"), + TYPE10(".aif", "audio/aiff"), + TYPE11(".aifc", "audio/aiff"), + TYPE12(".aiff", "audio/aiff"), + TYPE13(".anv", "application/x-anv"), + TYPE14(".asa", "text/asa"), + TYPE15(".asf", "video/x-ms-asf"), + TYPE16(".asp", "text/asp"), + TYPE17(".asx", "video/x-ms-asf"), + TYPE18(".au", "audio/basic"), + TYPE19(".avi", "video/avi"), + TYPE20(".awf", "application/vnd.adobe.workflow"), + TYPE21(".biz", "text/xml"), + TYPE22(".bmp", "application/x-bmp"), + TYPE23(".bot", "application/x-bot"), + TYPE24(".c4t", "application/x-c4t"), + TYPE25(".c90", "application/x-c90"), + TYPE26(".cal", "application/x-cals"), + TYPE27(".cat", "application/s-pki.seccat"), + TYPE28(".cdf", "application/x-netcdf"), + TYPE29(".cdr", "application/x-cdr"), + TYPE30(".cel", "application/x-cel"), + TYPE31(".cer", "application/x-x509-ca-cert"), + TYPE32(".cg4", "application/x-g4"), + TYPE33(".cgm", "application/x-cgm"), + TYPE34(".cit", "application/x-cit"), + TYPE35(".class", "java/*"), + TYPE36(".cml", "text/xml"), + TYPE37(".cmp", "application/x-cmp"), + TYPE38(".cmx", "application/x-cmx"), + TYPE39(".cot", "application/x-cot"), + TYPE40(".crl", "application/pkix-crl"), + TYPE41(".crt", "application/x-x509-ca-cert"), + TYPE42(".csi", "application/x-csi"), + TYPE43(".css", "text/css"), + TYPE44(".cut", "application/x-cut"), + TYPE45(".dbf", "application/x-dbf"), + TYPE46(".dbm", "application/x-dbm"), + TYPE47(".dbx", "application/x-dbx"), + TYPE48(".dcd", "text/xml"), + TYPE49(".dcx", "application/x-dcx"), + TYPE50(".der", "application/x-x509-ca-cert"), + TYPE51(".dgn", "application/x-dgn"), + TYPE52(".dib", "application/x-dib"), + TYPE53(".dll", "application/x-msdownload"), + TYPE54(".doc", "application/msword"), + TYPE55(".dot", "application/msword"), + TYPE56(".drw", "application/x-drw"), + TYPE57(".dtd", "text/xml"), + TYPE58(".dwf", "Model/vnd.dwf"), + TYPE59(".dwg", "application/x-dwg"), + TYPE60(".dxb", "application/x-dxb"), + TYPE61(".dxf", "application/x-dxf"), + TYPE62(".edn", "application/vnd.adobe.edn"), + TYPE63(".emf", "application/x-emf"), + TYPE64(".eml", "message/rfc822"), + TYPE65(".ent", "text/xml"), + TYPE66(".epi", "application/x-epi"), + TYPE67(".eps", "application/x-ps"), + TYPE68(".etd", "application/x-ebx"), + TYPE69(".exe", "application/x-msdownload"), + TYPE70(".fax", "image/fax"), + TYPE71(".fdf", "application/vnd.fdf"), + TYPE72(".fif", "application/fractals"), + TYPE73(".fo", "text/xml"), + TYPE74(".frm", "application/x-frm"), + TYPE75(".g4", "application/x-g4"), + TYPE76(".gbr", "application/x-gbr"), + TYPE77(".gcd", "application/x-gcd"), + TYPE78(".gif", "image/gif"), + TYPE79(".gl2", "application/x-gl2"), + TYPE80(".gp4", "application/x-gp4"), + TYPE81(".hgl", "application/x-hgl"), + TYPE82(".hmr", "application/x-hmr"), + TYPE83(".hpg", "application/x-hpgl"), + TYPE84(".hpl", "application/x-hpl"), + TYPE85(".hqx", "application/mac-binhex40"), + TYPE86(".hrf", "application/x-hrf"), + TYPE87(".hta", "application/hta"), + TYPE88(".htc", "text/x-component"), + TYPE89(".htm", "text/html"), + TYPE90(".html", "text/html"), + TYPE91(".htt", "text/webviewhtml"), + TYPE92(".htx", "text/html"), + TYPE93(".icb", "application/x-icb"), + TYPE94(".ico", "image/x-icon"), + TYPE95(".iff", "application/x-iff"), + TYPE96(".ig4", "application/x-g4"), + TYPE97(".igs", "application/x-igs"), + TYPE98(".iii", "application/x-iphone"), + TYPE99(".img", "application/x-img"), + TYPE100(".ins", "application/x-internet-signup"), + TYPE101(".isp", "application/x-internet-signup"), + TYPE102(".IVF", "video/x-ivf"), + TYPE103(".java", "java/*"), + TYPE104(".jfif", "image/jpeg"), + TYPE105(".jpe", "image/jpeg"), + TYPE106(".jpeg", "image/jpeg"), + TYPE107(".jpg", "image/jpeg"), + TYPE108(".js", "application/x-javascript"), + TYPE109(".jsp", "text/html"), + TYPE110(".la1", "audio/x-liquid-file"), + TYPE111(".lar", "application/x-laplayer-reg"), + TYPE112(".latex", "application/x-latex"), + TYPE113(".lavs", "audio/x-liquid-secure"), + TYPE114(".lbm", "application/x-lbm"), + TYPE115(".lmsff", "audio/x-la-lms"), + TYPE116(".ls", "application/x-javascript"), + TYPE117(".ltr", "application/x-ltr"), + TYPE118(".m1v", "video/x-mpeg"), + TYPE119(".m2v", "video/x-mpeg"), + TYPE120(".m3u", "audio/mpegurl"), + TYPE121(".m4e", "video/mpeg4"), + TYPE122(".mac", "application/x-mac"), + TYPE123(".man", "application/x-troff-man"), + TYPE124(".math", "text/xml"), + TYPE125(".mdb", "application/x-mdb"), + TYPE126(".mfp", "application/x-shockwave-flash"), + TYPE127(".mht", "message/rfc822"), + TYPE128(".mhtml", "message/rfc822"), + TYPE129(".mi", "application/x-mi"), + TYPE130(".mid", "audio/mid"), + TYPE131(".midi", "audio/mid"), + TYPE132(".mil", "application/x-mil"), + TYPE133(".mml", "text/xml"), + TYPE134(".mnd", "audio/x-musicnet-download"), + TYPE135(".mns", "audio/x-musicnet-stream"), + TYPE136(".mocha", "application/x-javascript"), + TYPE137(".movie", "video/x-sgi-movie"), + TYPE138(".mp1", "audio/mp1"), + TYPE139(".mp2", "audio/mp2"), + TYPE140(".mp2v", "video/mpeg"), + TYPE141(".mp3", "audio/mp3"), + TYPE142(".mp4", "video/mp4"), + TYPE143(".mpa", "video/x-mpg"), + TYPE144(".mpd", "application/-project"), + TYPE145(".mpe", "video/x-mpeg"), + TYPE146(".mpeg", "video/mpg"), + TYPE147(".mpg", "video/mpg"), + TYPE148(".mpga", "audio/rn-mpeg"), + TYPE149(".mpp", "application/-project"), + TYPE150(".mps", "video/x-mpeg"), + TYPE151(".mpt", "application/-project"), + TYPE152(".mpv", "video/mpg"), + TYPE153(".mpv2", "video/mpeg"), + TYPE154(".mpw", "application/s-project"), + TYPE155(".mpx", "application/-project"), + TYPE156(".mtx", "text/xml"), + TYPE157(".mxp", "application/x-mmxp"), + TYPE158(".net", "image/pnetvue"), + TYPE159(".nrf", "application/x-nrf"), + TYPE160(".nws", "message/rfc822"), + TYPE161(".odc", "text/x-ms-odc"), + TYPE162(".out", "application/x-out"), + TYPE163(".p10", "application/pkcs10"), + TYPE164(".p12", "application/x-pkcs12"), + TYPE165(".p7b", "application/x-pkcs7-certificates"), + TYPE166(".p7c", "application/pkcs7-mime"), + TYPE167(".p7m", "application/pkcs7-mime"), + TYPE168(".p7r", "application/x-pkcs7-certreqresp"), + TYPE169(".p7s", "application/pkcs7-signature"), + TYPE170(".pc5", "application/x-pc5"), + TYPE171(".pci", "application/x-pci"), + TYPE172(".pcl", "application/x-pcl"), + TYPE173(".pcx", "application/x-pcx"), + TYPE174(".pdf", "application/pdf"), + TYPE175(".pdx", "application/vnd.adobe.pdx"), + TYPE176(".pfx", "application/x-pkcs12"), + TYPE177(".pgl", "application/x-pgl"), + TYPE178(".pic", "application/x-pic"), + TYPE179(".pko", "application-pki.pko"), + TYPE180(".pl", "application/x-perl"), + TYPE181(".plg", "text/html"), + TYPE182(".pls", "audio/scpls"), + TYPE183(".plt", "application/x-plt"), + TYPE184(".png", "image/png"), + TYPE185(".pot", "applications-powerpoint"), + TYPE186(".ppa", "application/vs-powerpoint"), + TYPE187(".ppm", "application/x-ppm"), + TYPE188(".pps", "application-powerpoint"), + TYPE189(".ppt", "applications-powerpoint"), + TYPE190(".pr", "application/x-pr"), + TYPE191(".prf", "application/pics-rules"), + TYPE192(".prn", "application/x-prn"), + TYPE193(".prt", "application/x-prt"), + TYPE194(".ps", "application/postscript"), + TYPE195(".ptn", "application/x-ptn"), + TYPE196(".pwz", "application/powerpoint"), + TYPE197(".r3t", "text/vnd.rn-realtext3d"), + TYPE198(".ra", "audio/vnd.rn-realaudio"), + TYPE199(".ram", "audio/x-pn-realaudio"), + TYPE200(".ras", "application/x-ras"), + TYPE201(".rat", "application/rat-file"), + TYPE202(".rdf", "text/xml"), + TYPE203(".rec", "application/vnd.rn-recording"), + TYPE204(".red", "application/x-red"), + TYPE205(".rgb", "application/x-rgb"), + TYPE206(".rjs", "application/vnd.rn-realsystem-rjs"), + TYPE207(".rjt", "application/vnd.rn-realsystem-rjt"), + TYPE208(".rlc", "application/x-rlc"), + TYPE209(".rle", "application/x-rle"), + TYPE210(".rm", "application/vnd.rn-realmedia"), + TYPE211(".rmf", "application/vnd.adobe.rmf"), + TYPE212(".rmi", "audio/mid"), + TYPE213(".rmj", "application/vnd.rn-realsystem-rmj"), + TYPE214(".rmm", "audio/x-pn-realaudio"), + TYPE215(".rmp", "application/vnd.rn-rn_music_package"), + TYPE216(".rms", "application/vnd.rn-realmedia-secure"), + TYPE217(".rmvb", "application/vnd.rn-realmedia-vbr"), + TYPE218(".rmx", "application/vnd.rn-realsystem-rmx"), + TYPE219(".rnx", "application/vnd.rn-realplayer"), + TYPE220(".rp", "image/vnd.rn-realpix"), + TYPE221(".rpm", "audio/x-pn-realaudio-plugin"), + TYPE222(".rsml", "application/vnd.rn-rsml"), + TYPE223(".rt", "text/vnd.rn-realtext"), + TYPE224(".rtf", "application/x-rtf"), + TYPE225(".rv", "video/vnd.rn-realvideo"), + TYPE226(".sam", "application/x-sam"), + TYPE227(".sat", "application/x-sat"), + TYPE228(".sdp", "application/sdp"), + TYPE229(".sdw", "application/x-sdw"), + TYPE230(".sit", "application/x-stuffit"), + TYPE231(".slb", "application/x-slb"), + TYPE232(".sld", "application/x-sld"), + TYPE233(".slk", "drawing/x-slk"), + TYPE234(".smi", "application/smil"), + TYPE235(".smil", "application/smil"), + TYPE236(".smk", "application/x-smk"), + TYPE237(".snd", "audio/basic"), + TYPE238(".sol", "text/plain"), + TYPE239(".sor", "text/plain"), + TYPE240(".spc", "application/x-pkcs7-certificates"), + TYPE241(".spl", "application/futuresplash"), + TYPE242(".spp", "text/xml"), + TYPE243(".ssm", "application/streamingmedia"), + TYPE244(".sst", "application-pki.certstore"), + TYPE245(".stl", "application/-pki.stl"), + TYPE246(".stm", "text/html"), + TYPE247(".sty", "application/x-sty"), + TYPE248(".svg", "image/svg+xml"), + TYPE249(".swf", "application/x-shockwave-flash"), + TYPE250(".tdf", "application/x-tdf"), + TYPE251(".tg4", "application/x-tg4"), + TYPE252(".tga", "application/x-tga"), + TYPE253(".tif", "image/tiff"), + TYPE254(".tiff", "image/tiff"), + TYPE255(".tld", "text/xml"), + TYPE256(".top", "drawing/x-top"), + TYPE257(".torrent", "application/x-bittorrent"), + TYPE258(".tsd", "text/xml"), + TYPE259(".txt", "text/plain"), + TYPE260(".uin", "application/x-icq"), + TYPE261(".uls", "text/iuls"), + TYPE262(".vcf", "text/x-vcard"), + TYPE263(".vda", "application/x-vda"), + TYPE264(".vdx", "application/vnd.visio"), + TYPE265(".vml", "text/xml"), + TYPE266(".vpg", "application/x-vpeg005"), + TYPE267(".vsd", "application/x-vsd"), + TYPE268(".vss", "application/vnd.visio"), + TYPE269(".vst", "application/x-vst"), + TYPE270(".vsw", "application/vnd.visio"), + TYPE271(".vsx", "application/vnd.visio"), + TYPE272(".vtx", "application/vnd.visio"), + TYPE273(".vxml", "text/xml"), + TYPE274(".wav", "audio/wav"), + TYPE275(".wax", "audio/x-ms-wax"), + TYPE276(".wb1", "application/x-wb1"), + TYPE277(".wb2", "application/x-wb2"), + TYPE278(".wb3", "application/x-wb3"), + TYPE279(".wbmp", "image/vnd.wap.wbmp"), + TYPE280(".wiz", "application/msword"), + TYPE281(".wk3", "application/x-wk3"), + TYPE282(".wk4", "application/x-wk4"), + TYPE283(".wkq", "application/x-wkq"), + TYPE284(".wks", "application/x-wks"), + TYPE285(".wm", "video/x-ms-wm"), + TYPE286(".wma", "audio/x-ms-wma"), + TYPE287(".wmd", "application/x-ms-wmd"), + TYPE288(".wmf", "application/x-wmf"), + TYPE289(".wml", "text/vnd.wap.wml"), + TYPE290(".wmv", "video/x-ms-wmv"), + TYPE291(".wmx", "video/x-ms-wmx"), + TYPE292(".wmz", "application/x-ms-wmz"), + TYPE293(".wp6", "application/x-wp6"), + TYPE294(".wpd", "application/x-wpd"), + TYPE295(".wpg", "application/x-wpg"), + TYPE296(".wpl", "application/-wpl"), + TYPE297(".wq1", "application/x-wq1"), + TYPE298(".wr1", "application/x-wr1"), + TYPE299(".wri", "application/x-wri"), + TYPE300(".wrk", "application/x-wrk"), + TYPE301(".ws", "application/x-ws"), + TYPE302(".ws2", "application/x-ws"), + TYPE303(".wsc", "text/scriptlet"), + TYPE304(".wsdl", "text/xml"), + TYPE305(".wvx", "video/x-ms-wvx"), + TYPE306(".xdp", "application/vnd.adobe.xdp"), + TYPE307(".xdr", "text/xml"), + TYPE308(".xfd", "application/vnd.adobe.xfd"), + TYPE309(".xfdf", "application/vnd.adobe.xfdf"), + TYPE310(".xhtml", "text/html"), + TYPE311(".xls", "application/x-xls"), + TYPE312(".xlw", "application/x-xlw"), + TYPE313(".xml", "text/xml"), + TYPE314(".xpl", "audio/scpls"), + TYPE315(".xq", "text/xml"), + TYPE316(".xql", "text/xml"), + TYPE317(".xquery", "text/xml"), + TYPE318(".xsd", "text/xml"), + TYPE319(".xsl", "text/xml"), + TYPE320(".xslt", "text/xml"), + TYPE321(".xwd", "application/x-xwd"), + TYPE322(".x_b", "application/x-x_b"), + TYPE323(".x_t", "application/x-x_t"); + + + private final String suffix; + private final String contentType; + + /** + * 获取所有文件格式 + * + * @return list + */ + public static Map getKeyCodeMap() { + return Arrays.stream(MinioFileContentTypeEnum.values()).collect(Collectors.toMap(MinioFileContentTypeEnum::getSuffix, MinioFileContentTypeEnum::getContentType)); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MonitorPolymerizationType.java b/common/core/src/main/java/com/thing/common/core/enumeration/MonitorPolymerizationType.java new file mode 100644 index 0000000..dd5cf3a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MonitorPolymerizationType.java @@ -0,0 +1,38 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 聚合函数类型 + */ +@Getter +@AllArgsConstructor +public enum MonitorPolymerizationType { + /** + * 最大 + */ + MAX("max"), + /** + * 最小 + */ + MIN("min"), + /** + * 平均 + */ + AVG("avg"), + /** + * 求和 + */ + SUM("sum"), + /** + * 计数 + */ + COUNT("count"), + /** + * 空 + */ + BLANK("blank"); + + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MonitorTimeType.java b/common/core/src/main/java/com/thing/common/core/enumeration/MonitorTimeType.java new file mode 100644 index 0000000..bd097d9 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MonitorTimeType.java @@ -0,0 +1,30 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 监控分析时间类型 + */ +@Getter +@AllArgsConstructor +public enum MonitorTimeType { + /** + * 最新 + */ + LASTEST("lastest"), + /** + * 最近 + */ + NEAREST("nearest"), + /** + * 区间 + */ + INTERVAL("interval"), + /** + * 时间段 + */ + RANGE("range"); + + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/MsgType.java b/common/core/src/main/java/com/thing/common/core/enumeration/MsgType.java new file mode 100644 index 0000000..7eceadc --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/MsgType.java @@ -0,0 +1,34 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 消息状态 + */ +@Getter +@AllArgsConstructor +public enum MsgType { + /** + * 短信 + */ + Sms("0"), + /** + * 邮箱 + */ + Mail("1"), + /** + * 微信公众号 + */ + WeChat("2"), + /** + * 钉钉 + */ + DingTalk("3"), + /** + * 企业微信 + */ + WeChatCom("4"); + + private final String value; +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/OrderStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/OrderStatus.java new file mode 100644 index 0000000..e72ff03 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/OrderStatus.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 订单状态 + */ +@Getter +@AllArgsConstructor +public enum OrderStatus { + /** + * 已取消 + */ + CANCEL(-1), + /** + * 等待付款 + */ + WAITING(0), + /** + * 已完成 + */ + FINISH(1); + + private final int value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/PeakValleyEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/PeakValleyEnum.java new file mode 100644 index 0000000..33a08be --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/PeakValleyEnum.java @@ -0,0 +1,25 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +@AllArgsConstructor +public enum PeakValleyEnum { + RUSH("rush", "尖"), + PEAK("peak", "峰"), + VALLEY("valley", "谷"), + NORMAL("normal", "平"); + + private final String code; + private final String name; + + public static PeakValleyEnum match(String keyword) { + return Arrays.stream(PeakValleyEnum.values()) + .filter(item -> keyword.contains(item.code)) + .findFirst() + .orElse(null); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/PermissionStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/PermissionStatus.java new file mode 100644 index 0000000..e8b4f63 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/PermissionStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 字段是否需要关联权限 + */ +@Getter +@AllArgsConstructor +public enum PermissionStatus { + /** + * 附带权限字段 + */ + PERMISSION("0"), + /** + * 不附带权限字段 + */ + NO_PERMISSION("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/Protocol.java b/common/core/src/main/java/com/thing/common/core/enumeration/Protocol.java new file mode 100644 index 0000000..f658310 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/Protocol.java @@ -0,0 +1,31 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 协议 + */ +@Getter +@AllArgsConstructor +public enum Protocol { + /** + * http + */ + HTTP("http"), + /** + * https + */ + HTTPS("https"), + /** + * ws + */ + WS("ws"), + /** + * tcp + */ + TCP("tcp"); + + private final String value; + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ProtocolPrefix.java b/common/core/src/main/java/com/thing/common/core/enumeration/ProtocolPrefix.java new file mode 100644 index 0000000..7e7ae9c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ProtocolPrefix.java @@ -0,0 +1,25 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * tb api支持的时间粒度,请不要随便新增,若要新增其他方式请@author chenyang + */ +@Getter +@AllArgsConstructor +public enum ProtocolPrefix { + // http协议 + HTTP("http://"), + + // https协议 + HTTPS("https://"), + + // ws协议 + WS("ws://"), + + // tcp协议 + TCP("tcp://"); + + private final String prefix; +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/PushReceiveStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/PushReceiveStatus.java new file mode 100644 index 0000000..11f78de --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/PushReceiveStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 推送接收的状态 + */ +@Getter +@AllArgsConstructor +public enum PushReceiveStatus { + /** + * 已接收确认 + */ + RECEIVE("0"), + /** + * 未接收确认 + */ + NO_RECEIVE("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/PushStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/PushStatus.java new file mode 100644 index 0000000..c760a8e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/PushStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 推送状态 + */ +@Getter +@AllArgsConstructor +public enum PushStatus { + /** + * 成功 + */ + SUCCESS("0"), + /** + * 失败 + */ + FAIL("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/QueryChannel.java b/common/core/src/main/java/com/thing/common/core/enumeration/QueryChannel.java new file mode 100644 index 0000000..1ae5260 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/QueryChannel.java @@ -0,0 +1,12 @@ +package com.thing.common.core.enumeration; + +public enum QueryChannel { + /** + * 从TB中查 + */ + QUERY_FROM_TB, + /** + * 从DB中查 + */ + QUERY_FROM_DB; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/QueueEventType.java b/common/core/src/main/java/com/thing/common/core/enumeration/QueueEventType.java new file mode 100644 index 0000000..2427981 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/QueueEventType.java @@ -0,0 +1,24 @@ +package com.thing.common.core.enumeration; + +public enum QueueEventType { + /** + * 缓存 + */ + CACHE, + /** + * 告警 + */ + ALARM, + /** + * 计算 + */ + CALCULATION, + /** + * 看板 + */ + SOCKET, + /** + * 设备 + */ + DEVICE +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/QueueOriginType.java b/common/core/src/main/java/com/thing/common/core/enumeration/QueueOriginType.java new file mode 100644 index 0000000..e2d5cc2 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/QueueOriginType.java @@ -0,0 +1,35 @@ +package com.thing.common.core.enumeration; + +/** + * 数据来源 + */ +public enum QueueOriginType { + /** + * mqtt broker + */ + MQTT_BROKER, + /** + * mqtt client + */ + MQTT_CLIENT, + /** + * api + */ + API, + /** + * socket订阅tb + */ + TB, + /** + * modbus tcp + */ + MODBUS_TCP, + /** + * 自动数据同步 系统或socket关闭重启后同步 + */ + AUTO_DATA_SYNC, + /** + * 手动数据同步 保存缓存设置或缓存查询点击同步按钮 + */ + MANUAL_DATA_SYNC +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/RegionLeafEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/RegionLeafEnum.java new file mode 100644 index 0000000..23a5aaa --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/RegionLeafEnum.java @@ -0,0 +1,23 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 叶子节点枚举 + * + * @author Mark sunlightcs@gmail.com + */ +@AllArgsConstructor + +public enum RegionLeafEnum { + YES(1), + NO(0); + + private final int value; + + public int value() { + return this.value; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/RegionLevelEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/RegionLevelEnum.java new file mode 100644 index 0000000..2fd2097 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/RegionLevelEnum.java @@ -0,0 +1,22 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 行政区域 级别枚举 + * + * @author Mark sunlightcs@gmail.com + */ +@AllArgsConstructor +public enum RegionLevelEnum { + ONE(1), + TWO(2), + THREE(3); + + private final int value; + public int value() { + return this.value; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/RepeatPushStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/RepeatPushStatus.java new file mode 100644 index 0000000..b9504e8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/RepeatPushStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 开启关闭重复推送 + */ +@Getter +@AllArgsConstructor +public enum RepeatPushStatus { + /** + * 开启 + */ + ON("0"), + /** + * 关闭 + */ + OFF("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ReportTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/ReportTypeEnum.java new file mode 100644 index 0000000..85ba4a0 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ReportTypeEnum.java @@ -0,0 +1,40 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 导出文件类型 + */ +@Getter +@AllArgsConstructor +public enum ReportTypeEnum { + /** + * 监控实时分析列表 + */ + MONITORING("1", "监控实时分析列表"), + /** + * 用量分析列表 + */ + DOSAGE("2", "用量分析列表"), + /** + * 数据报表 + */ + DATAREPORT("3", "数据报表"); + + private final String code; + + private final String desc; + + + public static ReportTypeEnum getReportType(String code) { + return Arrays.stream(ReportTypeEnum.values()) + .filter(item -> StringUtils.equals(code, item.getCode())) + .findFirst() + .orElseThrow(() -> new RuntimeException("未找到对应数据")); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/RoleType.java b/common/core/src/main/java/com/thing/common/core/enumeration/RoleType.java new file mode 100644 index 0000000..0905795 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/RoleType.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 角色类型 + */ +@Getter +@AllArgsConstructor +public enum RoleType { + /** + * 企业角色 + */ + COMPANY("0"), + /** + * 用户角色 + */ + USER("1"), + /** + * 默认角色 + */ + DEFAULT("2"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/RsaKeyType.java b/common/core/src/main/java/com/thing/common/core/enumeration/RsaKeyType.java new file mode 100644 index 0000000..d01f1fd --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/RsaKeyType.java @@ -0,0 +1,12 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum RsaKeyType { + PUBLIC("public"), + PRIVATE("private"); + private final String value; +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ScheduleStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/ScheduleStatus.java new file mode 100644 index 0000000..081afa3 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ScheduleStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 定时任务状态 + */ +@Getter +@AllArgsConstructor +public enum ScheduleStatus { + /** + * 暂停 + */ + PAUSE(0), + /** + * 正常 + */ + NORMAL(1); + + private final int value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ShowStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/ShowStatus.java new file mode 100644 index 0000000..82041d8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ShowStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据源是否展示初值终值 + */ +@Getter +@AllArgsConstructor +public enum ShowStatus { + /** + * 展示 + */ + TRUE("0"), + /** + * 不展示 + */ + FALSE("1"); + + private final String showStatus; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SignEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SignEnum.java new file mode 100644 index 0000000..75fc1d8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SignEnum.java @@ -0,0 +1,98 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Getter +@AllArgsConstructor +public enum SignEnum { + GT(">", "大于", "<=", SignTypeEnum.RELATION), + GE(">=", "大于等于", "<", SignTypeEnum.RELATION), + LT("<", "小于", ">=", SignTypeEnum.RELATION), + LE("<=", "小于等于", ">", SignTypeEnum.RELATION), + EQ("=", "等于", "=", SignTypeEnum.RELATION), + DEQ("==", "等于", "==", SignTypeEnum.RELATION), + AND("&", "与", "|", SignTypeEnum.LOGIC), + DAND("&&", "短路与", "||", SignTypeEnum.LOGIC), + OR("|", "或", "&", SignTypeEnum.LOGIC), + DOR("||", "短路或", "&&", SignTypeEnum.LOGIC), + PLUS("+", "加", "-", SignTypeEnum.OPERATOR), + MINUS("-", "减", "+", SignTypeEnum.OPERATOR), + MULTIPLY("*", "乘", "/", SignTypeEnum.OPERATOR), + DIVIDE("/", "除", "*", SignTypeEnum.OPERATOR), + LB("(", "左侧小括号", ")", SignTypeEnum.BRACKETS), + RB(")", "右侧小括号", "(", SignTypeEnum.BRACKETS); + + private final String val; + private final String name; + private final String inverseSign; + private final SignTypeEnum type; + + public static SignEnum match(String val) { + return Arrays.stream(SignEnum.values()) + .filter(item -> Objects.equals(item.getVal(), val)) + .findFirst() + .orElse(null); + } + + public static List noBracketsSigns() { + return Arrays.stream(SignEnum.values()) + .filter(SignEnum::isNotBrackets) + .collect(Collectors.toList()); + } + + public boolean isNotBrackets() { + return type != SignTypeEnum.BRACKETS; + } + + /** 获取逆向符号 */ + public SignEnum getInverseSign() { + return match(inverseSign); + } + + public List getSubSigns() { + return switch (this) { + case GE -> Arrays.asList(GE, GT, EQ); + case LE -> Arrays.asList(LE, LT, EQ); + default -> List.of(this); + }; + } + + public List getSubSignStr() { + List subSigns = getSubSigns(); + return subSigns.stream().map(SignEnum::getVal).collect(Collectors.toList()); + } + + public boolean compare(String v1, String v2) { + Double t1 = Double.parseDouble(v1); + Double t2 = Double.parseDouble(v2); + return compare(t1, t2); + } + + public boolean compare(Double v1, Double v2) { + BigDecimal t1 = BigDecimal.valueOf(v1); + BigDecimal t2 = BigDecimal.valueOf(v2); + return compare(t1, t2); + } + + public boolean compare(BigDecimal v1, BigDecimal v2) { + int res = v1.compareTo(v2); + return switch (this) { + case GT -> res > 0; + case GE -> res >= 0; + case LT -> res < 0; + case LE -> res <= 0; + case EQ, DEQ -> res == 0; + default -> false; + }; + } + + public static SignEnum combineSign(SignEnum signA, SignEnum signB) { + String combineSign = signA.getVal() + signB.getVal(); + return match(combineSign); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SignTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SignTypeEnum.java new file mode 100644 index 0000000..8502921 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SignTypeEnum.java @@ -0,0 +1,15 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter + @AllArgsConstructor + public enum SignTypeEnum { + RELATION("关系运算符"), + LOGIC("逻辑连结符"), + OPERATOR("数值计算操作符"), + BRACKETS("括号"); + + private final String name; + } \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SmsService.java b/common/core/src/main/java/com/thing/common/core/enumeration/SmsService.java new file mode 100644 index 0000000..aed50ca --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SmsService.java @@ -0,0 +1,29 @@ +package com.thing.common.core.enumeration; + +/** + * 短信服务商 + */ + public enum SmsService { + /** + * 阿里云 + */ + ALIYUN(1), + /** + * 腾讯云 + */ + QCLOUD(2), + /** + * 七牛 + */ + QINIU(3); + + private int value; + + SmsService(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SocketEventType.java b/common/core/src/main/java/com/thing/common/core/enumeration/SocketEventType.java new file mode 100644 index 0000000..1a444aa --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SocketEventType.java @@ -0,0 +1,12 @@ +package com.thing.common.core.enumeration; + +public enum SocketEventType { + /** + * 客户端操作事件 + */ + CLIENT, + /** + * 设备操作事件 + */ + DEVICE +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SourceStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/SourceStatus.java new file mode 100644 index 0000000..0662397 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SourceStatus.java @@ -0,0 +1,17 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 数据源状态 + */ +@Getter +@AllArgsConstructor +public enum SourceStatus { + ENABLE(1), + + DISABLE(0); + + private final Integer status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SubscribeStatusEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SubscribeStatusEnum.java new file mode 100644 index 0000000..e474792 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SubscribeStatusEnum.java @@ -0,0 +1,20 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 订阅状态 + */ +@Getter +@AllArgsConstructor +public enum SubscribeStatusEnum { + /** + * 订阅 + */ + SUBSCRIBE, + /** + * 取消订阅 + */ + UNSUBSCRIBE +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SuperAdminEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SuperAdminEnum.java new file mode 100644 index 0000000..048d22c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SuperAdminEnum.java @@ -0,0 +1,27 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 超级管理员枚举 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@AllArgsConstructor +public enum SuperAdminEnum { + YES(1), + NO(0); + + private final int value; + + public int value() { + return this.value; + } + + public static boolean isSuperAdmin(int code){ + return code == YES.value; + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SuperTenantEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SuperTenantEnum.java new file mode 100644 index 0000000..6978c67 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SuperTenantEnum.java @@ -0,0 +1,22 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 租户管理员枚举 + * + * @author Mark sunlightcs@gmail.com + */ +@AllArgsConstructor +public enum SuperTenantEnum { + YES(1), + NO(0); + + private final int value; + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SyncUpdateEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SyncUpdateEnum.java new file mode 100644 index 0000000..9d11695 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SyncUpdateEnum.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 模板属性与物属性的关系类型 + */ +@Getter +@AllArgsConstructor +public enum SyncUpdateEnum { + /** + * 同步 + */ + SYNC("sync"), + /** + * 更新 + */ + UPDATE("update"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/SysTenantEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/SysTenantEnum.java new file mode 100644 index 0000000..108a86f --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/SysTenantEnum.java @@ -0,0 +1,22 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 系统租户枚举 + * + * @author Mark sunlightcs@gmail.com + */ +@AllArgsConstructor +public enum SysTenantEnum { + YES(1), + NO(0); + + private final int value; + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TaskTime.java b/common/core/src/main/java/com/thing/common/core/enumeration/TaskTime.java new file mode 100644 index 0000000..c2ab793 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TaskTime.java @@ -0,0 +1,26 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 定时类型 + */ +@Getter +@AllArgsConstructor +public enum TaskTime { + /** + * 时 + */ + hh("时"), + /** + * 分 + */ + mm("分"), + /** + * 秒 + */ + ss("秒"); + + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TaskTimeType.java b/common/core/src/main/java/com/thing/common/core/enumeration/TaskTimeType.java new file mode 100644 index 0000000..aa5c70d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TaskTimeType.java @@ -0,0 +1,34 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 定时类型 + */ +@Getter +@AllArgsConstructor +public enum TaskTimeType { + /** + * 定时 + */ + TIME("定时"), + /** + * 每日 + */ + DAY("每日"), + /** + * 每周 + */ + WEEK("每周"), + /** + * 轮询 + */ + POLLING("轮询"), + /** + * CRON + */ + CRON("CRON"); + + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TaskType.java b/common/core/src/main/java/com/thing/common/core/enumeration/TaskType.java new file mode 100644 index 0000000..91666de --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TaskType.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 定时任务类型 + */ +@Getter +@AllArgsConstructor +public enum TaskType { + /** + * 控制 + */ + CTL("设备控制"), + /** + * bean + */ + BEAN("Bean"); + + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TaskWeek.java b/common/core/src/main/java/com/thing/common/core/enumeration/TaskWeek.java new file mode 100644 index 0000000..2e721d8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TaskWeek.java @@ -0,0 +1,43 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 定时类型 + */ +@Getter +@AllArgsConstructor +public enum TaskWeek { + /** + * 周一 + */ + Mon("2", "周一"), + /** + * 周二 + */ + Tue("3", "周二"), + /** + * 周三 + */ + Wed("4", "周三"), + /** + * 周四 + */ + Thu("5", "周四"), + /** + * 周五 + */ + Fri("6", "周五"), + /** + * 周六 + */ + Sat("7", "周六"), + /** + * 周天 + */ + Sun("1", "周日"); + + private final String week; + private final String name; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TbTimeout.java b/common/core/src/main/java/com/thing/common/core/enumeration/TbTimeout.java new file mode 100644 index 0000000..f9c634a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TbTimeout.java @@ -0,0 +1,17 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * tb websocket超时连接超时时间,单位:秒 + */ +@Getter +@AllArgsConstructor +public enum TbTimeout { + ONE_DAY_SECONDS(86400L), + + ONE_WEEK_SECONDS(604800L); + + private final Long timeout; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TemplateMark.java b/common/core/src/main/java/com/thing/common/core/enumeration/TemplateMark.java new file mode 100644 index 0000000..30d2e90 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TemplateMark.java @@ -0,0 +1,30 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 物是否是模板类型 + */ +@Getter +@AllArgsConstructor +public enum TemplateMark { + /** + * 0 物实体 + */ + NO("0"), + /** + * 1 物模板 + */ + YES("1"); + + private final String value; + + public static boolean noneMatch(String status) { + return Arrays.stream(TemplateMark.values()).noneMatch(s -> StringUtils.equals(s.getValue(), status)); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TenantGroupEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/TenantGroupEnum.java new file mode 100644 index 0000000..f3ee728 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TenantGroupEnum.java @@ -0,0 +1,26 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 租户组管理员枚举 + * + * @author Mark sunlightcs@gmail.com + */ +@AllArgsConstructor +public enum TenantGroupEnum { + YES(1), + NO(0); + + private final int value; + + public int value() { + return this.value; + } + + public static boolean isTenantGroup(int code){ + return code == YES.value; + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TenantSaveType.java b/common/core/src/main/java/com/thing/common/core/enumeration/TenantSaveType.java new file mode 100644 index 0000000..3706459 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TenantSaveType.java @@ -0,0 +1,24 @@ +package com.thing.common.core.enumeration; + +public enum TenantSaveType { + /** + * 基础信息 + */ + base, + /** + * 基础信息+账号 + */ + user, + /** + * 基础信息+账号+菜单 + */ + menu, + /** + * 基础信息+账号+菜单+分配物 + */ + thing, + /** + * 基础信息+账号+分配物+分配企业 + */ + comp +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingAttrType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingAttrType.java new file mode 100644 index 0000000..8f72534 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingAttrType.java @@ -0,0 +1,31 @@ +package com.thing.common.core.enumeration; + +import com.thing.common.core.exception.SysException; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +@Getter +@AllArgsConstructor +public enum ThingAttrType { + + + YY("yy",TimeType.YEAR), + MM("mm",TimeType.MONTH), + DD("dd",TimeType.DAY), + HH("hh",TimeType.HOUR), + AM("am",TimeType.MINUTE); + + private final String code; + private final TimeType time; + + public static void matchAttr(String attr,TimeType timeType) { + boolean isMatch = Arrays.stream(ThingAttrType.values()) + .anyMatch(item -> item.code.endsWith(attr) && item.time.equals(timeType)); + if (!isMatch) { + throw new SysException("属性类型不存在"); + } + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingEnableStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingEnableStatus.java new file mode 100644 index 0000000..26d40dc --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingEnableStatus.java @@ -0,0 +1,29 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 物是否启用的状态 + */ +@Getter +@AllArgsConstructor +public enum ThingEnableStatus { + /** + * 启用 + */ + START("1"), + /** + * 停用 + */ + STOP("0"); + + private final String value; + + public static boolean noneMatch(String status){ + return Arrays.stream(ThingEnableStatus.values()).noneMatch(s-> StringUtils.equals(s.getValue(),status)); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingExistType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingExistType.java new file mode 100644 index 0000000..33243b6 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingExistType.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 租户内物存在类型 + */ +@Getter +@AllArgsConstructor +public enum ThingExistType { + /** + * 0虚拟 + */ + INVENTED("0"), + /** + * 1真实 + */ + REAL("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingRealType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingRealType.java new file mode 100644 index 0000000..c074140 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingRealType.java @@ -0,0 +1,31 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 物的真实类型 + */ +@Getter +@AllArgsConstructor +public enum ThingRealType { + /** + * 0虚拟 + */ + INVENTED("0"), + /** + * 1真实 + */ + REAL("1"); + + private final String value; + + + public static boolean noneMatch(String status) { + return Arrays.stream(ThingRealType.values()).noneMatch(s -> StringUtils.equals(s.getValue(), status)); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingSortType.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingSortType.java new file mode 100644 index 0000000..fb6cb6a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingSortType.java @@ -0,0 +1,10 @@ +package com.thing.common.core.enumeration; + +/** 从关系树中取出物后的排序方式 */ +public enum ThingSortType { + /** 深度优先 */ + DEPTH, + + /** 广度优先 */ + BREADTH +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatStatus.java new file mode 100644 index 0000000..2d3be6e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设备是否纳入数量统计 + */ +@Getter +@AllArgsConstructor +public enum ThingStatStatus { + /** + * 纳入统计 + */ + YES("0"), + /** + * 不纳入统计 + */ + NO("1"); + + private final String status; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatus.java new file mode 100644 index 0000000..00511d7 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/ThingStatus.java @@ -0,0 +1,55 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 设备在线离线 + */ +@Getter +@AllArgsConstructor +public enum ThingStatus { + /** + * 在线 + */ + ONLINE("1", "online","在线"), + /** + * 离线 + */ + OFFLINE("0", "offline","离线"), + /** + * 错误 + */ + ERROR("2", "error","错误"), + /** + * 未接入 + */ + NOT_CONNECTED("3", "not_connected","未接入"); + + /** + * iot_thing thingStatus + */ + private final String code; + /** + * iot_thing thingStatusKey + */ + private final String key; + + private final String name; + + public static ThingStatus getStatus(String code) { + return Arrays.stream(ThingStatus.values()) + .filter(item -> StringUtils.equals(code, item.getCode())) + .findFirst() + .orElseThrow(() -> new RuntimeException("未找到对应数据")); + } + + public static Map getNameCodeMap() { + return Arrays.stream(ThingStatus.values()).collect(Collectors.toMap(ThingStatus::getCode, ThingStatus::getName)); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TimeHorizonEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/TimeHorizonEnum.java new file mode 100644 index 0000000..6d0a4aa --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TimeHorizonEnum.java @@ -0,0 +1,69 @@ +package com.thing.common.core.enumeration; + +import com.thing.common.core.utils.DateTimeUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.tuple.Pair; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; +import java.util.Objects; + +@Getter +@AllArgsConstructor +public enum TimeHorizonEnum { + /** + * 当天 + */ + CURRENT_DAY("1", "当天"), + /** + * 24小时 + */ + LAST_HOURS("2", "24小时"), + /** + * 最近7天 + */ + LAST_DAYS("3", "最近7天"), + + /** + * 最近30天 + */ + LAST_MONTHS("4", "最近30天"); + + private final String code; + private final String desc; + + public static TimeHorizonEnum getByCode(String code){ + return Arrays.stream(TimeHorizonEnum.values()) + .filter(item -> Objects.equals(item.getCode(), code)) + .findFirst() + .orElse(null); + } + + public Pair getDateTimeRange() { + switch (this) { + case CURRENT_DAY: + return Pair.of(LocalDateTime.of(LocalDate.now(), LocalTime.MIN), LocalDateTime.of(LocalDate.now(), LocalTime.MAX)); + case LAST_HOURS: + return Pair.of(LocalDateTime.now().minusHours(24), LocalDateTime.now()); + case LAST_DAYS: + return Pair.of(LocalDateTime.now().minusDays(7), LocalDateTime.now()); + case LAST_MONTHS: + return Pair.of(LocalDateTime.now().minusDays(30), LocalDateTime.now()); + default: + return null; + } + } + + public Pair getTimestampRange() { + Pair dateTimeRange = getDateTimeRange(); + if (Objects.isNull(dateTimeRange)) { + return Pair.of(null, null); + } + return Pair.of(DateTimeUtils.parse(dateTimeRange.getLeft()), DateTimeUtils.parse(dateTimeRange.getRight())); + } + + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TimeType.java b/common/core/src/main/java/com/thing/common/core/enumeration/TimeType.java new file mode 100644 index 0000000..a66410c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TimeType.java @@ -0,0 +1,101 @@ +package com.thing.common.core.enumeration; + +import cn.hutool.core.util.ObjectUtil; + +import com.thing.common.core.exception.SysException; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import org.apache.commons.lang3.StringUtils; + +import java.time.LocalDate; +import java.time.temporal.ChronoUnit; + +/** + * 聚合函数类型 + */ +@Getter +@AllArgsConstructor +public enum TimeType { + /** + * 年 + */ + YEAR("year", "年"), + /** + * 月 + */ + MONTH("month", "月"), + /** + * 周 + */ + WEEK("week", "周"), + /** + * 天 + */ + DAY("day", "日"), + /** + * 时 + */ + HOUR("hour", "时"), + /** + * 分 + */ + MINUTE("minute", "分"), + /** + * 秒 + */ + SECOND("second", "秒"), + /** + * 任意时间 + */ + ANY("any", "任意"); + + private final String name; + private final String desc; + + public static void matchTimeRange(TimeType val, Long startTime, Long endTime, Integer interval) { + LocalDate startDate = LocalDate.ofEpochDay(startTime / (24 * 60 * 60 * 1000)); + LocalDate endDate = LocalDate.ofEpochDay(endTime / (24 * 60 * 60 * 1000)); + Long difference = null; + if(val.equals(YEAR)){ + difference = ChronoUnit.YEARS.between(startDate, endDate); + } if(val.equals(MONTH)){ + difference = ChronoUnit.MONTHS.between(startDate, endDate); + }if(val.equals(DAY)){ + difference = ChronoUnit.DAYS.between(startDate, endDate); + }if(val.equals(HOUR)){ + difference = ChronoUnit.HOURS.between(startDate, endDate); + }if(val.equals(MINUTE)){ + difference = ChronoUnit.MINUTES.between(startDate, endDate); + } + if (ObjectUtil.isNull(difference) || difference > interval) { + throw new SysException(val + "类型开始时间和结束时间已经超过间隔"); + } + } + + public static String matchIsDaySql(TimeType timeType,Integer interval) { + switch (timeType){ + case DAY, HOUR, WEEK,MINUTE, SECOND -> { + return "toUnixTimestamp(toStartOfInterval(toDateTime(vtk.ts / 1000,'Asia/Shanghai'), INTERVAL "+interval+ " " + timeType + " )) * 1000 AS ts "; + } + case YEAR,MONTH -> { + return "toUnixTimestamp(toDateTime(toStartOfInterval(toDateTime(vtk.ts / 1000,'Asia/Shanghai'), INTERVAL "+interval+ " " + timeType + " ), 'Asia/Shanghai'))*1000 as ts "; + } + default -> throw new IllegalStateException("Unexpected value: " + timeType); + } + } + + public static TimeType matchTimeType(String dataType) { + if(StringUtils.isBlank(dataType)){ + return TimeType.HOUR; + } + for (TimeType value : TimeType.values()) { + if(value.name().equalsIgnoreCase(dataType)){ + return value; + } + } + return TimeType.HOUR; + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TreeNodeStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/TreeNodeStatus.java new file mode 100644 index 0000000..75d4b3c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TreeNodeStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 检索树的状态 + */ +@Getter +@AllArgsConstructor +public enum TreeNodeStatus { + /** + * 全部 + */ + ALL("0"), + /** + * 层级 + */ + LAYERS("1"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/TriggerTypeEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/TriggerTypeEnum.java new file mode 100644 index 0000000..6b606db --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/TriggerTypeEnum.java @@ -0,0 +1,27 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 计算类型 + */ +@Getter +@AllArgsConstructor +public enum TriggerTypeEnum { + /** + * job计算 + */ + JOB("0"), + /** + * socket计算 + */ + SOCKET("1"), + /** + * 错误计算,仅job使用 + */ + ERROR("9"); + + private final String value; + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/UserStatusEnum.java b/common/core/src/main/java/com/thing/common/core/enumeration/UserStatusEnum.java new file mode 100644 index 0000000..af40bd8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/UserStatusEnum.java @@ -0,0 +1,22 @@ + + +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; + +/** + * 用户状态 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@AllArgsConstructor +public enum UserStatusEnum { + DISABLE(0), + ENABLED(1); + + private final int value; + public int value() { + return this.value; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/UsernameType.java b/common/core/src/main/java/com/thing/common/core/enumeration/UsernameType.java new file mode 100644 index 0000000..24bae56 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/UsernameType.java @@ -0,0 +1,26 @@ +//package com.thing.common.core.enumeration; +// +//import lombok.AllArgsConstructor; +//import lombok.Getter; +// +///** +// * 用户类型 +// */ +//@Getter +//@AllArgsConstructor +//public enum UsernameType { +// /** +// * 用户名 +// */ +// USERNAME("0"), +// /** +// * 电话 +// */ +// PHONE("1"), +// /** +// * 邮箱 +// */ +// EMAIL("2"); +// +// private final String type; +//} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/enumeration/menuStatus.java b/common/core/src/main/java/com/thing/common/core/enumeration/menuStatus.java new file mode 100644 index 0000000..e1fcebc --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/enumeration/menuStatus.java @@ -0,0 +1,22 @@ +package com.thing.common.core.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 是否新建菜单 + */ +@Getter +@AllArgsConstructor +public enum menuStatus { + /** + * 新增 + */ + ADD("1"), + /** + * 不新增 + */ + NO_ADD("0"); + + private final String value; +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/event/AuthParam.java b/common/core/src/main/java/com/thing/common/core/event/AuthParam.java new file mode 100644 index 0000000..56b92b8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/AuthParam.java @@ -0,0 +1,41 @@ +package com.thing.common.core.event; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.ToString; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +/** + * @author zhenghh. 2023-01-06 + **/ +@Getter +@ToString +@AllArgsConstructor +public class AuthParam implements Serializable { + + @Serial private static final long serialVersionUID = -2930891022485888530L; + + private final Long tenantCode; + private final Long companyId; + private final Long deptId; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthParam authParam = (AuthParam) o; + return tenantCode.equals(authParam.tenantCode) && companyId.equals(authParam.companyId) && deptId.equals(authParam.deptId); + } + + @Override + public int hashCode() { + return Objects.hash(tenantCode, companyId, deptId); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/CacheAlarmActionEvent.java b/common/core/src/main/java/com/thing/common/core/event/CacheAlarmActionEvent.java new file mode 100644 index 0000000..54ceac9 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/CacheAlarmActionEvent.java @@ -0,0 +1,38 @@ +package com.thing.common.core.event; + +import com.thing.common.core.enumeration.CacheEventType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-08-05 + **/ +public class CacheAlarmActionEvent extends ApplicationEvent implements CacheEvent { + + @Serial + private static final long serialVersionUID = -3968932162111798367L; + /** + * 规则主键/entityId + */ + @Getter + private final List ids; + + public CacheAlarmActionEvent(Object source, List ids) { + super(source); + this.ids = ids; + } + + /** + * 事件类型 + * + * @return type + */ + @Override + public CacheEventType getEventType() { + return CacheEventType.ALARM_ACTION_CACHE; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/event/CacheAlarmEvent.java b/common/core/src/main/java/com/thing/common/core/event/CacheAlarmEvent.java new file mode 100644 index 0000000..522d54b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/CacheAlarmEvent.java @@ -0,0 +1,37 @@ +package com.thing.common.core.event; + +import com.thing.common.core.enumeration.CacheEventType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-08-05 + **/ +public class CacheAlarmEvent extends ApplicationEvent implements CacheEvent { + + @Serial + private static final long serialVersionUID = -3968932162111798367L; + /** + * 规则主键 + */ + @Getter + private final List ids; + + public CacheAlarmEvent(Object source, List ids) { + super(source); + this.ids = ids; + } + + /** + * 事件类型 + * + * @return type + */ + @Override + public CacheEventType getEventType() { + return CacheEventType.ALARM_CACHE; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/CacheCalculationEvent.java b/common/core/src/main/java/com/thing/common/core/event/CacheCalculationEvent.java new file mode 100644 index 0000000..dd3913f --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/CacheCalculationEvent.java @@ -0,0 +1,50 @@ +package com.thing.common.core.event; + +import com.thing.common.core.enumeration.CacheEventType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-11-07 + **/ +public class CacheCalculationEvent extends ApplicationEvent implements CacheEvent { + + @Serial + private static final long serialVersionUID = -4369739585978979430L; + + @Getter + private final List list; + + public CacheCalculationEvent(Object source, List list) { + super(source); + this.list = list; + } + + /** + * 事件类型 + * + * @return type + */ + @Override + public CacheEventType getEventType() { + return CacheEventType.CALCULATION_CACHE; + } + + @Data + @AllArgsConstructor + public static class Attr { + /** + * 物编码 + */ + private String code; + /** + * 物属性编码 + */ + private String attr; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/CacheEvent.java b/common/core/src/main/java/com/thing/common/core/event/CacheEvent.java new file mode 100644 index 0000000..de7db98 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/CacheEvent.java @@ -0,0 +1,17 @@ +package com.thing.common.core.event; + + +import com.thing.common.core.enumeration.CacheEventType; + +/** + * @author zhenghh. 2022-07-22 + **/ +public interface CacheEvent { + + /** + * 事件类型 + * + * @return type + */ + CacheEventType getEventType(); +} diff --git a/common/core/src/main/java/com/thing/common/core/event/ExtensionSocketEvent.java b/common/core/src/main/java/com/thing/common/core/event/ExtensionSocketEvent.java new file mode 100644 index 0000000..aaf7c48 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/ExtensionSocketEvent.java @@ -0,0 +1,25 @@ +package com.thing.common.core.event; + +import com.alibaba.fastjson.JSONObject; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-12-08 + **/ +public class ExtensionSocketEvent extends ApplicationEvent { + + @Serial + private static final long serialVersionUID = 7221745740590396300L; + + @Getter + private final List msgList; + + public ExtensionSocketEvent(Object source, List msgList) { + super(source); + this.msgList = msgList; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/QueueAlarmEvent.java b/common/core/src/main/java/com/thing/common/core/event/QueueAlarmEvent.java new file mode 100644 index 0000000..ef78151 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/QueueAlarmEvent.java @@ -0,0 +1,34 @@ +package com.thing.common.core.event; + + +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.data.event.AbstractQueueEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-10-12 + **/ +public class QueueAlarmEvent extends AbstractQueueEvent { + + @Serial + private static final long serialVersionUID = 5582035786541576391L; + + public QueueAlarmEvent(Object source, List list) { + super(source, list); + } + + +// /** +// * 事件类型 +// * +// * @return type +// */ +// @Override +// public QueueEventType getEventType() { +// return QueueEventType.ALARM; +// } + + +} diff --git a/common/core/src/main/java/com/thing/common/core/event/QueueCacheEvent.java b/common/core/src/main/java/com/thing/common/core/event/QueueCacheEvent.java new file mode 100644 index 0000000..f290570 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/QueueCacheEvent.java @@ -0,0 +1,31 @@ +package com.thing.common.core.event; + + +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.data.event.AbstractQueueEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-10-12 + **/ +public class QueueCacheEvent extends AbstractQueueEvent { + + @Serial + private static final long serialVersionUID = 8295976378591176091L; + + public QueueCacheEvent(Object source, List list) { + super(source, list); + } +// +// /** +// * 事件类型 +// * +// * @return type +// */ +// @Override +// public QueueEventType getEventType() { +// return QueueEventType.CACHE; +// } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/QueueCalculationEvent.java b/common/core/src/main/java/com/thing/common/core/event/QueueCalculationEvent.java new file mode 100644 index 0000000..4a21b15 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/QueueCalculationEvent.java @@ -0,0 +1,31 @@ +package com.thing.common.core.event; + + +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.data.event.AbstractQueueEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-10-12 + **/ +public class QueueCalculationEvent extends AbstractQueueEvent { + + @Serial + private static final long serialVersionUID = 8467564888202925759L; + + public QueueCalculationEvent(Object source, List list) { + super(source, list); + } + +// /** +// * 事件类型 +// * +// * @return type +// */ +// @Override +// public QueueEventType getEventType() { +// return QueueEventType.CALCULATION; +// } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/QueueDeviceEvent.java b/common/core/src/main/java/com/thing/common/core/event/QueueDeviceEvent.java new file mode 100644 index 0000000..684d30b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/QueueDeviceEvent.java @@ -0,0 +1,21 @@ +package com.thing.common.core.event; + + +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.data.event.AbstractQueueEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-11-29 + **/ +public class QueueDeviceEvent extends AbstractQueueEvent { + + @Serial + private static final long serialVersionUID = 2248484319421832325L; + + public QueueDeviceEvent(Object source, List list) { + super(source, list); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/QueueSocketEvent.java b/common/core/src/main/java/com/thing/common/core/event/QueueSocketEvent.java new file mode 100644 index 0000000..69897aa --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/QueueSocketEvent.java @@ -0,0 +1,32 @@ +package com.thing.common.core.event; + + +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.data.event.AbstractQueueEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2023-03-14 + **/ +public class QueueSocketEvent extends AbstractQueueEvent { + + + @Serial + private static final long serialVersionUID = -7524682936528507783L; + + public QueueSocketEvent(Object source, List list) { + super(source, list); + } + +// /** +// * 事件类型 +// * +// * @return type +// */ +// @Override +// public QueueEventType getEventType() { +// return QueueEventType.SOCKET; +// } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/SocketClientEvent.java b/common/core/src/main/java/com/thing/common/core/event/SocketClientEvent.java new file mode 100644 index 0000000..645d8f1 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/SocketClientEvent.java @@ -0,0 +1,34 @@ +package com.thing.common.core.event; + +import com.thing.common.core.enumeration.SocketEventType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-09-20 + **/ +public class SocketClientEvent extends ApplicationEvent implements SocketEvent { + + @Serial + private static final long serialVersionUID = -3969951278335663186L; + @Getter + private final List sourceIds; + + public SocketClientEvent(Object source, List sourceIds) { + super(source); + this.sourceIds = sourceIds; + } + + /** + * 事件类型 + * + * @return type + */ + @Override + public SocketEventType getEventType() { + return SocketEventType.CLIENT; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/event/SocketEvent.java b/common/core/src/main/java/com/thing/common/core/event/SocketEvent.java new file mode 100644 index 0000000..a0d29a1 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/SocketEvent.java @@ -0,0 +1,17 @@ +package com.thing.common.core.event; + + +import com.thing.common.core.enumeration.SocketEventType; + +/** + * @author zhenghh. 2022-09-20 + **/ +public interface SocketEvent { + + /** + * 事件类型 + * + * @return type + */ + SocketEventType getEventType(); +} diff --git a/common/core/src/main/java/com/thing/common/core/event/SocketTelemetryEvent.java b/common/core/src/main/java/com/thing/common/core/event/SocketTelemetryEvent.java new file mode 100644 index 0000000..09f7f10 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/event/SocketTelemetryEvent.java @@ -0,0 +1,44 @@ +package com.thing.common.core.event; + +import com.thing.common.core.enumeration.SocketEventType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; +import java.util.List; + +/** + * @author zhenghh. 2022-09-20 + **/ + +public class SocketTelemetryEvent extends ApplicationEvent implements SocketEvent { + + @Serial + private static final long serialVersionUID = -2082830341571638797L; + /** + * 数据源主键 + */ + @Getter + private final Long sourceId; + /** + * cmdId集合 + */ + @Getter + private final List cmdIds; + + public SocketTelemetryEvent(Object source, Long sourceId, List cmdIds) { + super(source); + this.sourceId = sourceId; + this.cmdIds = cmdIds; + } + + /** + * 事件类型 + * + * @return type + */ + @Override + public SocketEventType getEventType() { + return SocketEventType.DEVICE; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/exception/ErrorCode.java b/common/core/src/main/java/com/thing/common/core/exception/ErrorCode.java new file mode 100644 index 0000000..743ad31 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/exception/ErrorCode.java @@ -0,0 +1,67 @@ +package com.thing.common.core.exception; + +/** + * 错误编码,由5位数字组成,前2位为模块编码,后3位为业务编码 + *

+ * 如:10001(10代表系统模块,001代表业务代码) + *

+ * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface ErrorCode { + int INTERNAL_SERVER_ERROR = 500; + int UNAUTHORIZED = 401; + int FORBIDDEN = 403; + + int NOT_NULL = 10001; + int DB_RECORD_EXISTS = 10002; + int PARAMS_GET_ERROR = 10003; + int ACCOUNT_PASSWORD_ERROR = 10004; + int ACCOUNT_DISABLE = 10005; + int IDENTIFIER_NOT_NULL = 10006; + int CAPTCHA_ERROR = 10007; + int SUB_MENU_EXIST = 10008; + int PASSWORD_ERROR = 10009; + int ACCOUNT_NOT_EXIST = 10010; + int SUPERIOR_DEPT_ERROR = 10011; + int SUPERIOR_MENU_ERROR = 10012; + int DATA_SCOPE_PARAMS_ERROR = 10013; + int DEPT_SUB_DELETE_ERROR = 10014; + int DEPT_USER_DELETE_ERROR = 10015; + int ACT_DEPLOY_ERROR = 10016; + int ACT_MODEL_IMG_ERROR = 10017; + int ACT_MODEL_EXPORT_ERROR = 10018; + int UPLOAD_FILE_EMPTY = 10019; + int TOKEN_NOT_EMPTY = 10020; + int TOKEN_INVALID = 10021; + int ACCOUNT_LOCK = 10022; + int ACT_DEPLOY_FORMAT_ERROR = 10023; + int OSS_UPLOAD_FILE_ERROR = 10024; + int SEND_SMS_ERROR = 10025; + int MAIL_TEMPLATE_NOT_EXISTS = 10026; + int REDIS_ERROR = 10027; + int JOB_ERROR = 10028; + int INVALID_SYMBOL = 10029; + int JSON_FORMAT_ERROR = 10030; + int SMS_CONFIG = 10031; + int ACCOUNT_EXIST = 10200; + int TASK_CLIME_FAIL = 10032; + int NONE_EXIST_PROCESS = 10033; + int SUPERIOR_NOT_EXIST = 10034; + int REJECT_MESSAGE = 10035; + int ROLLBACK_MESSAGE = 10036; + int UNCLAIM_ERROR_MESSAGE = 10037; + int SUPERIOR_REGION_ERROR = 10038; + int REGION_SUB_DELETE_ERROR = 10039; + int PROCESS_START_ERROR = 10040; + int REJECT_PROCESS_PARALLEL_ERROR = 10041; + int REJECT_PROCESS_HANDLEING_ERROR = 10042; + int END_PROCESS_PARALLEL_ERROR = 10043; + int END_PROCESS_HANDLEING_ERROR = 10044; + int END_PROCESS_MESSAGE = 10045; + int BACK_PROCESS_PARALLEL_ERROR = 10046; + int BACK_PROCESS_HANDLEING_ERROR = 10047; + int DEL_MYSELF_ERROR = 10048; + int NOT_OK_CODE = 104; +} diff --git a/common/core/src/main/java/com/thing/common/core/exception/ExceptionUtils.java b/common/core/src/main/java/com/thing/common/core/exception/ExceptionUtils.java new file mode 100644 index 0000000..94ed701 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/exception/ExceptionUtils.java @@ -0,0 +1,47 @@ + + +package com.thing.common.core.exception; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Exception工具类 + * + * @author Mark sunlightcs@gmail.com + */ +public class ExceptionUtils { + + /** + * 获取异常信息 + * @param ex 异常 + * @return 返回异常信息 + */ + public static String getErrorStackTrace(Exception ex){ + StringWriter sw = null; + PrintWriter pw = null; + try { + sw = new StringWriter(); + pw = new PrintWriter(sw, true); + ex.printStackTrace(pw); + }finally { + try { + if(pw != null) { + pw.close(); + } + } catch (Exception e) { + + } + try { + if(sw != null) { + sw.close(); + } + } catch (IOException e) { + + } + } + + return sw.toString(); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/exception/GlobalExceptionHandler.java b/common/core/src/main/java/com/thing/common/core/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..d62f077 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/exception/GlobalExceptionHandler.java @@ -0,0 +1,37 @@ +package com.thing.common.core.exception; + +import com.thing.common.core.web.response.Result; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.validation.BindingResult; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +/** + * @author ls + */ +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + /** + * 处理校验不通过异常的异常 + */ + @ExceptionHandler(value = MethodArgumentNotValidException.class) + public Result notValidExceptionHandler(MethodArgumentNotValidException exception){ + BindingResult bindingResult = exception.getBindingResult(); + StringBuilder builder = new StringBuilder(); + for (FieldError error : bindingResult.getFieldErrors()) { + builder.append(error.getDefaultMessage()); + builder.append(";"); + } + Result result = new Result<>(); + result.setCode(500); + result.setMsg(builder.toString()); + return result; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/exception/SysException.java b/common/core/src/main/java/com/thing/common/core/exception/SysException.java new file mode 100644 index 0000000..2b7c5b1 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/exception/SysException.java @@ -0,0 +1,64 @@ +package com.thing.common.core.exception; + + +import com.thing.common.core.utils.MessageUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 自定义异常 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SysException extends RuntimeException { + @Serial + private static final long serialVersionUID = 1L; + + private int code; + private String msg; + + public SysException(int code) { + this.code = code; + this.msg = MessageUtils.getMessage(code); + } + + public SysException(int code, String... params) { + this.code = code; + this.msg = MessageUtils.getMessage(code, params); + } + + public SysException(int code, Throwable e) { + super(e); + this.code = code; + this.msg = MessageUtils.getMessage(code); + } + + public SysException(int code, Throwable e, String... params) { + super(e); + this.code = code; + this.msg = MessageUtils.getMessage(code, params); + } + + public SysException(String msg) { + super(msg); + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + } + + public SysException(String msg, int code) { + super(msg); + this.code = code; + this.msg = msg; + } + + public SysException(String msg, Throwable e) { + super(msg, e); + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/expression/functions/AggFunction.java b/common/core/src/main/java/com/thing/common/core/expression/functions/AggFunction.java new file mode 100644 index 0000000..f8e042e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/expression/functions/AggFunction.java @@ -0,0 +1,151 @@ +package com.thing.common.core.expression.functions; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import com.googlecode.aviator.runtime.function.AbstractFunction; +import com.googlecode.aviator.runtime.type.AviatorDecimal; +import com.googlecode.aviator.runtime.type.AviatorObject; +import com.thing.common.core.enumeration.AggType; + +import lombok.AllArgsConstructor; +import lombok.NoArgsConstructor; + +import org.apache.commons.lang3.function.TriFunction; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-12 + * @description 自定义聚合函数 + */ +@NoArgsConstructor +@AllArgsConstructor +@SuppressWarnings("unused") +public class AggFunction extends AbstractFunction { + + + private String name; + + /** + * 获取指定时间段内,指定变量的聚合函数值
+ * 第一个参数: 变量集合
+ * 第二个参数: 聚合函数开始时间的描述
+ * 第三个参数: 聚合函数结束时间的描述
+ */ + private TriFunction, String, String, BigDecimal> fetchData; + + @Override + public String getName() { + return this.name; + } + + /** + * 关于 startObj 和 endObj,该方法不对这两个描述直接转换成实际的时间戳,因为考虑到如下情况:
+ * 1. fetchData 并不直接查询数据库(如果每个函数都查库,会造成极大的IO压力),只是象征意义得使用了开始时间和结束时间的描述
+ * 2. fetchData 关心的只是时间描述的范围,而其代表的实际时间可能在调用时有差异,后期若需要对时间分组,则这些差异会造成困扰
+ * 3. 保留 startObj 和 endObj 的变量名可以按照时间描述来分组,然后在同一组内进行查询数据库时再转成具体的时间戳 + * + * @param env 变量-值的map + * @param arg 当前函数中变量 + * @param startObj 开始时间描述对象 + * @param endObj 结束时间描述对象 + * @return 在这段时间内被计算出的聚合结果 + */ + @Override + public AviatorObject call( + Map env, + AviatorObject arg, + AviatorObject startObj, + AviatorObject endObj) { + Set vars = getVars(arg); + BigDecimal data = fetchData.apply(vars, getArgName(startObj), getArgName(endObj)); + return new AviatorDecimal(data); + } + + @Override + public AviatorObject call( + Map env, + AviatorObject arg1, + AviatorObject arg2, + AviatorObject startObj, + AviatorObject endObj) { + Set vars = getVars(arg1, arg2); + BigDecimal data = fetchData.apply(vars, getArgName(startObj), getArgName(endObj)); + return new AviatorDecimal(data); + } + + @Override + public AviatorObject call( + Map env, + AviatorObject arg1, + AviatorObject arg2, + AviatorObject arg3, + AviatorObject startObj, + AviatorObject endObj) { + Set vars = getVars(arg1, arg2, arg3); + BigDecimal data = fetchData.apply(vars, getArgName(startObj), getArgName(endObj)); + return new AviatorDecimal(data); + } + + @Override + public AviatorObject call( + Map env, + AviatorObject arg1, + AviatorObject arg2, + AviatorObject arg3, + AviatorObject arg4, + AviatorObject startObj, + AviatorObject endObj) { + Set vars = getVars(arg1, arg2, arg3, arg4); + BigDecimal data = fetchData.apply(vars, getArgName(startObj), getArgName(endObj)); + return new AviatorDecimal(data); + } + + private Set getVars(AviatorObject... args) { + return Arrays.stream(args).map(this::getArgName).collect(Collectors.toSet()); + } + + private String getArgName(AviatorObject arg) { + return arg.toString().split(",")[1].trim(); + } + + public static BigDecimal doSUM(BigDecimal... values) { + if (values.length == 0) { + return BigDecimal.ZERO; + } + return Arrays.stream(values).reduce(BigDecimal.ZERO, BigDecimal::add); + } + + public static BigDecimal doAVG(BigDecimal... values) { + if (values.length == 0) { + return BigDecimal.ZERO; + } + return Arrays.stream(values) + .reduce(BigDecimal.ZERO, BigDecimal::add) + .divide(new BigDecimal(values.length), 2, RoundingMode.HALF_UP); + } + + /** 使用示例 */ + public static void main(String[] args) { + AggFunction aggFunction = + new AggFunction( + AggType.SUM.name(), + (varSet, startTimeDesc, endTimeDesc) -> { + // 根据变量集合与时间范围描述,获取聚合值 + // 如果有多个变量,可以调用 doSUM、doAVG进行处理 + return BigDecimal.ONE; + }); + AviatorEvaluator.addFunction(aggFunction); + Expression compiledExp = + AviatorEvaluator.compile("A + B + SUM(B, C, $5_min_before, $now) > 5"); + Map variableMap = new HashMap<>(); + variableMap.put("A", 3); + variableMap.put("B", 2); + Object execute = compiledExp.execute(variableMap); + System.out.println(execute); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/expression/package-info.java b/common/core/src/main/java/com/thing/common/core/expression/package-info.java new file mode 100644 index 0000000..590eb9c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/expression/package-info.java @@ -0,0 +1,6 @@ +/** + * @author siyang + * @date 2024-03-12 + * @description 该目录用于便携解析引擎 aviator 中自定义函数,以及处理这些函数 + */ +package com.thing.common.core.expression; \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/expression/parser/AggFormulaParser.java b/common/core/src/main/java/com/thing/common/core/expression/parser/AggFormulaParser.java new file mode 100644 index 0000000..94a872c --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/expression/parser/AggFormulaParser.java @@ -0,0 +1,53 @@ +package com.thing.common.core.expression.parser; + +import com.thing.common.core.enumeration.AggType; + +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author siyang + * @date 2024-03-13 + * @description 聚合函数解析器 + */ +public class AggFormulaParser { + + private static final String AGG_REGEX = "(SUM|AVG|MAX|MIN|COUNT)"; + private static final Pattern AGG_REGEX_PATTERN = Pattern.compile(AGG_REGEX); + + private static final String NOW_TIME = "$now"; + private static final String AGG_FUN_BEFORE_TIME_REGEX = "\\d+"; + private static final Pattern AGG_FUN_BEFORE_TIME_REGEX_PATTERN = + Pattern.compile(AGG_FUN_BEFORE_TIME_REGEX); + + /** + * 获取公式中的聚合函数名称 + * + * @param formula 公式 + * @return 聚合函数名称集合 + */ + public static Set getAggTypes(String formula) { + Set aggTypes = new HashSet<>(); + Matcher matcher = AGG_REGEX_PATTERN.matcher(formula); + while (matcher.find()) { + aggTypes.add(AggType.match(matcher.group(1))); + } + return aggTypes; + } + + public static Long parseTimeDesc(String timeDesc) { + long now = System.currentTimeMillis(); + if (NOW_TIME.equals(timeDesc)) { + return now; + } + Matcher matcher = AGG_FUN_BEFORE_TIME_REGEX_PATTERN.matcher(timeDesc); + if (matcher.find()) { + String minutes = matcher.group(); + return now - Long.parseLong(minutes) * 60 * 1000; + } + return 0L; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/initializer/AbstractAppInitializer.java b/common/core/src/main/java/com/thing/common/core/initializer/AbstractAppInitializer.java new file mode 100644 index 0000000..92d5f33 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/initializer/AbstractAppInitializer.java @@ -0,0 +1,15 @@ +package com.thing.common.core.initializer; + +/** + * @author siyang + * @date 2024-03-12 + * @description 系统业务行为初始化器 + */ +public abstract class AbstractAppInitializer { + + /** 初始化行为名称 */ + public abstract String getActionName(); + + /** 项目启动时执行初始化 */ + public abstract void executeInit(); +} diff --git a/common/core/src/main/java/com/thing/common/core/initializer/AppInitializeExecutor.java b/common/core/src/main/java/com/thing/common/core/initializer/AppInitializeExecutor.java new file mode 100644 index 0000000..1454f68 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/initializer/AppInitializeExecutor.java @@ -0,0 +1,39 @@ +package com.thing.common.core.initializer; + +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-12 + * @description 系统业务行为初始化器执行者 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AppInitializeExecutor implements ApplicationListener { + + private final List appInitializers; + + @Override + public void onApplicationEvent(@NonNull ApplicationReadyEvent event) { + if (CollectionUtils.isEmpty(appInitializers)) { + return; + } + + // 初始化 + appInitializers.forEach( + e -> { + log.info("开始执行: {}", e.getActionName()); + e.executeInit(); + }); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/message/MessageData.java b/common/core/src/main/java/com/thing/common/core/message/MessageData.java new file mode 100644 index 0000000..21b7a46 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/message/MessageData.java @@ -0,0 +1,35 @@ +package com.thing.common.core.message; + +import lombok.Data; + +/** + * 响应客户端数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class MessageData { + /** + * 编码 0:文本消息 1:对象消息 + */ + private int type = 0; + /** + * 文本消息 + */ + private String msg; + /** + * 对象消息 + */ + private T data; + + public MessageData data(T data) { + this.setData(data); + this.type = 1; + return this; + } + + public MessageData msg(String msg) { + this.msg = msg; + return this; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/rest/dto/ApiRequest.java b/common/core/src/main/java/com/thing/common/core/rest/dto/ApiRequest.java new file mode 100644 index 0000000..ee4ee7a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/rest/dto/ApiRequest.java @@ -0,0 +1,23 @@ +package com.thing.common.core.rest.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; + +/** + * @author SiYang + * @date 2024/05/07 15:53 + * @description api请求 + */ +@Data +public abstract class ApiRequest implements Serializable { + @Serial private static final long serialVersionUID = 5771489578863815831L; + + private String url; + + public abstract Map getHeader(); + + public abstract Map getBody(); +} diff --git a/common/core/src/main/java/com/thing/common/core/rest/dto/ApiResponse.java b/common/core/src/main/java/com/thing/common/core/rest/dto/ApiResponse.java new file mode 100644 index 0000000..d773fc5 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/rest/dto/ApiResponse.java @@ -0,0 +1,32 @@ +package com.thing.common.core.rest.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +/** + * @author SiYang + * @date 2024/05/07 15:53 + * @description api响应
+ * 1. 定义api接口返回字段中最常见的 msg, code 属性,以及可能与其相关的failed判定方法
+ * 2. 其余字段、数据结构由子类详细描述 + */ +@Data +public abstract class ApiResponse implements Serializable { + @Serial private static final long serialVersionUID = 8417144156370612454L; + + private String msg; + + private String code; + + public abstract boolean failed(); + + public Integer getCodeNumber() { + if (Objects.isNull(code)) { + return 400; + } + return Integer.parseInt(code); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/rest/service/ApiService.java b/common/core/src/main/java/com/thing/common/core/rest/service/ApiService.java new file mode 100644 index 0000000..9fbde4e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/rest/service/ApiService.java @@ -0,0 +1,24 @@ +package com.thing.common.core.rest.service; + +import com.thing.common.core.rest.dto.ApiRequest; +import com.thing.common.core.rest.dto.ApiResponse; + +import org.springframework.http.HttpMethod; + +/** + * @author SiYang + * @date 2024/05/02 15:53 + * @description api调度的基础服务接口 + */ +public interface ApiService { + + /** + * 调度第三方接口 + * + * @param apiRequest api请求参数 + * @param httpMethod http请求方法 + * @param responseType 期望的返回类型 + * @return response + */ + T apiCall(ApiRequest apiRequest, HttpMethod httpMethod, Class responseType); +} diff --git a/common/core/src/main/java/com/thing/common/core/rest/service/ApiServiceImpl.java b/common/core/src/main/java/com/thing/common/core/rest/service/ApiServiceImpl.java new file mode 100644 index 0000000..8f081e8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/rest/service/ApiServiceImpl.java @@ -0,0 +1,141 @@ +package com.thing.common.core.rest.service; + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.rest.dto.ApiRequest; +import com.thing.common.core.rest.dto.ApiResponse; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.http.*; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * @author SiYang + * @date 2024/05/08 09:07 + * @description api调度的基础服务接口实现 + */ +@Slf4j +@Service +public class ApiServiceImpl implements ApiService { + String CONTENT_TYPE = "Content-Type"; + + private final RestTemplate restTemplate = new RestTemplate(); + + /** + * 调用第三方api + * + * @param apiRequest 请求参数 + * @param responseType 返回类型定义 + * @return 响应 + */ + public T apiCall(ApiRequest apiRequest, HttpMethod httpMethod, Class responseType) { + ResponseEntity response = null; + String responseBody = null; + try { + response = executeCall(apiRequest, httpMethod); + responseBody = response.getBody(); + return JSONObject.parseObject(responseBody, responseType); + } catch (Exception e) { + try { + T result = responseType.getDeclaredConstructor().newInstance(); + result.setCode(""); + if (Objects.isNull(response)) { + result.setMsg("api null response"); + } else if (Objects.isNull(responseBody)) { + result.setMsg("api null response body"); + } else { + result.setMsg("error msg: " + e + ", response body: " + responseBody); + } + return result; + } catch (Exception e2) { + log.error("api fetch error: ", e2); + } + } + return null; + } + + private ResponseEntity executeCall(ApiRequest apiRequest, HttpMethod httpMethod) { + Map body = apiRequest.getBody(); + + // 初始化uri + UriComponents uriComponents = initUriComponents(apiRequest, body, httpMethod); + + // 初始化请求头 + HttpHeaders headers = initHttpHeaders(apiRequest); + + // 组装http请求 + HttpEntity> httpEntity = + new HttpEntity<>( + (Objects.equals(headers.getContentType(), MediaType.APPLICATION_JSON) + ? body + : convertMap(body)), + headers); + + // 设置字符集为UTF8 + setDefaultChartSet(); + + // 执行请求 + return restTemplate.exchange(uriComponents.toUri(), httpMethod, httpEntity, String.class); + } + + private void setDefaultChartSet() { + List> httpMessageConverters = restTemplate.getMessageConverters(); + httpMessageConverters.forEach( + httpMessageConverter -> { + if (httpMessageConverter + instanceof StringHttpMessageConverter messageConverter) { + messageConverter.setDefaultCharset(StandardCharsets.UTF_8); + } + }); + } + + private UriComponents initUriComponents( + ApiRequest apiRequest, Map body, HttpMethod httpMethod) { + UriComponentsBuilder uriBuilder = UriComponentsBuilder.fromHttpUrl(apiRequest.getUrl()); + if (HttpMethod.GET.equals(httpMethod)) { + uriBuilder.queryParams(convertMap(body)); + } + return uriBuilder.build(); + } + + private HttpHeaders initHttpHeaders(ApiRequest apiRequest) { + HttpHeaders headers = new HttpHeaders(); + Map headerMap = + Optional.ofNullable(apiRequest.getHeader()).orElse(new HashMap<>()); + Object contentType = headerMap.get(CONTENT_TYPE); + if (Objects.nonNull(contentType)) { + if (contentType instanceof MediaType) { + headers.setContentType((MediaType) contentType); + } else { + headers.setContentType(MediaType.parseMediaType(contentType.toString())); + } + headerMap.remove(CONTENT_TYPE); + } else { + headers.setContentType(MediaType.APPLICATION_JSON); + } + headerMap.forEach((k, v) -> headers.add(k, v.toString())); + return headers; + } + + private MultiValueMap convertMap(Map params) { + MultiValueMap map = new LinkedMultiValueMap<>(); + if (Objects.isNull(params)) { + return map; + } + params.forEach( + (k, v) -> map.set(k, URLEncoder.encode(String.valueOf(v), StandardCharsets.UTF_8))); + return map; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/AggUtil.java b/common/core/src/main/java/com/thing/common/core/utils/AggUtil.java new file mode 100644 index 0000000..edb6806 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/AggUtil.java @@ -0,0 +1,48 @@ +package com.thing.common.core.utils; + +import java.math.BigDecimal; +import java.util.Objects; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/7/17 14:30 + * @description 聚合计算工具 + */ +public class AggUtil { + /** + * 忽略null值累加数据 + * + * @param values 数据值的数组 + * @return 累加结果 + */ + public static BigDecimal sum(BigDecimal... values) { + BigDecimal res = null; + for (BigDecimal value : values) { + if (Objects.nonNull(value)) { + res = Optional.ofNullable(res).orElse(BigDecimal.ZERO).add(value); + } + } + return res; + } + + public static Long sum(Long... values) { + Long res = null; + for (Long value : values) { + if (Objects.nonNull(value)) { + res = Optional.ofNullable(res).orElse(0L) + value; + } + } + return res; + } + + public static Integer sum(Integer... values) { + Integer res = null; + for (Integer value : values) { + if (Objects.nonNull(value)) { + res = Optional.ofNullable(res).orElse(0) + value; + } + } + return res; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/BizUtils.java b/common/core/src/main/java/com/thing/common/core/utils/BizUtils.java new file mode 100644 index 0000000..247ceed --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/BizUtils.java @@ -0,0 +1,108 @@ +package com.thing.common.core.utils; + +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.HexUtil; +import cn.hutool.core.util.StrUtil; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.ProtocolPrefix; +import org.apache.commons.lang3.StringUtils; + +import java.nio.charset.Charset; +import java.util.Random; + +public class BizUtils { + + + /** + * 将字符串转换为十六进制字符串,结果为大写 + * + * @param data 需要被编码的字符串 + * @param charset 编码 + * @return 十六进制String + */ + public static String encodeHexStr(String data, Charset charset) { + return HexUtil.encodeHexStr(StrUtil.bytes(data, charset), Boolean.FALSE); + } + + /** + * 将字符串转换为十六进制字符串,结果为大写,默认编码是UTF-8 + *` + * @param data 被编码的字符串 + * @return 十六进制String + */ + public static String encodeHexStr(String data) { + return encodeHexStr(data, CharsetUtil.CHARSET_UTF_8); + } + + /** + * 获取带协议前缀的url,如果已经带了协议则不做处理 + * @param url 配置的url + * @return + */ + public static String transUrl(String url) { + return trimEnd(StringUtils.contains(url, Constant.PROTOCOL_SEPARATOR) + ? url : ProtocolPrefix.HTTP.getPrefix() + url, "/"); + } + + + /** + * 获取带websocket前缀的url,如果带了协议且不是ws,强制转换为ws + * @param url 配置的url + * @return + */ + public static String transWSUrl(String url) { + if(StringUtils.isBlank(url) + || StringUtils.startsWith(url, ProtocolPrefix.WS.getPrefix())){ return url; } + return trimEnd(ProtocolPrefix.WS.getPrefix() + (StringUtils.contains(url, Constant.PROTOCOL_SEPARATOR) + ? url.split(Constant.PROTOCOL_SEPARATOR)[1] : url), "/"); + } + + /** + * 获取带websocket前缀的url,如果带了协议且不是ws,强制转换为ws + * @param url 配置的url + * @param port tcp端口 + * @return url + */ + public static String transTcpUrl(String url, String port) { + if(StringUtils.isBlank(url) || StringUtils.startsWith(url, ProtocolPrefix.TCP.getPrefix())){ return url; } + String uri = trimEnd(StringUtils.contains(url, Constant.PROTOCOL_SEPARATOR) ? url.split(Constant.PROTOCOL_SEPARATOR)[1] : url, "/"); + return ProtocolPrefix.TCP.getPrefix() + (StringUtils.contains(uri, Constant.COLON) ? uri.split(Constant.COLON)[0] : uri) + Constant.COLON + port; + } + /** + * 删掉字符串结尾的指定字符 + * @param str 处理的字符串 + * @param suffix + * @return + */ + public static String trimEnd(String str, String suffix) { + if(StringUtils.isBlank(str) || StringUtils.isBlank(suffix)) return str; + return str.endsWith(suffix) ? str.substring(0, str.length() - suffix.length()) : str; + } + + /** + * 随机clientId + * + * @param prefix 前缀 + * @return clientId + */ + public static String getClientId(String prefix) { + Random random = new Random(); + StringBuilder clientId = new StringBuilder(prefix); + String[] options = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".split(""); + for (int i = 0; i < 8; i++) { + clientId.append(options[random.nextInt(options.length)]); + } + return clientId.toString(); + } + + /** + * 移除所有空格 + * + * @param str 字符串 + * @return 字符串 + */ + public static String trimAll(String str) { + return StringUtils.join(StringUtils.split(str)); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/CompareUtils.java b/common/core/src/main/java/com/thing/common/core/utils/CompareUtils.java new file mode 100644 index 0000000..277c346 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/CompareUtils.java @@ -0,0 +1,20 @@ +package com.thing.common.core.utils; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.constants.Constant; +import org.apache.commons.lang3.StringUtils; + +import java.util.Comparator; + +public class CompareUtils { + + + public static Comparator getComparator(String order, String finalOrderField) { + Comparator comparator = Comparator.comparingLong(obj -> obj.get(finalOrderField).asLong()); + if (StringUtils.isBlank(order) || StringUtils.equalsIgnoreCase(order, Constant.DESC)) { + comparator = comparator.reversed(); + } + return comparator; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/ConvertUtils.java b/common/core/src/main/java/com/thing/common/core/utils/ConvertUtils.java new file mode 100644 index 0000000..c4b8999 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/ConvertUtils.java @@ -0,0 +1,124 @@ +package com.thing.common.core.utils; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.poi.ss.formula.functions.T; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeanUtils; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 转换工具类 + * + * @author Mark sunlightcs@gmail.com + */ +public class ConvertUtils { + private static final Logger logger = LoggerFactory.getLogger(ConvertUtils.class); + + public static T sourceToTarget(Object source, Class target, TypeConvertEnum... convertEnums) { + if (source == null) { + return null; + } + T targetObject = null; + try { + targetObject = target.getDeclaredConstructor().newInstance(); + // copy 同名,同类型的属性 [基于属性的getter,setter方法进行copy] + BeanUtils.copyProperties(source, targetObject); + if (convertEnums.length == 0) { + return targetObject; + } + + // copy 同名,不同类型的属性 [基于同名的属性进行属性类型和值的转化] + Map sourceFieldMap = getInheritanceDeclaredFields(source.getClass()); + Map targetFieldMap = getInheritanceDeclaredFields(target); + for (String fieldName : targetFieldMap.keySet()) { + Field sourceField = sourceFieldMap.get(fieldName); + Field targetField = targetFieldMap.get(fieldName); + if(ObjectUtils.isEmpty(sourceField)||ObjectUtils.isEmpty(targetField)){ + continue; + } + for (TypeConvertEnum convertEnum : convertEnums) { + if(isSameType(sourceField, targetField)){ + continue; + } + convertEnum.doConvert(sourceField, targetField, source, targetObject); + } + } + } catch (Exception e) { + logger.error("convert error ", e); + } + + return targetObject; + } + + public static List sourceToTarget(Collection sourceList, Class target, TypeConvertEnum... convertEnums) { + if (sourceList == null) { + return null; + } + return sourceList.stream() + .map(s -> sourceToTarget(s, target, convertEnums)) + .collect(Collectors.toList()); + } + + /** + * copy属性的同时,会将同名但不同类型的属性也进行转换 + */ + public static T convertWithTypeAdapt(Object source, Class target){ + return sourceToTarget(source, target, TypeConvertEnum.values()); + } + + public static List convertWithTypeAdapt(Collection sourceList, Class target) { + if (sourceList == null) { + return null; + } + return sourceList.stream() + .map(s -> convertWithTypeAdapt(s, target)) + .collect(Collectors.toList()); + } + + private static Map getInheritanceDeclaredFields(Class clazz) { + return getInheritanceDeclaredFields(clazz, new HashMap<>()); + } + + private static Map getInheritanceDeclaredFields(Class clazz, Map fieldMap) { + Field[] selfFields = clazz.getDeclaredFields(); + for (Field selfField : selfFields) { + fieldMap.putIfAbsent(selfField.getName(), selfField); + } + Class superclass = clazz.getSuperclass(); + if (superclass != Object.class) { + getInheritanceDeclaredFields(superclass, fieldMap); + } + return fieldMap; + } + + private static boolean isSameType(Field sourceField, Field targetField) { + return sourceField.getType().equals(targetField.getType()); + } + + /** + * 由于数据结构变动,部分entity的时间字段由date修改为了long + * 因此需要做额外的适配,否则dto的时间字段可能没有值 + */ + public enum TypeConvertEnum { + Long_to_Date { + @Override + void doConvert(Field sourceField, Field targetField, Object source, Object target) + throws IllegalAccessException { + if (sourceField.getType().equals(Long.class) + && targetField.getType().equals(Date.class)) { + targetField.setAccessible(true); + sourceField.setAccessible(true); + targetField.set(target, new Date((long) sourceField.get(source))); + } + } + }; + + abstract void doConvert(Field sourceField, Field targetField, Object source, Object target) + throws IllegalAccessException; + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/utils/DataTypeUtils.java b/common/core/src/main/java/com/thing/common/core/utils/DataTypeUtils.java new file mode 100644 index 0000000..c230755 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/DataTypeUtils.java @@ -0,0 +1,192 @@ +package com.thing.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; + +/** + * 数据类型判断工具类 + */ +public class DataTypeUtils { + + public static final String INTEGER = "integer"; + public static final String DOUBLE = "double"; + public static final String JSON = "json"; + public static final String BOOLEAN = "boolean"; + public static final String STRING = "string"; + + /** + * 目标类型校验 + * @param type 需要校验的类型 + * @param value 需要校验的值 + * @return + */ + public static Boolean isNotTargeType(String type, String value){ + return !isTargeType(type, value); + } + + /** + * 目标类型校验 + * @param type 需要校验的类型 + * @param value 需要校验的值 + * @return + */ + public static Boolean isTargeType(String type, String value){ + if(StringUtils.isBlank(type)||StringUtils.isBlank(value)){ + return Boolean.FALSE; + } + switch (type){ + case INTEGER: + return isInteger(value); + case DOUBLE: + return isDouble(value); + case JSON: + return isJsonObject(value)||isJsonArray(value); + case BOOLEAN: + return isBoolean(value)||StringUtils.equals("1", value)||StringUtils.equals("0", value); + case STRING: + return Boolean.TRUE; + default: + return Boolean.FALSE; + } + + } + + + /** + * 字符串是否是整数 + * @param value + * @return + */ + public static Boolean isInteger(String value){ + try{ + Integer.parseInt(value); + }catch (Exception e){ + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + + /** + * 字符串是否是小数 + * @param value + * @return + */ + public static Boolean isDouble(String value){ + try{ + Double.parseDouble(value); + }catch (Exception e){ + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + + /** + * 字符串是否是布尔值 支持1、0或者true、false + * @param value + * @return + */ + public static Boolean isBoolean(String value){ + return StringUtils.equalsIgnoreCase("true",value)||StringUtils.equalsIgnoreCase("false",value); + } + + + /** + * 是否是数字 + * @param value + * @return + */ + public static Boolean isNumeric(String value){ + try{ + new BigDecimal(value); + }catch (Exception e){ + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + /** + * 是否JSON对象 + * @param value + * @return + */ + public static Boolean isJsonObject(String value){ + try{ + JSONObject.parseObject(value); + }catch (Exception e){ + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + /** + * 是否JSON数组 + * @param value + * @return + */ + public static Boolean isJsonArray(String value){ + try{ + JSONArray.parseArray(value); + }catch (Exception e){ + return Boolean.FALSE; + } + return Boolean.TRUE; + } + + + /** + * 还原数据到原来的类型 + * @param valueType + * @param value + * @return + */ + public static Object restoreValue(String valueType, String value) { + + if(StringUtils.equals(valueType, DataTypeUtils.STRING)){ + return ObjectUtil.isNotNull(value)? value : null; + } + if(StringUtils.equals(valueType, DataTypeUtils.INTEGER)){ + return ObjectUtil.isNotNull(value)? Integer.valueOf(value) : null; + } + if(StringUtils.equals(valueType, DataTypeUtils.DOUBLE)){ + return ObjectUtil.isNotNull(value)? Double.valueOf(value) : null; + } + if(StringUtils.equals(valueType, DataTypeUtils.JSON)){ + if(DataTypeUtils.isJsonObject(value)){ + return JSONObject.parseObject(value); + } + if(DataTypeUtils.isJsonArray(value)){ + return JSONObject.parseArray(value); + } + } + if(StringUtils.equals(valueType, DataTypeUtils.BOOLEAN)){ + return Boolean.valueOf(value) || StringUtils.equals("1", value); + } + return null; + } + + + /** + * 校验字符串是否为合法的uuid + * + * @param uuidStr uuid字符串 + * @return 合法:true, 不合法: false + */ + public static Boolean isUUID(String uuidStr) { + if (StringUtils.isBlank(uuidStr)) { + return Boolean.FALSE; + } + try { + cn.hutool.core.lang.UUID.fromString(uuidStr); + return Boolean.TRUE; + } catch (Exception ex) { + return Boolean.FALSE; + } + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/DateTimeUtils.java b/common/core/src/main/java/com/thing/common/core/utils/DateTimeUtils.java new file mode 100644 index 0000000..5ff6532 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/DateTimeUtils.java @@ -0,0 +1,1492 @@ +package com.thing.common.core.utils; + +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.TimeHorizonEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.params.SplitedTimeParam; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.joda.time.DateTime; +import org.joda.time.format.DateTimeFormat; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAdjuster; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * @author zhenghh. 2022-05-07 + **/ +@Slf4j +public class DateTimeUtils { + + /** + * UTC +8 + */ + public final static ZoneId ZONE_ID = ZoneId.of("+8"); + public final static ZoneOffset ZONE_OFFSET = ZoneOffset.ofHours(8); + /** + * 时间格式(yyyy) + */ + public final static String YEAR_PATTERN = "yyyy"; + /** + * 时间格式(yyyy-MM) + */ + public final static String MONTH_PATTERN = "yyyy-MM"; + public final static String YEAR_MONTH_PATTERN_STR = "yyyyMM"; + public final static String DATE_PATTERN_STR = "yyyy-MM-dd"; + public final static String DATE_TIME_PATTERN_STR = "yyyy-MM-dd HH:mm:ss"; + public final static String DATE_TIME_PATTERN_X_STR = "yyyy/MM/dd HH:mm:ss"; + + /** + * 时间格式(yyyy-MM-dd) + */ + public final static DateTimeFormatter YEAR_MONTH_PATTERN = DateTimeFormatter.ofPattern(YEAR_MONTH_PATTERN_STR); + /** + * 时间格式(yyyy-MM-dd) + */ + public final static DateTimeFormatter DATE_PATTERN = DateTimeFormatter.ofPattern(DATE_PATTERN_STR); + /** + * 时间格式(yyyy-MM-dd HH:mm:ss) + */ + public static final DateTimeFormatter DATE_TIME_PATTERN = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_STR); + + /** + * 时间格式(yyyy-MM-dd HH:mm:ss) + */ + public static final DateTimeFormatter DATE_TIME_PATTERN_X = DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_X_STR); + public final static DateTimeFormatter YEAR_MONTH_PATTERN01 = DateTimeFormatter.ofPattern("yyyy-MM"); + + public static String MONTH_BEGIN_TIME = "0"; + public static String DAY_BEGIN_TIME = "1"; + + public static String MONTH_END_TIME = "0"; + public static String DAY_END_TIME = "1"; + /** + * 时间戳转分区日期时间 + * + * @param localDateTime 时间 + * @return 时间戳 + */ + public static long parse(LocalDateTime localDateTime) { + Assert.notNull(localDateTime, "时间不能为空"); + return localDateTime.toInstant(ZONE_OFFSET).toEpochMilli(); + } + + public static Date toDate(LocalDateTime dateTime){ + return Date.from(dateTime.atZone(ZONE_ID).toInstant()); + } + + /** + * 字符串转换成日期 + * @param strDate 日期字符串 + * @param pattern 日期的格式,如:DateUtils.DATE_TIME_PATTERN + */ + public static Date stringToDate(String strDate, String pattern) { + if (StringUtils.isBlank(strDate)){ + return null; + } + + org.joda.time.format.DateTimeFormatter fmt = DateTimeFormat.forPattern(pattern); + return fmt.parseLocalDateTime(strDate).toDate(); + } + + /** + * 时间戳转分区日期时间 + * + * @param timestamp 时间戳 + * @return ZonedDateTime + */ + public static ZonedDateTime parse(Long timestamp) { + Assert.notNull(timestamp, "时间戳不能为空"); + return Instant.ofEpochMilli(timestamp).atZone(ZONE_ID); + } + + /** + * 毫秒时间戳转时间 + * + * @param timestamp 毫秒时间戳 + * @return LocalDateTime + */ + public static LocalDateTime parseDateTime(Long timestamp) { + return parse(timestamp).toLocalDateTime(); + } + + + /** + * 时间字符串转分区日期时间 + * + * @param date 时间 2021-10-10 10:10:10 + * @return ZonedDateTime + */ + public static LocalDateTime parseDateTime(String date) { + Assert.notNull(date, "时间不能为空"); + return LocalDateTime.parse(date, DATE_TIME_PATTERN); + } + + /** + * 毫秒时间戳转时间 + * + * @param timestamp 毫秒时间戳 + * @return LocalDate + */ + public static LocalDate parseDate(Long timestamp) { + return parse(timestamp).toLocalDate(); + } + + /** + * 时间格式化 + * + * @param currentTime 当前时间戳 + * @param localTime 时间 + * @return LocalDateTime + */ + public static LocalDateTime getDayDateTime(Long currentTime, LocalTime localTime) { + return LocalDateTime.of(parseDate(currentTime), localTime); + } + + /** + * 时间格式化 + * + * @param currentTime 当前时间戳 + * @param localTime 时间 + * @return LocalDateTime + */ + public static long getDayTimestamp(Long currentTime, LocalTime localTime) { + return getDayDateTime(currentTime, localTime).toInstant(ZONE_OFFSET).toEpochMilli(); + } + + /** + * 时间格式化 + * + * @param currentTime 当前时间戳 + * @param localTime 时间 + * @return LocalDateTime + */ + public static LocalDateTime getDayDateTime(Long currentTime, LocalTime localTime, TemporalAdjuster adjuster) { + return getDayDateTime(currentTime, localTime).with(adjuster); + } + + /** + * 时间格式化 + * + * @param currentTime 当前时间戳 + * @param localTime 时间 + * @return LocalDateTime + */ + public static long getDayTimestamp(Long currentTime, LocalTime localTime, TemporalAdjuster adjuster) { + return getDayDateTime(currentTime, localTime, adjuster).toInstant(ZONE_OFFSET).toEpochMilli(); + } + + /** + * 获得当前时间的所在天的第一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getDayBeginTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MIN); + } + + /** + * 获得当前时间的所在天的最后一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getDayEndTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MAX); + } + + /** + * 获得当前时间的所在月的第一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getMonthBeginTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MIN, TemporalAdjusters.firstDayOfMonth()); + } + + /** + * 获得当前时间的所在月的最后一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getMonthEndTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MAX, TemporalAdjusters.lastDayOfMonth()); + } + + /** + * 获得当前时间的所在年的第一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getYearBeginTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MIN, TemporalAdjusters.firstDayOfYear()); + } + + /** + * 获得当前时间的所在年的最后一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getYearEndTimestamp(Long currentTime) { + return getDayTimestamp(currentTime, LocalTime.MAX, TemporalAdjusters.lastDayOfYear()); + } + + /** + * 获得当前时间的所在周的第一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getWeekBeginTimestamp(Long currentTime) { + LocalDate localDate = parseDate(currentTime); + return parse(localDate.minusDays(localDate.getDayOfWeek().getValue() - 1).atStartOfDay().with(LocalTime.MIN)); + } + + /** + * 获得当前时间的所在周的最后一秒 + * + * @param currentTime 当前时间戳 + * @return yyyy-MM-dd HH:mm:ss + */ + public static long getWeekEndTimestamp(Long currentTime) { + LocalDate localDate = parseDate(currentTime); + return parse(localDate.plusDays(7 - localDate.getDayOfWeek().getValue()).atStartOfDay().with(LocalTime.MAX)); + } + + /** + * 获取区间计算的开始时间 + * + * @param dateTime 时间 + * @param interval 时间间隔分钟(例:15,30,60) + * @return 第一笔时间 + */ + public static long getStartTime(LocalDateTime dateTime, int interval) { + int minute = dateTime.getMinute(); + int j = minute / interval; + int startMinute = j * interval; + return parse(dateTime.withMinute(startMinute).withSecond(0).withNano(0)); + } + + /** + * 获取区间时间内的采集时间 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param interval 时间间隔分钟(例:15,30,60) + * @return 第一笔时间 + */ + public static List getIntervalTimeList(long startTime, long endTime, int interval) { + List list = new ArrayList<>(); + long start = getStartTime(parseDateTime(startTime), interval); + int millisecond = interval * 60000; + while (start <= endTime) { + list.add(start); + start += millisecond; + } + return list; + } + + public static List getIntervalList(long startTime, long endTime, int interval) { + List list = new ArrayList<>(); + long start = startTime; + while (start <= endTime) { + list.add(start); + start += interval; + } + return list; + } + + + public static List getIntervalDayList(long startTime, long endTime, int intervalDay) { + List list = new ArrayList<>(); + long start = startTime; + while (start <= endTime) { + list.add(start); + LocalDateTime localDateTime = parseDateTime(start).plusDays(intervalDay); + start = parse(localDateTime); + } + return list; + } + + public static List getIntervalMonthList(long startTime, long endTime, int intervalMonth) { + List list = new ArrayList<>(); + long start = startTime; + while (start <= endTime) { + list.add(start); + LocalDateTime localDateTime = parseDateTime(start).plusMonths(intervalMonth); + start = parse(localDateTime); + } + return list; + } + + /** + * 获取区间时间内的采集时间 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param interval 时间间隔分钟(例:15,30,60) + * @return 第一笔时间 + */ + public static List getIntervalTimeList(LocalDateTime startTime, LocalDateTime endTime, int interval) { + List list = new ArrayList<>(); + long start = getStartTime(startTime, interval), end = parse(endTime); + int millisecond = interval * 60000; + while (start <= end) { + list.add(start); + start += millisecond; + } + return list; + } + + /** + * 时间戳转换为yyyyMM + * + * @param ts 时间戳 + * @return yyyyMM + */ + public static String getYearMonth(Long ts) { + return timeConvertStr(ts, YEAR_MONTH_PATTERN); + } + + public static String timeConvertStr(Long ts) { + return parseDateTime(ts).format(DATE_TIME_PATTERN); + } + + public static String timeConvertStr(Long ts, DateTimeFormatter formatter) { + return parseDateTime(ts).format(formatter); + } + + public static long getCurrentTime(){ + return System.currentTimeMillis(); + } + + public static Long convertTimeToLong(String time) { + if(StringUtils.isBlank(time)){ + return null; + } + LocalDateTime parse = LocalDateTime.parse(time, DATE_TIME_PATTERN); + return LocalDateTime.from(parse).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * 将年月日时分秒字符串转为 时间戳(13位) + */ + public static Long convertTimeToLong(String time,DateTimeFormatter formatter) { + if(StringUtils.isBlank(time)){ + return null; + } + LocalDateTime parse = LocalDateTime.parse(time, formatter); + return LocalDateTime.from(parse).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + public static String convertTimeToString(String dateTimeString,DateTimeFormatter datePattern,boolean isSfm) { + if(StringUtils.isBlank(dateTimeString)){ + return null; + } + if(isSfm){ + LocalDate date = LocalDate.parse(dateTimeString.substring(0, 10)); + return date.format(datePattern); + }else{ + LocalDateTime date = LocalDateTime.parse(dateTimeString); + return date.format(datePattern); + } + } + + public static String convertTimeToString_x(String dateTimeString,boolean isSfm) { + if(StringUtils.isBlank(dateTimeString)){ + return null; + } + if(isSfm){ + LocalDate date = LocalDate.parse(dateTimeString.substring(0, 10)); + return date.format(DATE_PATTERN); + }else{ + LocalDateTime date = LocalDateTime.parse(dateTimeString); + return date.format(DATE_TIME_PATTERN); + } + } + + /** + * 获取当天的00:00:00 + */ + public static LocalDateTime getDayStart(LocalDateTime time) { + return time.with(LocalTime.MIN); + } + + /** + * 获取向前推迟N天的时间 + */ + public static LocalDateTime getMinusDays(LocalDateTime time,long day) { + return time.minusDays(day); + } + + /** + * 获取向前推迟N周的时间 + */ + public static LocalDateTime getMinusWeeks(LocalDateTime time,long week) { + return time.minusWeeks(week); + } + + /** + * 获取向前推迟N月的时间 + */ + public static LocalDateTime getMinusMonths(LocalDateTime time,long month) { + return time.minusMonths(month); + } + + /** + * 获取向前推迟N年的时间 + */ + public static LocalDateTime getMinusYears(LocalDateTime time,long year) { + return time.minusYears(year); + } + + /** + * 获取当天的23:59:59 + */ + public static LocalDateTime getHourStart(LocalDateTime time) { + return time.truncatedTo(ChronoUnit.HOURS); + } + + /** + * 获取当天的23:59:59的最后一毫秒 + */ + public static LocalDateTime getHourEnd(LocalDateTime time) { + LocalDateTime hourStart = getHourStart(time); + return hourStart.plusHours(1).minusSeconds(1).withNano(999999999); + } + + + /** + * 获取当天的23:59:59 + */ + public static LocalDateTime getMinuteStart(LocalDateTime time) { + // 获取当前分钟的开始时间 + return time.truncatedTo(ChronoUnit.MINUTES); + } + + /** + * 获取当天的23:59:59 + */ + public static LocalDateTime getMinuteEnd(LocalDateTime time) { + // 获取当前分钟的开始时间 + LocalDateTime minuteStart = time.truncatedTo(ChronoUnit.MINUTES); + // 获取当前分钟的结束时间 + return minuteStart.plusMinutes(1).minusNanos(1); + } + + /** + * 获取当天的23:59:59 + */ + public static LocalDateTime getDayEnd(LocalDateTime time) { + return time.with(LocalTime.MAX); + } + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getWeekEnd(LocalDateTime time) { + return time.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY)).with(LocalTime.MAX); + } + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getWeekStart(LocalDateTime time) { + return time.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).with(LocalTime.MIN); + } + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getMonthStart(LocalDateTime time) { + return time.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN); + } + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getMonthEnd(LocalDateTime time) { + return time.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX); + } + + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getYearStart(LocalDateTime time) { + return time.with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN); + } + + /** + * 获取当周的23:59:59 + */ + public static LocalDateTime getYearEnd(LocalDateTime time) { + return time.with(TemporalAdjusters.lastDayOfYear()).with(LocalTime.MAX); + } + + /** + * 获取指定日期的毫秒 + */ + public static Long getMillis(LocalDateTime time) { + return time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + public static String timestamp2Str(Long ts, DateTimeFormatter formatter) { + return parseDateTime(ts).format(formatter); + } + + public static String timestamp2Str(Long ts) { + return timestamp2Str(ts, DATE_TIME_PATTERN); + } + + /** + * 日期格式化 日期格式为:yyyy-MM-dd + * + * @param date 日期 + * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN + * @return 返回yyyy-MM-dd格式日期 + */ + public static String format(Date date, String pattern) { + if (ObjectUtil.isNull(date)) { + return null; + } + SimpleDateFormat df = new SimpleDateFormat(pattern); + return df.format(date); + } + + /** + * 将时间字符串转换成时间戳 + * @param strDate + * @return + */ + public static Long dateToStamp(String strDate) { + Date date = parse(strDate, DATE_TIME_PATTERN_STR); + return date.getTime(); + } + + /** + * 日期解析 + * @param date 日期 + * @param pattern 格式,如:DateUtils.DATE_TIME_PATTERN + * @return 返回Date + */ + public static Date parse(String date, String pattern) { + try { + return new SimpleDateFormat(pattern).parse(date); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + /** + * 根据年份获取月份list,格式为2023-01,2023-02 + * @param year + * @return + */ + public static List getMonthList(String year) { + List monthList = new ArrayList<>(); + + for (int i = 1; i <= 12; i++) { + String month = String.format("%02d", i); + String monthString = year + "-" + month; + monthList.add(monthString); + } + + return monthList; + } + + /** + * 获取月份所有天数,传参格式2024-01,返回参数[2024-01-01....2024-01-31] + * @param monthString + * @return + */ + public static List getDatesOfMonth(String monthString) { + List datesList = new ArrayList<>(); + try { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM"); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(dateFormat.parse(monthString)); + int daysInMonth = calendar.getActualMaximum(Calendar.DAY_OF_MONTH); + SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd"); + + for (int day = 1; day <= daysInMonth; day++) { + calendar.set(Calendar.DAY_OF_MONTH, day); + String dateString = dateFormatter.format(calendar.getTime()); + datesList.add(dateString); + } + } catch (Exception e) { + e.printStackTrace(); + } + return datesList; + } + + /** + * 获取上月时间,传参格式2024-01,返回格式2023-12 + * @param dateString + * @return + */ + public static String getLastMonth(String dateString) { + try { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM"); + Date date = dateFormat.parse(dateString); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.add(Calendar.MONTH, -1); + Date lastMonthDate = calendar.getTime(); + return dateFormat.format(lastMonthDate); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取最近n周的起始和结束时间戳 注: 如果想要获得4周的时间,那么应该传入3, 因为本周也需要计算在内 + * + * @param weeks 周数 + * @return 起始时间戳 - 结束时间戳 + */ + public static Pair getTimestampRangeForWeeks(int weeks) { + return getTimestampRange(weeks, "week"); + } + + /** + * 获得最近某个时间段的起始和结束时间, 结束时间为当天的最后时间 + * + * @param timeNum 时间数量 + * @param timeUnit 时间单位 + * @return 起始时间-结束时间 + */ + private static Pair getTimestampRange(int timeNum, String timeUnit) { + LocalDate now = LocalDate.now(); + // n周前的起始时间 + LocalDate targetDay = null; + switch (timeUnit) { + case "day": + targetDay = now.minusDays(timeNum); + break; + case "week": + targetDay = now.minusWeeks(timeNum); + break; + case "month": + targetDay = now.minusMonths(timeNum); + break; + case "year": + targetDay = now.minusYears(timeNum); + break; + default: + throw new SysException("not support for " + timeUnit); + } + + Long beginTime = + targetDay + .with(DayOfWeek.MONDAY) + .atStartOfDay() + .atZone(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli(); + + Long endTime = + LocalDateTime.of(now, LocalTime.MAX) + .atZone(ZoneId.systemDefault()) + .toInstant() + .toEpochMilli(); + + return Pair.of(beginTime, endTime); + } + + /** + * 时间戳转时间 + * + * @param currentTimestamp 当前时间戳 + * @return 返回日期 + */ + public static Date timestampToDate(Long currentTimestamp) { + return new Date(currentTimestamp); + } + + /** + * 时间戳转时间 + * + * @param currentTimestamp 当前时间戳 + * @param pattern 字符串格式 + * @return 返回日期 + */ + public static String timestampToDate(Long currentTimestamp, String pattern) { + Date date = new Date(currentTimestamp); + return format(date, pattern); + } + + /** + * "yyyy-MM-dd HH:mm:ss"; 字符串时间转时间戳 + * + * @param currentTime 当前时间 + * @return 返回时间戳 + */ + public static Long dateToTimestamp(String currentTime) { + return parse(currentTime, DATE_TIME_PATTERN_STR).getTime(); + } + + /** + * 时间转时间戳 + * + * @param currentDate 当前时间 + * @return 返回时间戳 + */ + public static Long dateToTimestamp(Date currentDate) { + return currentDate.getTime(); + } + + /** + * 字符串时间转时间戳 + * + * @param currentTime 当前时间 + * @param pattern 日期格式 + * @return 返回时间戳 + */ + public static Long dateToTimestamp(String currentTime, String pattern) { + return parse(currentTime, pattern).getTime(); + } + + /** + * 获得当前时间的所在天的第一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayBeginTime() { + return getDayBeginTime(new Date()); + } + + /** + * 获得当前时间的所在天的第一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayBeginTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getDayBeginTime(currentTime); + } + + /** + * 获得当前时间的所在天的起始时间 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayBeginTime(String currentTime) { + return format(parse(currentTime, DATE_PATTERN_STR), DATE_TIME_PATTERN_STR); + } + + + /** + * 获得当前时间的所在天的最后一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayEndTime() { + String currentTime = format(new Date(), DATE_TIME_PATTERN_STR); + return getDayEndTime(currentTime); + } + + + /** + * 获得当前时间的所在天的最后一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayEndTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getDayEndTime(currentTime); + } + + /** + * 获得当前时间的所在天的最后一秒 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getDayEndTime(String currentTime) { + Date resetDate = parse(getDayBeginTime(currentTime), DATE_TIME_PATTERN_STR); + return format(org.apache.commons.lang3.time.DateUtils.addSeconds(org.apache.commons.lang3.time.DateUtils.addDays(resetDate, 1), -1), DATE_TIME_PATTERN_STR); + } + + /** + * 获得当前时间的所在月的第一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthBeginTime() { + String currentTime = format(new Date(), DATE_TIME_PATTERN_STR); + return getMonthBeginTime(currentTime); + } + + /** + * 获得当前时间的所在月的第一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthBeginTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getMonthBeginTime(currentTime); + } + + /** + * 获得当前时间的所在月的第一秒 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthBeginTime(String currentTime) { + return format(parse(currentTime, MONTH_PATTERN), DATE_TIME_PATTERN_STR); + } + + + /** + * 获得当前时间的所在月的最后一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthEndTime() { + String currentTime = format(new Date(), DATE_TIME_PATTERN_STR); + return getMonthEndTime(currentTime); + } + + /** + * 获得当前时间的所在月的最后一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthEndTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getMonthEndTime(currentTime); + } + + /** + * 获得当前时间的所在月的最后一秒 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getMonthEndTime(String currentTime) { + Date resetDate = parse(getMonthBeginTime(currentTime), DATE_TIME_PATTERN_STR); + return format(org.apache.commons.lang3.time.DateUtils.addSeconds(org.apache.commons.lang3.time.DateUtils.addMonths(resetDate, 1), -1), DATE_TIME_PATTERN_STR); + } + + /** + * 获得当前时间的所在年的第一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearBeginTime() { + String currentTime = format(new Date(), DATE_TIME_PATTERN_STR); + return getYearBeginTime(currentTime); + } + + /** + * 获得当前时间的所在年的第一秒 + * + * @return date + */ + public static Date getYearBeginDate(){ + return parse(getYearBeginTime(), DATE_TIME_PATTERN_STR); + } + + /** + * 获得当前时间的所在年的第一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearBeginTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getYearBeginTime(currentTime); + } + + /** + * 获得当前时间的所在年的第一秒 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearBeginTime(String currentTime) { + return format(parse(currentTime, YEAR_PATTERN), DATE_TIME_PATTERN_STR); + } + + + /** + * 获得当前时间的所在年的最后一秒 + * + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearEndTime() { + String currentTime = format(new Date(), DATE_TIME_PATTERN_STR); + return getYearEndTime(currentTime); + } + + /** + * 获得当前时间的所在年的最后一秒 + * + * @param currentDate 当前时间 + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearEndTime(Date currentDate) { + String currentTime = format(currentDate, DATE_TIME_PATTERN_STR); + return getYearEndTime(currentTime); + } + + /** + * 获得当前时间的所在年的最后一秒 + * + * @param currentTime 当前时间 yyyy-MM-dd HH:mm:ss + * @return yyyy-MM-dd HH:mm:ss + */ + public static String getYearEndTime(String currentTime) { + Date resetDate = parse(getYearBeginTime(currentTime), DATE_TIME_PATTERN_STR); + return format(org.apache.commons.lang3.time.DateUtils.addSeconds(org.apache.commons.lang3.time.DateUtils.addYears(resetDate, 1), -1), DATE_TIME_PATTERN_STR); + } + + public static List> splitTimeRange(Long startTime, Long endTime, Long interval, String sort) { + List> resList = Lists.newArrayList(); + if(ObjectUtil.isNull(startTime) || ObjectUtil.isNull(endTime)){ + return resList; + } + if(ObjectUtil.isNull(interval) || ObjectUtil.equals(interval,0L)){ + Map map = new HashMap<>(); + map.put("startTime", startTime); + map.put("endTime", endTime); + resList.add(map); + }else{ + long currentStartTime = startTime; + long endTimestamp = endTime; + while (currentStartTime < endTimestamp) { + Map map = new HashMap<>(); + map.put("startTime", currentStartTime); + long currentEndTime = currentStartTime + interval; + if(currentEndTime>= endTimestamp){ + map.put("endTime", endTimestamp); + }else{ + map.put("endTime", currentEndTime); + } + resList.add(map); + currentStartTime = currentEndTime+1; + } + if(StringUtils.equalsAnyIgnoreCase(sort, Constant.DESC)){ + Collections.reverse(resList); //倒序 + } + } + return resList; + } + + public static Long firstDayOfWeekMilli(){ + LocalDate now = LocalDate.now(); + LocalDate firstDayOfWeek = now.with(java.time.temporal.TemporalAdjusters.previousOrSame(java.time.DayOfWeek.MONDAY)); + ZoneId chinaZone = ZoneId.of("Asia/Shanghai"); + return firstDayOfWeek.atStartOfDay(chinaZone).toInstant().toEpochMilli(); + } + + + /** + * 毫秒戳转换为字符串形式日期 + * @param milliseconds + * @return + */ + public static String millisecondsFormatStrDate(Long milliseconds ){ + // 将时间戳转换为Instant对象 + Date date = new Date(milliseconds); + SimpleDateFormat formatter =new SimpleDateFormat(DATE_TIME_PATTERN_STR); + return formatter.format(date); + } + + /** + * 生成时间范围以及该范围对应的分组名称 + * + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param interval 时间间隔 + * @param groupNameGenerator 分组名称生成器 + * @return List(开始时间-结束时间-分组名称) + */ + public static List> getTimeGroupTupleList( + Long startTime, + Long endTime, + Long interval, + BiFunction groupNameGenerator) { + groupNameGenerator = Optional.ofNullable(groupNameGenerator).orElse(defaultGroupNameGenerator()); + List> res = new ArrayList<>(); + for (long i = startTime; i < endTime; i += interval) { + Long groupEndTime = i + interval; + Triple triple = + Triple.of( + i, + groupEndTime, + groupNameGenerator.apply(i, groupEndTime)); + res.add(triple); + } + return res; + } + + private static BiFunction defaultGroupNameGenerator(){ + return (start, end) -> { + LocalDateTime startDateTime = parseDateTime(start); + return startDateTime.toLocalDate().toString(); + }; + } + + /** + * 按天切割时间,返回一个列表 + * + * @param startTm 开始时间 "yyyy-MM-dd HH:mm:ss" + * @param endTm 结束时间 "yyyy-MM-dd HH:mm:ss" + * @return [{"beginTime":"2021-11-02 22:28:59","endTime":"2021-11-02 23:59:59"},{"beginTime":"2021-11-03 00:00:00","endTime":"2021-11-03 23:56:50"}] + */ + public static List getSplitedTimeListByDay(String startTm, String endTm) { + Date beginDate = parse(startTm, DATE_TIME_PATTERN_STR); + Date endDate = parse(endTm, DATE_TIME_PATTERN_STR); + Map splitedTimeParamMap = getSplitedTimeMapByDay(beginDate, endDate); + return Lists.newArrayList(splitedTimeParamMap.values()); + } + + + /** + * 按天分割时间,返回一个映射 + * + * @param beginDate 开始时间 + * @param endDate 结束时间 + * @return {"2021-11-02 00:00:00":{"beginTime":"2021-11-02 22:28:59","endTime":"2021-11-02 23:59:59"},"2021-11-03 00:00:00":{"beginTime":"2021-11-03 00:00:00","endTime":"2021-11-03 23:56:50"}} + */ + public static Map getSplitedTimeMapByDay(Date beginDate, Date endDate) { + String startTm = format(beginDate, DATE_TIME_PATTERN_STR); + String endTm = format(endDate, DATE_TIME_PATTERN_STR); + + Map splitedTimeMap = Maps.newLinkedHashMap(); + if (CompareUtil.compare(endDate, beginDate) < 0) { + log.warn("endDate is earlier than beginDate, beginDate:{}, endDate:{}", startTm, endTm); + return splitedTimeMap; + } + Date resetDate = parse(format(parse(startTm, DATE_PATTERN_STR), DATE_TIME_PATTERN_STR), DATE_TIME_PATTERN_STR); + while (CompareUtil.compare(resetDate, endDate) <= 0) { + SplitedTimeParam splitedTimeParam = new SplitedTimeParam(); + if (CompareUtil.compare(resetDate, beginDate) <= 0) { + splitedTimeParam.setBeginTime(startTm); + } else if (CompareUtil.compare(resetDate, beginDate) > 0) { + splitedTimeParam.setBeginTime(format(resetDate, DATE_TIME_PATTERN_STR)); + } + + if (CompareUtil.compare(org.apache.commons.lang3.time.DateUtils.addDays(resetDate, 1), endDate) > 0) { + splitedTimeParam.setEndTime(endTm); + } else if (CompareUtil.compare(org.apache.commons.lang3.time.DateUtils.addDays(resetDate, 1), endDate) <= 0) { + Date timePeriodEndDate = org.apache.commons.lang3.time.DateUtils.addSeconds(org.apache.commons.lang3.time.DateUtils.addDays(resetDate, 1), -1); + splitedTimeParam.setEndTime(format(timePeriodEndDate, DATE_TIME_PATTERN_STR)); + } + splitedTimeMap.put(format(resetDate, DATE_TIME_PATTERN_STR), splitedTimeParam); + resetDate = org.apache.commons.lang3.time.DateUtils.addDays(resetDate, 1); + } + return splitedTimeMap; + } + + /** + * 根据时间范围获取时间 + */ + public static Map getDateTimeByTimeHorizon(String timeHorizon) { + Date date = Calendar.getInstance().getTime(); + Map dateMap = Maps.newHashMap(); + if (StringUtils.equals(timeHorizon, TimeHorizonEnum.CURRENT_DAY.getCode())) { + dateMap.put("beginTime", getDayBeginTime(date)); + dateMap.put("endTime", getDayEndTime(date)); + return dateMap; + } + + if (StringUtils.equals(timeHorizon, TimeHorizonEnum.LAST_HOURS.getCode())) { + dateMap.put("beginTime", addDateHours(date, -24)); + dateMap.put("endTime", format(date, DATE_TIME_PATTERN_STR)); + return dateMap; + } + + if (StringUtils.equals(timeHorizon, TimeHorizonEnum.LAST_DAYS.getCode())) { + dateMap.put("beginTime", addDateDays(date, -7)); + dateMap.put("endTime", format(date, DATE_TIME_PATTERN_STR)); + return dateMap; + } + dateMap.put("beginTime", addDateDays(date, -30)); + dateMap.put("endTime", format(date, DATE_TIME_PATTERN_STR)); + return dateMap; + } + + /** + * 对日期的【小时】进行加/减 + * + * @param date 日期 + * @param hours 小时数,负数为减 + * @return 加/减几小时后的日期 + */ + public static String addDateHours(Date date, int hours) { + DateTime dateTime = new DateTime(date); + return format(dateTime.plusHours(hours).toDate(), DATE_TIME_PATTERN_STR); + } + + /** + * 对日期的【天】进行加/减 + * + * @param date 日期 + * @param days 天数,负数为减 + * @return 加/减几天后的日期 + */ + public static String addDateDays(Date date, int days) { + DateTime dateTime = new DateTime(date); + return format(dateTime.plusDays(days).toDate(), DATE_TIME_PATTERN_STR); + } + + public static LocalDateTime getNextDayStart(LocalDateTime time) { + return time.plusDays(1).with(LocalTime.MIN); + } + + public static Long convertYYMMTimeToLong(String time) { + if (StringUtils.isBlank(time)) { + return null; + } + LocalDate date = LocalDate.parse(time + "-01"); + LocalDateTime startDateTime = date.atStartOfDay(); + return startDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + public static Long convertYYTimeToLong(String time) { + if (StringUtils.isBlank(time)) { + return null; + } + LocalDate date = LocalDate.parse(time + "-01"+ "-01"); + LocalDateTime startDateTime = date.atStartOfDay(); + return startDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * 获取某年第一天日期 + * + * @param year 年份 + * @return Date + */ + public static Date getYearFirst(int year) { + Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(Calendar.YEAR, year); + Date currYearFirst = calendar.getTime(); + return currYearFirst; + } + /** + * 获取某年最后一天日期 + * + * @param year 年份 + * @return Date + */ + public static Date getYearLast(int year) { + Calendar calendar = Calendar.getInstance(); + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.roll(Calendar.DAY_OF_YEAR, -1); + Date currYearFirst = calendar.getTime(); + return currYearFirst; + } + + /** + * + * @param beginFlag 0--每月第一毫秒 1--每天第一毫秒 不传非法则不变 + * @return + */ + public static long getBeginTime(String Date, String beginFlag){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(parse(Date, DATE_TIME_PATTERN_STR)); + + calendar.set(Calendar.DATE, StringUtils.equals(beginFlag, MONTH_BEGIN_TIME) ? 1 : calendar.get(Calendar.DATE) ); + calendar.set(Calendar.HOUR_OF_DAY, StringUtils.equals(beginFlag, DAY_BEGIN_TIME) || StringUtils.equals(beginFlag, MONTH_BEGIN_TIME) ? 0 : calendar.get(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, StringUtils.equals(beginFlag, DAY_BEGIN_TIME) || StringUtils.equals(beginFlag, MONTH_BEGIN_TIME)? 0 : calendar.get(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, StringUtils.equals(beginFlag, DAY_BEGIN_TIME) || StringUtils.equals(beginFlag, MONTH_BEGIN_TIME)? 0 : calendar.get(Calendar.SECOND)); + Long timestrap = StringUtils.equals(beginFlag, DAY_BEGIN_TIME) || StringUtils.equals(beginFlag, MONTH_BEGIN_TIME) ? + (calendar.getTime().getTime()/1000)*1000 : calendar.getTime().getTime(); + return timestrap; + + } + + /** + * + * @param endFlag 0--每月结束的毫秒数 1--每天结束的毫秒数 不传非法则不变 + * @return + */ + public static long getEndTime(String Date, String endFlag){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(parse(Date, DATE_TIME_PATTERN_STR)); + + calendar.add(Calendar.MONTH,StringUtils.equals(endFlag, MONTH_END_TIME) ? 1 : 0); + calendar.add(Calendar.DATE,StringUtils.equals(endFlag, DAY_END_TIME) ? 1 : 0); + + calendar.set(Calendar.DATE, StringUtils.equals(endFlag, MONTH_END_TIME) ? 1 : calendar.get(Calendar.DATE)); + calendar.set(Calendar.HOUR_OF_DAY, StringUtils.equals(endFlag, DAY_END_TIME) || StringUtils.equals(endFlag, MONTH_END_TIME) ? 0 : calendar.get(Calendar.HOUR_OF_DAY)); + calendar.set(Calendar.MINUTE, StringUtils.equals(endFlag, DAY_END_TIME) || StringUtils.equals(endFlag, MONTH_END_TIME)? 0 : calendar.get(Calendar.MINUTE)); + calendar.set(Calendar.SECOND, StringUtils.equals(endFlag, DAY_END_TIME) || StringUtils.equals(endFlag, MONTH_END_TIME)? 0 : calendar.get(Calendar.SECOND)); + Long timestrap = StringUtils.equals(endFlag, DAY_END_TIME) || StringUtils.equals(endFlag, MONTH_END_TIME) ? + (calendar.getTime().getTime()/1000-1)*1000 : calendar.getTime().getTime(); + return timestrap; + } + + /** + * 获取当前年份 + * @return + */ + public static String getCurrentYear() { + Year year = Year.now(); + return String.valueOf(year.getValue()); + } + + public static List generateYearList(String dateString, int yearsToSubtract) { + List yearList = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); + try { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(sdf.parse(dateString)); + for (int i = 0; i <= yearsToSubtract; i++) { + yearList.add(sdf.format(calendar.getTime())); + calendar.add(Calendar.YEAR, -1); + } + } catch (ParseException e) { + e.printStackTrace(); + } + return yearList; + } + + /** + * 获取今天的月份 + * @return + */ + public static int getTodayMonth() { + Calendar calendar = Calendar.getInstance(); + return calendar.get(Calendar.MONTH)+1; + } + + /** + * 获取指定日期的月份 + * @return + */ + public static int getMonthByDate(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + return calendar.get(Calendar.MONTH)+1; + } + + + /** + * 上一年第一秒得毫秒戳 + * @return + */ + public static Long fistDayOfLastYear(){ + LocalDate currentDate = LocalDate.now(); // 获取当前日期 + int currentYear = currentDate.getYear(); // 获取当前年份 + + LocalDateTime dateTime = LocalDateTime.of(currentYear - 1, Month.JANUARY, 1, 0, 0, 0); // 上一年 1 月 1 日 0 时 0 分 0 秒 + return dateTime.toInstant(ZoneOffset.UTC).toEpochMilli(); + } + + /** + * 上一年最后一秒得毫秒戳 + * @return + */ + public static Long lastDayOfLastYear(){ + LocalDate currentDate = LocalDate.now(); // 获取当前日期 + int currentYear = currentDate.getYear(); // 获取当前年份 + + LocalDateTime dateTime = LocalDateTime.of(currentYear - 1, Month.DECEMBER, 31, 23, 59, 59); + // 上一年的 12 月 31 日 23 时 59 分 59 秒 + + LocalDateTime lastDayOfYear = dateTime.with(TemporalAdjusters.lastDayOfYear()); + // 获取上一年的最后一天 + + return lastDayOfYear.toInstant(ZoneOffset.UTC).toEpochMilli(); + } + + public static Integer yearsPassed(String buildDate ) { + // 定义日期格式 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + // 解析日期字符串为 LocalDate 对象 + LocalDate parsedDate = LocalDate.parse(buildDate, formatter); + + // 获取当前日期 + LocalDate currentDate = LocalDate.now(); + + // 计算年份差距 + Period period = Period.between(parsedDate, currentDate); + return period.getYears()==0?1:period.getYears(); + } + + public static Long theDayBeforeYesterdayDayStartTs() { + return parse(LocalDate.now().minusDays(2).atStartOfDay()); + } + + public static Long yesterdayStartTs() { + return parse(LocalDate.now().minusDays(1).atStartOfDay()); + } + + public static Long todayStartTs() { + return parse(LocalDate.now().atStartOfDay()); + } + + public static Long monthStartTs() { + return parse(monthStartDate()); + } + + public static Long monthEndTs() { + return parse(monthEndDate()); + } + + public static Long lastMonthStartTs() { + return parse(lastMonthStartDate()); + } + + public static Long lastMonthEndTs() { + return parse(lastMonthEndDate()); + } + + public static Long yearStartTs() { + return parse(yearStartDateTime()); + } + + public static Long yearEndTs() { + return parse(yearEndDateTime()); + } + + public static Long lastYearStartTs() { + return parse(lastYearStartDate()); + } + + public static LocalDateTime monthStartDate() { + return LocalDateTime.of( + LocalDate.from(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth())), + LocalTime.MIN); + } + + public static LocalDateTime monthEndDate() { + return LocalDateTime.of( + LocalDate.from(LocalDate.now().with(TemporalAdjusters.lastDayOfMonth())), + LocalTime.MAX); + } + + public static LocalDateTime lastMonthStartDate() { + return LocalDateTime.of( + LocalDate.from( + LocalDate.now().minusMonths(1).with(TemporalAdjusters.firstDayOfMonth())), + LocalTime.MIN); + } + + public static LocalDateTime lastMonthEndDate() { + return LocalDateTime.of( + LocalDate.from( + LocalDate.now().minusMonths(1).with(TemporalAdjusters.lastDayOfMonth())), + LocalTime.MIN); + } + + public static LocalDateTime yearStartDateTime() { + return LocalDateTime.of( + LocalDate.from(LocalDate.now().with(TemporalAdjusters.firstDayOfYear())), + LocalTime.MIN); + } + + public static LocalDateTime yearStartDateTime(LocalDateTime localDateTime) { + return LocalDateTime.of( + LocalDate.from(localDateTime.toLocalDate().with(TemporalAdjusters.firstDayOfYear())), + LocalTime.MIN); + } + + public static Date yearStartDate(LocalDateTime localDateTime) { + LocalDateTime startDateTime = yearStartDateTime(localDateTime); + return toDate(startDateTime); + } + + public static LocalDateTime yearEndDateTime() { + return LocalDateTime.of( + LocalDate.from(LocalDate.now().with(TemporalAdjusters.lastDayOfYear())), + LocalTime.MAX); + } + + public static LocalDateTime yearEndDateTime(LocalDateTime localDateTime) { + return LocalDateTime.of( + LocalDate.from(localDateTime.toLocalDate().with(TemporalAdjusters.lastDayOfYear())), + LocalTime.MAX); + } + + public static Date yearEndDate(LocalDateTime localDateTime) { + LocalDateTime startDateTime = yearEndDateTime(localDateTime); + return toDate(startDateTime); + } + + public static LocalDateTime lastYearStartDate() { + return LocalDateTime.of( + LocalDate.from( + LocalDate.now().minusYears(1).with(TemporalAdjusters.firstDayOfYear())), + LocalTime.MIN); + } + + public static List allTsInMonth() { + LocalDateTime begin = monthStartDate(); + LocalDateTime end = LocalDateTime.now(); + return generateTs(begin, end, x -> x.plusDays(1)); + } + + public static List allTsInLastMonth() { + LocalDateTime begin = lastMonthStartDate(); + LocalDateTime end = monthStartDate(); + return generateTs(begin, end, x -> x.plusDays(1)); + } + + public static List allTsInYear() { + LocalDateTime begin = yearStartDateTime(); + LocalDateTime end = LocalDateTime.now(); + return generateTs(begin, end, x -> x.plusMonths(1)); + } + + public static List allTsInLastYear() { + LocalDateTime begin = lastYearStartDate(); + LocalDateTime end = yearStartDateTime(); + return generateTs(begin, end, x -> x.plusMonths(1)); + } + + public static List generateTs( + LocalDateTime begin, + LocalDateTime end, + Function reduceFun) { + List tsList = new ArrayList<>(); + while (!begin.isAfter(end)) { + tsList.add(parse(begin)); + begin = reduceFun.apply(begin); + } + return tsList; + } + + public static Long getTsByDay(Integer day) { + return parse(LocalDate.now().minusDays(day).atStartOfDay()); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/EncryptUtils.java b/common/core/src/main/java/com/thing/common/core/utils/EncryptUtils.java new file mode 100644 index 0000000..774755a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/EncryptUtils.java @@ -0,0 +1,243 @@ +package com.thing.common.core.utils; + +import static org.bouncycastle.jce.provider.BouncyCastleProvider.PROVIDER_NAME; + +import com.google.common.hash.Hashing; +import com.thing.common.core.enumeration.RsaKeyType; + +import lombok.SneakyThrows; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringReader; +import java.nio.charset.StandardCharsets; +import java.security.*; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +/** + * @author siyang + * @date 2024/4/29 11:29 + * @description 加密工具类 + */ +@SuppressWarnings("unused") +public class EncryptUtils { + /* + 将 BouncyCastleProvider 添加到Java的安全提供者列表中,之后就可以通过 "BC" 引用它来获取或实例化加密算法等组件 + 如 Cipher.getInstance("AES/ECB/PKCS7Padding", "BC") + */ + static { + Security.addProvider(new BouncyCastleProvider()); + } + + private static final String AES_ALGORITHM = "AES"; + private static final String RSA_ALGORITHM = "RSA"; + private static final String AES_ALGORITHM_MODEL = "AES/ECB/PKCS5Padding"; + + /* ============================== AES: 加密业务数据 ============================== */ + + /** + * 获取Aes密钥。 + * + * @param keySize 密钥长度,可选值:128, 192;理论可填写256,由于返回时会对字节进行base64编码,无形中扩大了字节数,所以实际选择128、192 + * @return base64格式密钥 + */ + @SneakyThrows + public static String createAesKey(int keySize) { + KeyGenerator keygen = KeyGenerator.getInstance(AES_ALGORITHM); + keygen.init(keySize); + SecretKey secretKey = keygen.generateKey(); + byte[] byteKey = secretKey.getEncoded(); + return Base64.getEncoder().encodeToString(byteKey); + } + + /** + * AES 加密 + * + * @param content 需要加密的内容 + * @param key aes密钥 + * @return 加密后的密文 + */ + @SneakyThrows + public static String aesEncrypt(String content, String key) { + Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME); + SecretKeySpec secretKeySpec = + new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec); + + byte[] encryptedBytes = cipher.doFinal(content.getBytes(StandardCharsets.UTF_8)); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + /** + * AES 解密 + * + * @param encryptedContent 已加密的密文 + * @param key aes密钥,必须与加密时使用的密钥一致 + * @return 解密后的内容 + */ + @SneakyThrows + public static String aesDecrypt(String encryptedContent, String key) { + Cipher cipher = Cipher.getInstance(AES_ALGORITHM_MODEL, PROVIDER_NAME); + SecretKeySpec secretKeySpec = + new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), AES_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); + + byte[] decodedBytes = + Base64.getDecoder().decode(encryptedContent.getBytes(StandardCharsets.UTF_8)); + byte[] decryptedBytes = cipher.doFinal(decodedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + /* ============================== RSA: 加密AES密钥 ============================== */ + + /** + * 生成rsa密钥对 + * + * @param keySize 密钥长度 + * @return 密钥对:left:公钥; right:私钥 + */ + @SneakyThrows + public static Pair createPemRsaKeyPair(int keySize) { + String pemPublicKeyFormat = "-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----"; + String pemPrivateKeyFormat = "-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----"; + KeyPairGenerator generator = KeyPairGenerator.getInstance(RSA_ALGORITHM, PROVIDER_NAME); + generator.initialize(keySize); + KeyPair kp = generator.generateKeyPair(); + String publicKey = + Base64.getEncoder() + .encodeToString(kp.getPublic().getEncoded()) + .replaceAll("(.{64})", "$1\n"); + String privateKey = + Base64.getEncoder() + .encodeToString(kp.getPrivate().getEncoded()) + .replaceAll("(.{64})", "$1\n"); + return Pair.of( + String.format(pemPublicKeyFormat, publicKey), + String.format(pemPrivateKeyFormat, privateKey)); + } + + /** + * 使用公钥加密数据 + * + * @param pemPublicKey pem格式公钥 + * @param content 待加密的数据 + * @return 加密后的数据Base64编码字符串 + */ + @SneakyThrows + public static String rsaEncrypt(String content, String pemPublicKey) { + byte[] publicKeyBytes = parsePemKey(pemPublicKey); + KeySpec keySpec = new X509EncodedKeySpec(publicKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME); + PublicKey key = keyFactory.generatePublic(keySpec); + + Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, key); + + byte[] encryptedBytes = cipher.doFinal(content.getBytes()); + return Base64.getEncoder().encodeToString(encryptedBytes); + } + + /** + * 使用私钥解密数据 + * + * @param pemPrivateKey pem格式私钥 + * @param encryptedContent 加密后的数据Base64编码字符串 + * @return 原始数据字符串 + */ + @SneakyThrows + public static String rsaDecrypt(String encryptedContent, String pemPrivateKey) { + byte[] privateKeyBytes = parsePemKey(pemPrivateKey); + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes); + KeyFactory keyFactory = KeyFactory.getInstance(RSA_ALGORITHM, PROVIDER_NAME); + PrivateKey key = keyFactory.generatePrivate(keySpec); + + Cipher cipher = Cipher.getInstance(RSA_ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, key); + + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedContent); + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + return new String(decryptedBytes, StandardCharsets.UTF_8); + } + + @SneakyThrows + public static byte[] parsePemKey(String pemKey) { + PemReader pemReader = new PemReader(new StringReader(pemKey)); + PemObject pemObject = pemReader.readPemObject(); + pemReader.close(); + return pemObject.getContent(); + } + + /* ============================== 密钥获取: 读取resource目录下的密钥 ============================== */ + @SneakyThrows + public static String getPemRsaKeyFromResource(RsaKeyType keyType) { + String keyPath = String.format("classpath:keys/rsa_%s.pem", keyType.getValue()); + ResourceLoader resourceLoader = new DefaultResourceLoader(); + Resource resource = resourceLoader.getResource(keyPath); + InputStream inputStream = resource.getInputStream(); + try (final Reader reader = new InputStreamReader(inputStream)) { + StringBuilder sb = new StringBuilder(); + IOUtils.copy(reader, sb); + return sb.toString(); + } + } + + /* ============================== MD5加密 ============================== */ + @SuppressWarnings("all") + public static String md5Encrypt(String content) { + return Hashing.md5() + .newHasher() + .putString(content, StandardCharsets.UTF_8) + .hash() + .toString(); + } + + public static void main(String[] args) { + System.out.println("================准备各类密钥==================="); + String aesKey = createAesKey(192); + System.out.println("AES key: " + aesKey); + // Pair rsaKeyPair = createPemRsaKeyPair(1024); + String rsaPublicKey = getPemRsaKeyFromResource(RsaKeyType.PUBLIC); + String rsaPrivateKey = getPemRsaKeyFromResource(RsaKeyType.PRIVATE); + System.out.println("RSA public key: \n" + rsaPublicKey); + System.out.println(); + System.out.println("RSA private key: \n" + rsaPrivateKey); + + System.out.println("\n================准备待传输(加密)数据==================="); + String data = "hello word! hello lrd!"; + System.out.println("待加密数据: " + data); + + System.out.println("\n================使用AES key对数据进行加密==================="); + String encryptedText = aesEncrypt(data, aesKey); + System.out.println("AES 加密后的数据: " + encryptedText); + + System.out.println("\n================使用RSA公钥对AES key进行加密==================="); + String encryptAesKey = rsaEncrypt(aesKey, rsaPublicKey); + System.out.println("加密后的AES key: " + encryptAesKey); + + System.out.println("\n================使用RSA私钥对AES key进行解密==================="); + String decryptAesKey = rsaDecrypt(encryptAesKey, rsaPrivateKey); + System.out.println("解密后的AES key: " + decryptAesKey); + + System.out.println("\n================使用解密后的AES key进行数据解密==================="); + String decryptedText = aesDecrypt(encryptedText, decryptAesKey); + System.out.println("解密后的数据: " + decryptedText); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/FormulaUtil.java b/common/core/src/main/java/com/thing/common/core/utils/FormulaUtil.java new file mode 100644 index 0000000..fb3b8d8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/FormulaUtil.java @@ -0,0 +1,158 @@ +package com.thing.common.core.utils; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import com.thing.common.core.enumeration.SignEnum; +import com.thing.common.core.exception.SysException; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +/** + * @author siyang + * @date 2024-03-01 + * @description 涉及公式的工具类 + */ +@Slf4j +public class FormulaUtil { + + /** + * 验证输入的公式是否合法 + * + * @param formula 公式表达式 + * @return 是否合法 + */ + public static boolean validateFormula(String formula) { + // 记录每个符号的索引,以及上一个符号的位置 + List> signIndexList = new ArrayList<>(); + // 判断括号是否对称出现,记录位置,递归处理括号内部的子公式 + Stack> stack = new Stack<>(); + for (int i = 0; i < formula.length(); i++) { + String ch = String.valueOf(formula.charAt(i)); + SignEnum sign = SignEnum.match(ch); + if (Objects.isNull(sign)) { + continue; + } + if (sign == SignEnum.LB) { + stack.push(Pair.of(i, SignEnum.LB)); + } else if (sign == SignEnum.RB) { + if (stack.isEmpty()) { + return false; + } + Pair lbPair = stack.pop(); + if (lbPair.getLeft() >= (i - 1)) { + return false; + } + String subFormula = formula.substring(lbPair.getLeft() + 1, i); + boolean subFormulaValid = validateFormula(subFormula); + if (!subFormulaValid) { + return false; + } + } else { + Integer lastSignIndex = + signIndexList.isEmpty() + ? null + : signIndexList.get(signIndexList.size() - 1).getMiddle(); + signIndexList.add(Triple.of(lastSignIndex, i, sign)); + } + } + if (!stack.isEmpty()) { + return false; + } + + // 没有括号:1. 不能以运算符作为开头和结尾, 2. 运算符不能连续出现 + List signEnums = SignEnum.noBracketsSigns(); + for (SignEnum signEnum : signEnums) { + if (formula.startsWith(signEnum.getVal()) || formula.endsWith(signEnum.getVal())) { + return false; + } + } + + // 连续符号列表 + List> continuousSignList = + signIndexList.stream() + .filter( + t -> + Objects.nonNull(t.getLeft()) + && (t.getMiddle() - t.getLeft() == 1)) + .toList(); + + for (Triple t : continuousSignList) { + String nextChar = String.valueOf(formula.charAt(t.getMiddle() + 1)); + SignEnum nextSign = SignEnum.match(nextChar); + if (Objects.nonNull(nextSign) && nextSign.isNotBrackets()) { + return false; + } + // 前一个符号与当前符号的组合是否是合法的符号 + String frontChar = String.valueOf(formula.charAt(t.getLeft())); + SignEnum frontSign = SignEnum.match(frontChar); + SignEnum combineSign = SignEnum.combineSign(frontSign, t.getRight()); + if (Objects.isNull(combineSign)) { + return false; + } + } + + return true; + } + + /** + * 在公式中替换变量,前提条件:变量都是以单个字符形式出现 + * 该方法可避免不被期望的递归替换 + * + * @param formula 公式表达式 + * @param variableMap 变量map, key为变量, value为变量中文名称 + * @return 变量被替换后的公式表达式 + */ + public static String replaceVariable(String formula, Map variableMap) { + if (StringUtils.isBlank(formula) || CollectionUtils.isEmpty(variableMap)) { + return formula; + } + List formulaChars = + formula.chars().mapToObj(c -> String.valueOf((char) c)).toList(); + StringBuilder formulaBuilder = new StringBuilder(); + formulaChars.forEach(s -> formulaBuilder.append(variableMap.getOrDefault(s, s))); + + return formulaBuilder.toString(); + } + + /** + * 获取公式中的变量 + * + * @param formula 公式 + * @return 公式中的变量 + */ + public static Set getFormulaVariables(String formula) { + Expression compiledExp = AviatorEvaluator.compile(formula); + List variableNames = compiledExp.getVariableNames(); + return new HashSet<>(variableNames); + } + + /** + * 执行计算公式 + * + * @param formula 公式表达式 + * @param variableMap 变量-变量值map + * 注意:变量值是类型敏感的,举例如下 + * String formula = "A + B"; + * Map<,> variableMap = new HashMap<>(); + * variableMap.put("A", "1"); | variableMap.put("A", 1); + * variableMap.put("B", "2"); | variableMap.put("B", 2); + * ====> 结果为 "12" | ====> 结果为 3 + * @return 计算结果 + */ + public static Object executeFormula(String formula, Map variableMap) { + Expression compiledExp = AviatorEvaluator.compile(formula); + Object result = compiledExp.execute(variableMap); + if (Objects.isNull(result)) { + throw new SysException("execute to a null result"); + } + return result; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/HttpContextUtils.java b/common/core/src/main/java/com/thing/common/core/utils/HttpContextUtils.java new file mode 100644 index 0000000..5f48dc3 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/HttpContextUtils.java @@ -0,0 +1,101 @@ + + +package com.thing.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.constants.Constant; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + + +/** + * Http + * + * @author Mark sunlightcs@gmail.com + */ +public class HttpContextUtils { + + public static HttpServletRequest getHttpServletRequest() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if(requestAttributes == null){ + return null; + } + + return ((ServletRequestAttributes) requestAttributes).getRequest(); + } + + public static Map getParameterMap(HttpServletRequest request) { + Enumeration parameters = request.getParameterNames(); + + Map params = new HashMap<>(); + while (parameters.hasMoreElements()) { + String parameter = parameters.nextElement(); + String value = request.getParameter(parameter); + if (StringUtils.isNotBlank(value)) { + params.put(parameter, value); + } + } + + return params; + } + + public static String getDomain(){ + HttpServletRequest request = getHttpServletRequest(); + StringBuffer url = request.getRequestURL(); + return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString(); + } + + public static String getOrigin(){ + HttpServletRequest request = getHttpServletRequest(); + if(ObjectUtil.isNull(request)) return null; + return request.getHeader(HttpHeaders.ORIGIN); + } + + public static Long getTenantCode(){ + HttpServletRequest request = getHttpServletRequest(); + if(ObjectUtil.isNull(request)) return null; + String tenantCode = request.getHeader(Constant.TENANT_CODE); + return StringUtils.isNotBlank(tenantCode) ? Long.parseLong(tenantCode) : null; + } + + + public static Long getCompanyId() { + HttpServletRequest request = getHttpServletRequest(); + if(ObjectUtil.isNull(request)) return null; + String companyId = request.getHeader(Constant.TENANT_CODE); + return StringUtils.isNotBlank(companyId) ? Long.parseLong(companyId) : null; + } + + public static String getToken(){ + HttpServletRequest request = getHttpServletRequest(); + if(ObjectUtil.isNull(request)) return null; + return request.getHeader(Constant.TOKEN_HEADER); + } + + public static String getLanguage() { + //默认语言 + String defaultLanguage = "zh-CN"; +// //request +// HttpServletRequest request = getHttpServletRequest(); +// if(request == null){ +// return defaultLanguage; +// } +// +// //请求语言 +// defaultLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE); + + return defaultLanguage; + } + public static String getConsumerPid() { + HttpServletRequest request = getHttpServletRequest(); + return request.getHeader(Constant.CONSUMER_PID); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/utils/IpUtils.java b/common/core/src/main/java/com/thing/common/core/utils/IpUtils.java new file mode 100644 index 0000000..4230767 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/IpUtils.java @@ -0,0 +1,151 @@ + + +package com.thing.common.core.utils; + +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.SocketException; +import java.util.Enumeration; + +/** + * IP地址 + * + * @author Mark sunlightcs@gmail.com + */ +public class IpUtils { + private static final Logger logger = LoggerFactory.getLogger(IpUtils.class); + + + public static String getInternalIp(HttpServletRequest request) { + String unknown = "unknown"; + String ip = null; + try { + ip = request.getHeader("Public-Ip"); + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("x-forwarded-for"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } catch (Exception e) { + logger.error("IPUtils ERROR ", e); + } + + return ip; + } + + /** + * 获取IP地址 + * + * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址 + * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址 + */ + public static String getIpAddr(HttpServletRequest request) { + String unknown = "unknown"; + String ip = null; + try { + ip = request.getHeader("x-forwarded-for"); + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || ip.length() == 0 || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (StringUtils.isEmpty(ip) || unknown.equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + } catch (Exception e) { + logger.error("IPUtils ERROR ", e); + } + + return ip; + } + + /** + * 获取本机局域网内ipv4 + */ + public static String getLocalIpV4() { + try { + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface netInterface = networkInterfaces.nextElement(); + if(!netInterface.isUp() || netInterface.isLoopback()){ + continue; + } + Enumeration inetAddresses = netInterface.getInetAddresses(); + while (inetAddresses.hasMoreElements()) { + InetAddress address = inetAddresses.nextElement(); + if (!address.isLinkLocalAddress() && address instanceof Inet4Address) { + return address.getHostAddress(); + } + } + } + } catch (Exception ignore) { + } + return null; + } + + + /** + * 获取本机的内网ip地址 + * + * @return + * @throws SocketException + */ + public static String getInnetIp() throws SocketException { + String localip = null;// 本地IP,如果没有配置外网IP则返回它 + String netip = null;// 外网IP + Enumeration netInterfaces; + netInterfaces = NetworkInterface.getNetworkInterfaces(); + InetAddress ip = null; + boolean finded = false;// 是否找到外网IP + while (netInterfaces.hasMoreElements() && !finded) { + NetworkInterface ni = netInterfaces.nextElement(); + Enumeration address = ni.getInetAddresses(); + while (address.hasMoreElements()) { + ip = address.nextElement(); + if (!ip.isSiteLocalAddress() + &&!ip.isLoopbackAddress() + &&ip.getHostAddress().indexOf(":") == -1){// 外网IP + netip = ip.getHostAddress(); + finded = true; + break; + } else if (ip.isSiteLocalAddress() + &&!ip.isLoopbackAddress() + &&ip.getHostAddress().indexOf(":") == -1){// 内网IP + localip = ip.getHostAddress(); + } + } + } + if (netip != null && !"".equals(netip)) { + return netip; + } else { + return localip; + } + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/JacksonUtil.java b/common/core/src/main/java/com/thing/common/core/utils/JacksonUtil.java new file mode 100644 index 0000000..a2e8419 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/JacksonUtil.java @@ -0,0 +1,249 @@ +package com.thing.common.core.utils; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonWriteFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +public class JacksonUtil { + + public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + public static final ObjectMapper PRETTY_SORTED_JSON_MAPPER = JsonMapper.builder() + .enable(SerializationFeature.INDENT_OUTPUT) + .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true) + .configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true) + .build(); + public static ObjectMapper ALLOW_UNQUOTED_FIELD_NAMES_MAPPER = JsonMapper.builder() + .configure(JsonWriteFeature.QUOTE_FIELD_NAMES.mappedFeature(), false) + .configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true) + .build(); + + public static T convertValue(Object fromValue, Class toValueType) { + try { + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueType) : null; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("The given object value: " + + fromValue + " cannot be converted to " + toValueType, e); + } + } + + public static T convertValue(Object fromValue, TypeReference toValueTypeRef) { + try { + return fromValue != null ? OBJECT_MAPPER.convertValue(fromValue, toValueTypeRef) : null; + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("The given object value: " + + fromValue + " cannot be converted to " + toValueTypeRef, e); + } + } + + public static T fromString(String string, Class clazz) { + try { + return string != null ? OBJECT_MAPPER.readValue(string, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + string + " cannot be transformed to Json object", e); + } + } + + public static T fromString(String string, TypeReference valueTypeRef) { + try { + return string != null ? OBJECT_MAPPER.readValue(string, valueTypeRef) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + string + " cannot be transformed to Json object", e); + } + } + + public static T fromBytes(byte[] bytes, Class clazz) { + try { + return bytes != null ? OBJECT_MAPPER.readValue(bytes, clazz) : null; + } catch (IOException e) { + throw new IllegalArgumentException("The given string value: " + + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + } + } + + public static JsonNode fromBytes(byte[] bytes) { + try { + return OBJECT_MAPPER.readTree(bytes); + } catch (IOException e) { + throw new IllegalArgumentException("The given byte[] value: " + + Arrays.toString(bytes) + " cannot be transformed to Json object", e); + } + } + + public static String toString(Object value) { + try { + return value != null ? OBJECT_MAPPER.writeValueAsString(value) : null; + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("The given Json object value: " + + value + " cannot be transformed to a String", e); + } + } + + + public static String toPrettyString(Object o) { + try { + return PRETTY_SORTED_JSON_MAPPER.writeValueAsString(o); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + + public static T treeToValue(JsonNode node, Class clazz) { + try { + return OBJECT_MAPPER.treeToValue(node, clazz); + } catch (IOException e) { + throw new IllegalArgumentException("Can't convert value: " + node.toString(), e); + } + } + + public static JsonNode toJsonNode(String value) { + return toJsonNode(value, OBJECT_MAPPER); + } + + public static JsonNode toJsonNode(String value, ObjectMapper mapper) { + if (value == null || value.isEmpty()) { + return null; + } + try { + return mapper.readTree(value); + } catch (IOException e) { + throw new IllegalArgumentException(e); + } + } + + public static ObjectNode newObjectNode() { + return newObjectNode(OBJECT_MAPPER); + } + + public static ObjectNode newObjectNode(ObjectMapper mapper) { + return mapper.createObjectNode(); + } + + public static ArrayNode newArrayNode() { + return newArrayNode(OBJECT_MAPPER); + } + + public static ArrayNode newArrayNode(ObjectMapper mapper) { + return mapper.createArrayNode(); + } + + public static T clone(T value) { + @SuppressWarnings("unchecked") + Class valueClass = (Class) value.getClass(); + return fromString(toString(value), valueClass); + } + + public static JsonNode valueToTree(T value) { + return OBJECT_MAPPER.valueToTree(value); + } + + public static byte[] writeValueAsBytes(T value) { + try { + return OBJECT_MAPPER.writeValueAsBytes(value); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("The given Json object value: " + + value + " cannot be transformed to a String", e); + } + } + + public static byte[] writeValueAsPrettyBytes(T value) { + try { + return PRETTY_SORTED_JSON_MAPPER.writeValueAsBytes(value); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException("The given Json object value: " + + value + " cannot be transformed to a String", e); + } + } + + public static boolean filter(ObjectNode jsonObject, String fieldName,String fieldValue) { + if (StringUtils.isBlank(fieldName) || StringUtils.isBlank(fieldValue)) { + return true; + } + if (jsonObject.isNull() || jsonObject.isEmpty()) { + return false; + } + boolean has = jsonObject.has(fieldName); + if(!has){ + // System.out.println("======"+jsonObject+"======"+fieldName+"======"+fieldValue); + throw new IllegalArgumentException("The given Json object value has not "); + } + return jsonObject.get(fieldName).asText().contains(fieldValue); + } + + public static boolean filterOr(ObjectNode jsonObject,String fieldValue,String... fieldNames) { + if (StringUtils.isBlank(fieldValue) || fieldNames == null || fieldNames.length == 0) { + return true; + } + if (jsonObject.isNull() || jsonObject.isEmpty()) { + return false; + } + return Arrays.stream(fieldNames).anyMatch(fieldName -> jsonObject.has(fieldName) && jsonObject.get(fieldName).asText().contains(fieldValue)); + } + + public static boolean filter(ObjectNode jsonObject, List> filterList) { + for (Pair filter : filterList) { + String fieldName = filter.getLeft(); + String fieldValue = filter.getRight(); + if (StringUtils.isBlank(fieldName) || StringUtils.isBlank(fieldValue)) { + return true; + } + if (jsonObject == null || jsonObject.isEmpty()) { + return false; + } + if (!jsonObject.has(fieldName)) { + throw new IllegalArgumentException("The JSON object does not contain field: " + fieldName); + } + String actualValue = jsonObject.get(fieldName).asText(); + if (StringUtils.isBlank(actualValue)) { + return false; + } + if (!actualValue.contains(fieldValue)) { + return false; + } + } + return true; + } + + public static boolean filter(ObjectNode jsonObject, List> filterList,String timeField,Long beginTime, Long endTime) { + if(filter( jsonObject, filterList)){ + //时间的过滤 + return filterByDateRange(jsonObject,timeField,beginTime,endTime); + }; + return false; + } + + public static boolean filterByDateRange(ObjectNode jsonObject, String fieldName,Long beginTime, Long endTime) { + if (Objects.isNull(beginTime) || Objects.isNull(endTime)) { + return true; + } + if (jsonObject.isNull() || jsonObject.isEmpty()) { + return false; + } + boolean has = jsonObject.has(fieldName); + if(!has){ + // System.out.println("======"+jsonObject+"======"+fieldName+"======"+fieldValue); + throw new IllegalArgumentException("The given time Json object value has not "); + } + long createDate = jsonObject.get(fieldName).asLong(); + return createDate >= beginTime && createDate <= endTime; + } + + + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/JsonProcessingUtils.java b/common/core/src/main/java/com/thing/common/core/utils/JsonProcessingUtils.java new file mode 100644 index 0000000..a4b7529 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/JsonProcessingUtils.java @@ -0,0 +1,81 @@ +package com.thing.common.core.utils; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.FileUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 15:36 2022/6/16 + */ +public class JsonProcessingUtils { + + + /** + * json文件上传解析 + * + * @param multipartFile multipartFile + * @return + */ + public static String readJson(MultipartFile multipartFile) { + try { + String fileName = multipartFile.getOriginalFilename(); + File file = new File("/" + fileName); + FileUtils.copyInputStreamToFile(multipartFile.getInputStream(), file); + String jsonString = FileUtils.readFileToString(file, "UTF-8"); + return jsonString; + } catch (Exception e) { + return null; + } + } + + + /** + * json 文件生成下载 + * + * @param response response + * @param obj obj + * @param fileName fileName + */ + public static void exportJson(HttpServletResponse response, Object obj, String fileName) { + try { + + String jsonString = JSONObject.toJSONString(obj, SerializerFeature.PrettyFormat, SerializerFeature.WriteMapNullValue, + SerializerFeature.WriteDateUseDateFormat); + String fullPath = "/" + fileName; + File file = new File(fullPath); + + Writer write = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8); + write.write(jsonString); + write.flush(); + write.close(); + + FileInputStream fis = new FileInputStream(file); + response.setContentType("application/force-download"); + response.setHeader("Content-Disposition", "attachment;filename=" + .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8")))); + response.setCharacterEncoding("utf-8"); + + OutputStream os = response.getOutputStream(); + byte[] buf = new byte[1024]; + int len = 0; + while ((len = fis.read(buf)) != -1) { + os.write(buf, 0, len); + } + os.close(); + fis.close(); + + } catch (Exception e) { + e.getMessage(); + } + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/JsonUtils.java b/common/core/src/main/java/com/thing/common/core/utils/JsonUtils.java new file mode 100644 index 0000000..ac0f323 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/JsonUtils.java @@ -0,0 +1,148 @@ +package com.thing.common.core.utils; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.thing.common.core.exception.SysException; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + + +public class JsonUtils { + + public static JsonNode toJsonNode(String jsonString) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readTree(StringUtils.isNotBlank(jsonString)? jsonString : "{}"); + } + + + public static String jsonToString(Object obj) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(obj); + } + + public static T parseObject(String jsonString, Class var1) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonString, var1); + } + + public static Map stringToMap(String jsonString) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonString, Map.class); + } + + public static List jsonToList(String jsonString, Class cls) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + return mapper.readValue(jsonString, getCollectionType(List.class, cls)); + } + + public static T jsonConvertBean(String params, Class clazz) { + Map paramMap = JSON.parseObject(params, new TypeReference<>() {}); + T dto = BeanUtil.mapToBean(paramMap, clazz, false, new CopyOptions()); + return dto; + } + + private static JavaType getCollectionType(Class collectionClass, Class... elementClasses) { + ObjectMapper mapper = new ObjectMapper(); + return mapper.getTypeFactory().constructParametricType(collectionClass, elementClasses); + } + public static String jsonToStringPro(Object obj) throws JsonProcessingException { + ObjectMapper mapper = new ObjectMapper(); + //mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + return mapper.writeValueAsString(obj); + } + + public static void jsonValueToString(Object object) { + if(object instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) object; + for (Map.Entry stringObjectEntry : jsonObject.entrySet()) { + Object o = stringObjectEntry.getValue(); + if (o instanceof Long) { + jsonObject.put(String.valueOf(((Map.Entry) stringObjectEntry).getKey()), String.valueOf(stringObjectEntry.getValue())); + } else if (o instanceof Integer) { + jsonObject.put(String.valueOf(((Map.Entry) stringObjectEntry).getKey()), String.valueOf(stringObjectEntry.getValue())); + } else if (o instanceof JSONArray) { + jsonValueToString(o); + } else if (o instanceof JSONObject) { + jsonValueToString(o); + } else if (o instanceof ArrayList) { + jsonValueToString(o); + } + } + } + if(object instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) object; + for (Object o : jsonArray) { + jsonValueToString(o); + } + } + if(object instanceof ArrayList) { + List jsonList = (List) object; + for (Object o : jsonList) { + jsonValueToString(o); + } + } + } + + public static void exportJson(JSONObject json, HttpServletResponse response){ + try { + byte[] data = JSON.toJSONString(json, true).getBytes(StandardCharsets.UTF_8); + response.reset(); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Content-Disposition", "attachment; filename=" + UUID.randomUUID() + ".json"); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } catch (IOException e) { + e.printStackTrace(); + throw new SysException("导出失败: " + e.getMessage()); + } + } + + /** + * 获取俩个json的交集 + * @param superStr 父集合 + * @param subStr 子集合 + * @return 交集 + */ + public static String getIntersection(String superStr, String subStr) { + JSONObject jsonObject1 = JSONObject.parseObject(superStr); + JSONObject jsonObject2 = JSONObject.parseObject(subStr); + // 创建一个新的 JSONObject 用于存储交集结果 + JSONObject intersection = new JSONObject(); + + // 遍历第一个 JSONObject 的键值对 + for (String key : jsonObject1.keySet()) { + // 如果第二个 JSONObject 中包含相同的键 + if (jsonObject2.containsKey(key)) { + // 检查对应的值是否相同 + if (jsonObject1.get(key).equals(jsonObject2.get(key))) { + // 如果值相同,则将键值对添加到交集结果中 + intersection.put(key, jsonObject1.get(key)); + }else{ + intersection.put(key, jsonObject2.get(key)); + } + } + } + return JSON.toJSONString(intersection); + } + + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/JwtUtils.java b/common/core/src/main/java/com/thing/common/core/utils/JwtUtils.java new file mode 100644 index 0000000..618a81a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/JwtUtils.java @@ -0,0 +1,39 @@ +package com.thing.common.core.utils; + +import cn.hutool.core.codec.Base64; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.exception.SysException; +import org.apache.commons.lang3.StringUtils; + +/** + * @author zhenghh. 2022-06-06 + **/ +public class JwtUtils { + + /** + * 获取token + * @param token token + * @return token + */ + public static JSONObject getPayload(String token) { + if(StringUtils.isBlank(token) || !token.contains(".")) { + throw new SysException("token格式不合法"); + } + return JSON.parseObject(new String(Base64.decode(token.split("\\.")[1]))); + } + + /** + * 获取过期时间 + * @param token token + * @return 过期时间 + */ + public static long getExp(String token) { + return getPayload(token).getLongValue("exp") * 1000L; + } + + public static void main(String[] args) { + System.out.println(getExp("eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJhZG1pbkBkYXRheC5jb20iLCJzY29wZXMiOlsiVEVOQU5UX0FETUlOIl0sInVzZXJJZCI6IjAwNjgyZDAwLTk5MGItMTFlYy05YjJkLTU3MzYzMGZjMDBiZSIsImVuYWJsZWQiOnRydWUsImlzUHVibGljIjpmYWxzZSwidGVuYW50SWQiOiIwMDY0ODM4MC05OTBiLTExZWMtOWIyZC01NzM2MzBmYzAwYmUiLCJjdXN0b21lcklkIjoiMTM4MTQwMDAtMWRkMi0xMWIyLTgwODAtODA4MDgwODA4MDgwIiwiaXNzIjoidGhpbmdzYm9hcmQuaW8iLCJpYXQiOjE2NTQ0Nzk1MjUsImV4cCI6MTY1NDQ4ODUyNX0.MxDfRtHKD63og0nFwaorASkqyx6E3YDcvkHjKLradvIEM8SmzrneIqUuG2_HFtwufpLqgCf_WSw9esHBcogUkA")); + + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/KeyGeneratorUtils.java b/common/core/src/main/java/com/thing/common/core/utils/KeyGeneratorUtils.java new file mode 100644 index 0000000..7f23501 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/KeyGeneratorUtils.java @@ -0,0 +1,30 @@ +package com.thing.common.core.utils; + +import java.util.ArrayList; +import java.util.List; + +public class KeyGeneratorUtils { + + public static List generateKeys(int count) { + List keys = new ArrayList<>(); + + for (int i = 0; i < count; i++) { + keys.add(generateKey(i)); + } + + return keys; + } + + private static String generateKey(int index) { + StringBuilder sb = new StringBuilder(); + + while (index >= 0) { + sb.insert(0, (char) ('A' + (index % 26))); + index /= 26; + index--; + } + + return sb.toString(); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/MapUtil.java b/common/core/src/main/java/com/thing/common/core/utils/MapUtil.java new file mode 100644 index 0000000..c0c8927 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/MapUtil.java @@ -0,0 +1,33 @@ +package com.thing.common.core.utils; + +import com.google.common.collect.Maps; + +import org.springframework.util.CollectionUtils; + +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-22 + * @description 列表转map、分组等 + */ +public class MapUtil { + + public static Map toMap(List list, Function keyMapper) { + if (CollectionUtils.isEmpty(list)) { + return Maps.newHashMap(); + } + return list.stream() + .collect(Collectors.toMap(keyMapper, Function.identity(), (v1, v2) -> v1)); + } + + public static Map> group(List list, Function keyMapper) { + if (CollectionUtils.isEmpty(list)) { + return Maps.newHashMap(); + } + return list.stream().collect(Collectors.groupingBy(keyMapper)); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/MessageUtils.java b/common/core/src/main/java/com/thing/common/core/utils/MessageUtils.java new file mode 100644 index 0000000..d97c15a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/MessageUtils.java @@ -0,0 +1,27 @@ + + +package com.thing.common.core.utils; + +import org.springframework.context.MessageSource; +import org.springframework.context.i18n.LocaleContextHolder; + +/** + * 国际化 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class MessageUtils { + private static MessageSource messageSource; + static { + messageSource = (MessageSource)SpringContextUtils.getBean("messageSource"); + } + + public static String getMessage(int code){ + return getMessage(code, new String[0]); + } + + public static String getMessage(int code, String... params){ + return messageSource.getMessage(code+"", params, LocaleContextHolder.getLocale()); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/PageUtils.java b/common/core/src/main/java/com/thing/common/core/utils/PageUtils.java new file mode 100644 index 0000000..5989eb8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/PageUtils.java @@ -0,0 +1,99 @@ + +package com.thing.common.core.utils; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; + + +import java.util.List; + +/** + * 分页工具 + * + * @author Mark sunlightcs@gmail.com + */ +public class PageUtils { + + /** + * 开始分页 + * @param list + * @param pageNum 页码 + * @param pageSize 每页多少条数据 + * @return 返回值为null请检查入参 + */ + public static List startPage(List list, Integer pageNum, + Integer pageSize) { + if (list == null || ObjectUtil.isNull(pageNum) || ObjectUtil.isNull(pageSize) + || pageNum < 1 || pageSize < 1) { + return null; + } + if((list.size() == 0 || (list.size()/pageSize + 1) * pageSize < pageNum * pageSize)){ + return Lists.newArrayList(); + } + + Integer count = list.size(); // 记录总数 + Integer pageCount = 0; // 页数 + if (count % pageSize == 0) { + pageCount = count / pageSize; + } else { + pageCount = count / pageSize + 1; + } + + int fromIndex = 0; // 开始索引 + int toIndex = 0; // 结束索引 + pageNum = Math.min(pageNum, pageCount); + if (!pageNum.equals(pageCount)) { + fromIndex = (pageNum - 1) * pageSize; + toIndex = fromIndex + pageSize; + } else { + fromIndex = (pageNum - 1) * pageSize; + toIndex = count; + } + + return list.subList(fromIndex, Math.min(toIndex, list.size())); + } + + /** + * 开始分页 + * @param list + * @param pageNum 页码 + * @param pageSize 每页多少条数据 + * @return 返回值为null请检查入参 + */ + public static List startPageSort(List list, + Integer pageNum, + Integer pageSize, + String orderField, + String order) { + if (list == null || ObjectUtil.isNull(pageNum) || ObjectUtil.isNull(pageSize) + || pageNum < 1 || pageSize < 1) { + return null; + } + if((list.size() == 0 || (list.size()/pageSize + 1) * pageSize < pageNum * pageSize)){ + return Lists.newArrayList(); + } + + Integer count = list.size(); // 记录总数 + Integer pageCount = 0; // 页数 + if (count % pageSize == 0) { + pageCount = count / pageSize; + } else { + pageCount = count / pageSize + 1; + } + + int fromIndex = 0; // 开始索引 + int toIndex = 0; // 结束索引 + pageNum = Math.min(pageNum, pageCount); + if (!pageNum.equals(pageCount)) { + fromIndex = (pageNum - 1) * pageSize; + toIndex = fromIndex + pageSize; + } else { + fromIndex = (pageNum - 1) * pageSize; + toIndex = count; + } + + return list.subList(fromIndex, Math.min(toIndex, list.size())); + } + + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/RandomUtils.java b/common/core/src/main/java/com/thing/common/core/utils/RandomUtils.java new file mode 100644 index 0000000..efa58c0 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/RandomUtils.java @@ -0,0 +1,23 @@ +package com.thing.common.core.utils; + +import java.util.concurrent.ThreadLocalRandom; + +public class RandomUtils { + + private static final String CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + private static final int DEFAULT_LENGTH = 8; + + public static String getRandomString(int length) { + StringBuilder sb = new StringBuilder(length); + ThreadLocalRandom random = ThreadLocalRandom.current(); + for (int i = 0; i < length; i++) { + int randomIndex = random.nextInt(CHARACTERS.length()); + sb.append(CHARACTERS.charAt(randomIndex)); + } + return sb.toString(); + } + + public static String getRandomString() { + return getRandomString(DEFAULT_LENGTH); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/utils/RegexUtils.java b/common/core/src/main/java/com/thing/common/core/utils/RegexUtils.java new file mode 100644 index 0000000..6ffe589 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/RegexUtils.java @@ -0,0 +1,23 @@ +package com.thing.common.core.utils; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +import java.util.function.UnaryOperator; +import java.util.regex.Pattern; + +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class RegexUtils { + + public static final Pattern UUID_PATTERN = Pattern.compile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"); + + + public static String replace(String s, Pattern pattern, UnaryOperator replacer) { + return pattern.matcher(s).replaceAll(matchResult -> replacer.apply(matchResult.group())); + } + + public static boolean matches(String input, Pattern pattern) { + return pattern.matcher(input).matches(); + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/RestTemplateUtils.java b/common/core/src/main/java/com/thing/common/core/utils/RestTemplateUtils.java new file mode 100644 index 0000000..2ac49ef --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/RestTemplateUtils.java @@ -0,0 +1,578 @@ +package com.thing.common.core.utils; + +/* +* restTemplate工具类 +* */ + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.web.client.RestTemplate; + +import java.util.Map; + +@Slf4j +public class RestTemplateUtils { + + private static final RestTemplate restTemplate =new RestTemplate(); + + /*----------------------------------GET-------------------------------------------------------*/ + /** + * 请求调⽤⽅式 GET请求 + * @param url URL + * @param responseType 返回对象类型 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntity get(String url, Class responseType){ + return restTemplate.getForEntity(url, responseType); + } + + /** + * 请求调⽤⽅式 GET请求 + * @param url URL + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityget(String url, Class responseType, Object... uriVariables){ + return restTemplate.getForEntity(url, responseType, uriVariables); + } + + /** + 请求调⽤⽅式 GET请求 + * @param url URL + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityget(String url, Class responseType, Map uriVariables){ + return restTemplate.getForEntity(url, responseType, uriVariables); + } + + /** + * 带请求头的GET请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity响应对象封装类 + */ + public static ResponseEntityget(String url, Map headers, Class responseType, Object... uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return get(url, httpHeaders, responseType, uriVariables); + } + + /** + * 带请求头的GET请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityget(String url, HttpHeaders headers, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity<>(headers); + return exchange(url, HttpMethod.GET, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的GET请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityget(String url, Map headers, Class responseType, Map uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return get(url, httpHeaders, responseType, uriVariables); + } + + /** + * 带请求头的GET请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityget(String url, HttpHeaders headers, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity<>(headers); + return exchange(url, HttpMethod.GET, requestEntity, responseType, uriVariables); + } + + + /*----------------------------------POST-------------------------------------------------------*/ + /** + * POST请求调⽤⽅式 + * @param url URL + * @param responseType 返回对象类型 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Class responseType){ + return restTemplate.postForEntity(url, HttpEntity.EMPTY, responseType); + } + + /** + * POST请求调⽤⽅式 + * @param url URL + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Object requestBody, Class responseType){ + return restTemplate.postForEntity(url, requestBody, responseType); + } + + /** + * POST请求调⽤⽅式 + * @param url URL + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Object requestBody, Class responseType, Object... uriVariables){ + return restTemplate.postForEntity(url, requestBody, responseType, uriVariables); + } + + /** + * POST请求调⽤⽅式 + * @param url URL + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Object requestBody, Class responseType, Map uriVariables){ + return restTemplate.postForEntity(url, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的POST请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Map headers, Object requestBody, Class responseType, Object... uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return post(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的POST请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, HttpHeaders headers, Object requestBody, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return post(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的POST请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, Map headers, Object requestBody, Class responseType, Map uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return post(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的POST请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数体 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, HttpHeaders headers, Object requestBody, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return post(url, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的POST请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装对象参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, HttpEntity requestEntity, Class responseType, Object... uriVariables){ + return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的POST请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装对象参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitypost(String url, HttpEntity requestEntity, Class responseType, Map uriVariables){ + return restTemplate.exchange(url, HttpMethod.POST, requestEntity, responseType, uriVariables); + } + + /*----------------------------------PUT-------------------------------------------------------*/ + /** + * PUT请求调⽤⽅式 + * @param url URL + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, Class responseType, Object... uriVariables){ + return put(url, HttpEntity.EMPTY, responseType, uriVariables); + } + + /** + * PUT请求调⽤⽅式 + * @param url URL + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, Object requestBody, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody); + return put(url, requestEntity, responseType, uriVariables); + } + + /** + * PUT请求调⽤⽅式 + * @param url URL + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, Object requestBody, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody); + return put(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的PUT请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, Map headers, Object requestBody, Class responseType, Object... uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return put(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的PUT请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, HttpHeaders headers, Object requestBody, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return put(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的PUT请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, Map headers, Object requestBody, Class responseType, Map uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return put(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的PUT请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求参数提 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, HttpHeaders headers, Object requestBody, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return put(url, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的PUT请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装对象参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, HttpEntity requestEntity, Class responseType, Object... uriVariables){ + return restTemplate.exchange(url, HttpMethod.PUT, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的PUT请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装对象参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityput(String url, HttpEntity requestEntity, Class responseType, Map uriVariables){ + return restTemplate.exchange(url, HttpMethod.PUT, requestEntity, responseType, uriVariables); + } + + + /*----------------------------------DELETE-------------------------------------------------------*/ + /** + * DELETE请求调⽤⽅式 + * @param url URL + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Class responseType, Object... uriVariables){ + return delete(url, HttpEntity.EMPTY, responseType, uriVariables); + } + + /** + * DELETE请求调⽤⽅式 + * @param url URL + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Class responseType, Map uriVariables){ + return delete(url, HttpEntity.EMPTY, responseType, uriVariables); + } + + /** + * DELETE请求调⽤⽅式 + * @param url URL + * @param requestBody requestBody请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Object requestBody, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * DELETE请求调⽤⽅式 + * @param url URL + * @param requestBody requestBody请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Object requestBody, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Map headers, Class responseType, Object... uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return delete(url, httpHeaders, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpHeaders headers, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(headers); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Map headers, Class responseType, Map uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return delete(url, httpHeaders, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpHeaders headers, Class responseType, Map uriVariables){ + HttpEntity requestEntity =new HttpEntity(headers); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Map headers, Object requestBody, Class responseType, Object... uriVariables) + { + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return delete(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpHeaders headers, Object requestBody, Class responseType, Object... uriVariables){ + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, Map headers, Object requestBody, Class responseType, Map uriVariables){ + HttpHeaders httpHeaders =new HttpHeaders(); + httpHeaders.setAll(headers); + return delete(url, httpHeaders, requestBody, responseType, uriVariables); + } + + /** + * 带请求头的DELETE请求调⽤⽅式 + * @param url URL + * @param headers 请求头参数 + * @param requestBody 请求体参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpHeaders headers, Object requestBody, Class responseType, Map uriVariables) { + HttpEntity requestEntity =new HttpEntity(requestBody, headers); + return delete(url, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的DELETE请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpEntity requestEntity, Class responseType, Object... uriVariables){ + return restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, responseType, uriVariables); + } + + /** + * 自定义请求头和请求体的DELETE请求调⽤⽅式 + * @param url URL + * @param requestEntity 请求头和请求体封装参数 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntitydelete(String url, HttpEntity requestEntity, Class responseType, Map uriVariables){ + return restTemplate.exchange(url, HttpMethod.DELETE, requestEntity, responseType, uriVariables); + } + + /*----------------------------------通⽤⽅法-------------------------------------------------------*/ + /** + * 通⽤调⽤⽅式 + * @param url URL + * @param method 请求⽅法类型 + * @param requestEntity 请求头和请求体封装对象 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,按顺序依次对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityexchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Object... uriVariables){ + return restTemplate.exchange(url, method, requestEntity, responseType, uriVariables); + } + + /** + * 通⽤调⽤⽅式 + * @param url URL + * @param method 请求⽅法类型 + * @param requestEntity 请求头和请求体封装对象 + * @param responseType 返回对象类型 + * @param uriVariables URL中的变量,与Map中的key对应 + * @return ResponseEntity 响应对象封装类 + */ + public static ResponseEntityexchange(String url, HttpMethod method, HttpEntity requestEntity, Class responseType, Map uriVariables){ + return restTemplate.exchange(url, method, requestEntity, responseType, uriVariables); + } + + /** + * 获取RestTemplate实例对象,可⾃由调⽤其⽅法 + * @return RestTemplate实例对象 + */ + public static RestTemplate getRestTemplate(){ + return restTemplate; + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/ResultCommonUtils.java b/common/core/src/main/java/com/thing/common/core/utils/ResultCommonUtils.java new file mode 100644 index 0000000..bcb6c02 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/ResultCommonUtils.java @@ -0,0 +1,64 @@ +package com.thing.common.core.utils; + + +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.web.response.Result; + +public class ResultCommonUtils { + + /** + * 新增通用返回值 + * @Author zl + * @Date 2020/8/6 下午5:56 + * @param flag + * @return io.renren.common.utils.Result + */ + public static Result addResult(boolean flag) { + if(flag) { + return new Result().ok("新增成功"); + } else { + return new Result().error(ErrorCode.NOT_OK_CODE, "新增失败"); + } + } + + /** + * 更新通用返回值 + * @Author zl + * @Date 2020/8/6 下午5:57 + * @param flag + * @return io.renren.common.utils.Result + */ + public static Result updateResult(boolean flag) { + if(flag) { + return new Result().ok("更新成功"); + } else { + return new Result().error(ErrorCode.NOT_OK_CODE, "更新失败"); + } + } + + /** + * 删除通用返回值 + * @Author zl + * @Date 2020/8/6 下午6:00 + * @param flag + * @return io.renren.common.utils.Result + */ + public static Result deleteResult(boolean flag) { + if(flag) { + return new Result().ok("删除成功"); + } else { + return new Result().error(ErrorCode.NOT_OK_CODE, "删除失败"); + } + } + + /** + * 返回失败信息 + * @Author zl + * @Date 2020/8/6 下午6:06 + * @param msg + * @return io.renren.common.utils.Result + */ + public static Result error(String msg) { + return new Result().error(ErrorCode.NOT_OK_CODE, msg); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/SpringContextUtils.java b/common/core/src/main/java/com/thing/common/core/utils/SpringContextUtils.java new file mode 100644 index 0000000..a76ba50 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/SpringContextUtils.java @@ -0,0 +1,49 @@ + + +package com.thing.common.core.utils; + +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +/** + * Spring Context 工具类 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +public class SpringContextUtils implements ApplicationContextAware { + public static ApplicationContext applicationContext; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + SpringContextUtils.applicationContext = applicationContext; + } + + public static Object getBean(String name) { + return applicationContext.getBean(name); + } + + public static T getBean(Class requiredType) { + return applicationContext.getBean(requiredType); + } + + public static T getBean(String name, Class requiredType) { + return applicationContext.getBean(name, requiredType); + } + + public static boolean containsBean(String name) { + return applicationContext.containsBean(name); + } + + public static boolean isSingleton(String name) { + return applicationContext.isSingleton(name); + } + + public static Class getType(String name) { + return applicationContext.getType(name); + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/utils/SqlUtil.java b/common/core/src/main/java/com/thing/common/core/utils/SqlUtil.java new file mode 100644 index 0000000..21d3a5e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/SqlUtil.java @@ -0,0 +1,62 @@ +package com.thing.common.core.utils; + + +import cn.hutool.core.exceptions.UtilException; +import org.apache.commons.lang3.StringUtils; + +/** + * sql操作工具类 + * + * @author ruoyi + */ +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/common/core/src/main/java/com/thing/common/core/utils/TokenGenerator.java b/common/core/src/main/java/com/thing/common/core/utils/TokenGenerator.java new file mode 100644 index 0000000..291096f --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/TokenGenerator.java @@ -0,0 +1,45 @@ +package com.thing.common.core.utils; + + +import com.thing.common.core.exception.SysException; + +import java.security.MessageDigest; +import java.util.UUID; + +/** + * 生成token + * + * @author Mark sunlightcs@gmail.com + */ +public class TokenGenerator { + + public static String generateValue() { + return generateValue(UUID.randomUUID().toString()); + } + + private static final char[] HEX_CODE = "0123456789abcdef".toCharArray(); + + public static String toHexString(byte[] data) { + if (data == null) { + return null; + } + StringBuilder r = new StringBuilder(data.length * 2); + for (byte b : data) { + r.append(HEX_CODE[(b >> 4) & 0xF]); + r.append(HEX_CODE[(b & 0xF)]); + } + return r.toString(); + } + + public static String generateValue(String param) { + try { + MessageDigest algorithm = MessageDigest.getInstance("MD5"); + algorithm.reset(); + algorithm.update(param.getBytes()); + byte[] messageDigest = algorithm.digest(); + return toHexString(messageDigest); + } catch (Exception e) { + throw new SysException("token invalid", e); + } + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/TreeUtils.java b/common/core/src/main/java/com/thing/common/core/utils/TreeUtils.java new file mode 100644 index 0000000..598b93b --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/TreeUtils.java @@ -0,0 +1,174 @@ + + +package com.thing.common.core.utils; + + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.utils.params.SortTreeNode; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.utils.params.TreeNode; + +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * 树形结构工具类,如:菜单、部门等 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class TreeUtils { + + /** + * 根据pid,构建树节点 + * 可以生成多棵树,物关系采用新的表结构后,如果不指定顶级节点(topNodeId),则一个节点下面可能会存在多个相同子节点 + */ + public static > List build(List treeNodes, Long pid) { + //pid不能为空 + AssertUtils.isNull(pid, "pid"); + + List treeList = new ArrayList<>(); + for(T treeNode : treeNodes) { + if (pid.equals(treeNode.getPid())) { + treeList.add(findChildren(treeNodes, treeNode)); + } + } + + return treeList; + } + + /** + * 查找子节点 + */ + private static > T findChildren(List treeNodes, T rootNode) { + Long topNodeId = rootNode.getTopNodeId(); + List subTreeNodes = treeNodes; + if (Objects.nonNull(topNodeId)) { + subTreeNodes = + treeNodes.stream() + .filter(n -> Objects.equals(n.getTopNodeId(), topNodeId)) + .collect(Collectors.toList()); + } + for(T treeNode : subTreeNodes) { + if(rootNode.getId().equals(treeNode.getPid())) { + rootNode.getChildren().add(findChildren(subTreeNodes, treeNode)); + } + } + return rootNode; + } + + /** + * 构建树节点 + */ + public static > List build(List treeNodes) { + List result = new ArrayList<>(); + + //list转map + Map nodeMap = new LinkedHashMap<>(treeNodes.size()); + treeNodes.stream() + .filter(Objects::nonNull) + .forEach(node -> nodeMap.put(node.getId(), node)); + + for(T node : nodeMap.values()) { + T parent = nodeMap.get(node.getPid()); + if(parent != null && !(node.getId().equals(parent.getId()))){ + parent.getChildren().add(node); + continue; + } + + result.add(node); + } + + return result; + } + + + /** + * 一个节点不允许被生成在多颗树中, 因此需要保证调用该方法时 treeNodes 全都是同一颗树中的节点 + */ + public static > List buildSortTree(List treeNodes, Long pid) { + //pid不能为空 + AssertUtils.isNull(pid, "pid"); + + List treeList = new ArrayList<>(); + for (int i = 0; i < treeNodes.size(); i++) { + T treeNode = treeNodes.get(i); + if(Objects.isNull(treeNode)){ + continue; + } + if (pid.equals(treeNode.getPid())) { + treeNodes.set(i, null); + T children = findAndSortChildren(treeNodes, treeNode, 1); + children.setDepth(1); + treeList.add(children); + } + } + sort(treeList); + return treeList; + } + + /** + * 递归操作所有的节点 + * @param treeNode 树的某个节点 + * @param consumer 对每个节点的操作 + */ + public static > void recursiveOperation( + TreeNode treeNode, Consumer> consumer) { + consumer.accept(treeNode); + List children = treeNode.getChildren(); + for (T child : children) { + recursiveOperation(child, consumer); + } + } + + /** + * 节点自身的排序 + * @param treeNodes 需要排序的树节点列表 + * @param 排序树类型 + */ + public static > void sort(List treeNodes) { + treeNodes.sort(Comparator.comparing(SortTreeNode::getSort)); + treeNodes.forEach( + tree -> { + List children = tree.getChildren(); + if (!children.isEmpty()) { + sort(children); + } + }); + } + + public static > List flatTree(List treeNodes) { + List collector = new ArrayList<>(); + flatTree(treeNodes, collector); + return collector; + } + + private static > void flatTree(List treeNodes, List collector) { + collector = Optional.ofNullable(collector).orElse(new ArrayList<>()); + for (T treeNode : treeNodes) { + collector.add(treeNode); + List children = treeNode.getChildren(); + if (!children.isEmpty()) { + flatTree(children, collector); + } + } + } + + private static > T findAndSortChildren(List treeNodes, T rootNode, int fatherDepth) { + for (int i = 0; i < treeNodes.size(); i++) { + T treeNode = treeNodes.get(i); + if (Objects.isNull(treeNode)) { + continue; + } + if(rootNode.getId().equals(treeNode.getPid())) { + treeNodes.set(i, null); + treeNode.setSort(rootNode.getSort() + treeNode.getSort()); + treeNode.setDepth(fatherDepth + 1); + rootNode.getChildren().add(findAndSortChildren(treeNodes, treeNode, fatherDepth + 1)); + } + } + return rootNode; + } + +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelExportUtil.java b/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelExportUtil.java new file mode 100644 index 0000000..eac496d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelExportUtil.java @@ -0,0 +1,168 @@ +package com.thing.common.core.utils.excel; + +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.TemplateExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelBatchExportService; +import cn.afterturn.easypoi.excel.export.ExcelExportService; +import cn.afterturn.easypoi.excel.export.template.ExcelExportOfTemplateUtil; +import cn.afterturn.easypoi.handler.inter.IExcelExportServer; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 重写导出方法 + * + */ +public class ExcelExportUtil { + public static int USE_SXSSF_LIMIT = 100000; + public static final String SHEET_NAME = "sheetName"; + + private ExcelExportUtil() { + } + + /** + * 大数据量导出 + * + * @param entity 表格标题属性 + * @param pojoClass Excel对象Class + * @param server 查询数据的接口 + * @param queryParams 查询数据的参数 + */ + public static Workbook exportBigExcel(ExportParams entity, Class pojoClass, + IExcelExportServer server, Object queryParams) { + ExcelBatchExportService batchServer = new ExcelBatchExportService(); + batchServer.init(entity, pojoClass); + return batchServer.exportBigExcel(server, queryParams); + } + + /** + * 大数据量导出 + * + * @param entity + * @param excelParams + * @param server 查询数据的接口 + * @param queryParams 查询数据的参数 + * @return + */ + public static Workbook exportBigExcel(ExportParams entity, List excelParams, + IExcelExportServer server, Object queryParams) { + ExcelBatchExportService batchServer = new ExcelBatchExportService(); + batchServer.init(entity, excelParams); + return batchServer.exportBigExcel(server, queryParams); + } + + + /** + * @param entity 表格标题属性 + * @param pojoClass Excel对象Class + * @param dataSet Excel对象数据List + */ + public static Workbook exportExcel(ExportParams entity, Class pojoClass, + Collection dataSet) { + Workbook workbook = getWorkbook(entity.getType(), dataSet.size()); + new ExcelExportService().createSheet(workbook, entity, pojoClass, dataSet); + return workbook; + } + + private static Workbook getWorkbook(ExcelType type, int size) { + if (ExcelType.HSSF.equals(type)) { + return new HSSFWorkbook(); + } else if (size < USE_SXSSF_LIMIT) { + return new XSSFWorkbook(); + } else { + return new SXSSFWorkbook(); + } + } + + /** + * 根据Map创建对应的Excel + * + * @param entity 表格标题属性 + * @param entityList Map对象列表 + * @param dataSet Excel对象数据List + */ + public static Workbook exportExcel(ExportParams entity, List entityList, + Collection dataSet) { + Workbook workbook = getWorkbook(entity.getType(), dataSet.size()); + new ExcelExportService().createSheetForMap(workbook, entity, entityList, dataSet); + return workbook; + } + + /** + * 根据Map创建对应的Excel(一个excel 创建多个sheet) + * + * @param list 多个Map key title 对应表格Title key entity 对应表格对应实体 key data + * Collection 数据 + * @return + */ + public static Workbook exportExcel(List> list, ExcelType type) { + Workbook workbook = getWorkbook(type, 0); + for (Map map : list) { + ExcelExportService service = new ExcelExportService(); + service.createSheet(workbook, (ExportParams) map.get("title"), + (Class) map.get("entity"), (Collection) map.get("data")); + } + return workbook; + } + + /** + * 导出文件通过模板解析,不推荐这个了,推荐全部通过模板来执行处理 + * + * @param params 导出参数类 + * @param pojoClass 对应实体 + * @param dataSet 实体集合 + * @param map 模板集合 + * @return + */ + @Deprecated + public static Workbook exportExcel(TemplateExportParams params, Class pojoClass, + Collection dataSet, Map map) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, pojoClass, dataSet, + map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcel(TemplateExportParams params, Map map) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, null, null, map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * 每个sheet对应一个map,导出到处,key是sheet的NUM + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcel(Map> map, + TemplateExportParams params) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * 每个sheet对应一个list,按照数量进行导出排序,key是sheet的NUM + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcelClone(Map>> map, + TemplateExportParams params) { + return new ExcelExportOfTemplateUtil().createExcelCloneByTemplate(params, map); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelUtils.java b/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelUtils.java new file mode 100644 index 0000000..70d57e5 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/excel/ExcelUtils.java @@ -0,0 +1,203 @@ +package com.thing.common.core.utils.excel; + +import cn.afterturn.easypoi.excel.ExcelImportUtil; +import cn.afterturn.easypoi.excel.annotation.ExcelTarget; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.ImportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelExportService; +import cn.afterturn.easypoi.exception.excel.ExcelExportException; +import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum; +import cn.afterturn.easypoi.util.PoiPublicUtil; +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.exception.SysException; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.net.URLEncoder; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + + +/** + * Excel导入导出公共工具类 + * @author ChenYang + */ +@Slf4j +public class ExcelUtils { + + public static void exportExcel(List list, String title, String sheetName, Class pojoClass, String fileName, boolean isCreateHeader, HttpServletResponse response){ + ExportParams exportParams = new ExportParams(title, sheetName); + exportParams.setCreateHeadRows(isCreateHeader); + defaultExport(list, pojoClass, fileName, response, exportParams); + + } + + public static void exportExcel(List list, String title, String sheetName, Class pojoClass, String fileName, HttpServletResponse response){ + defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName)); + } + + + public static void exportExcel(List list, Class pojoClass, String title, String sheetName, String fileName, ExcelType excelType, HttpServletResponse response){ + defaultExport(list, pojoClass, fileName, response, new ExportParams(title, sheetName, excelType)); + } + + public static void exportExcel(List> list, String fileName, HttpServletResponse response){ + defaultExport(list, fileName, response); + } + + public static void exportExcel(List entityList, List list, String fileName, ExportParams exportParams, HttpServletResponse response){ + defaultExport(entityList, list, fileName, exportParams, response); + } + + private static void defaultExport(List entityList, List list, String fileName, ExportParams exportParams, HttpServletResponse response) { + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, entityList, list); + if (workbook != null) downLoadExcel(fileName, response, workbook); + } + + private static void defaultExport(List list, Class pojoClass, String fileName, HttpServletResponse response, ExportParams exportParams) { + Workbook workbook = ExcelExportUtil.exportExcel(exportParams, pojoClass, list); + if (workbook != null) downLoadExcel(fileName, response, workbook); + } + + private static void defaultExport(List> list, String fileName, HttpServletResponse response) { + Workbook workbook = ExcelExportUtil.exportExcel(list, ExcelType.HSSF); + if (workbook != null) downLoadExcel(fileName, response, workbook); + } + + public static void exportSheetsExcel(List> list, String fileName, HttpServletResponse response) { + Workbook workbook = ExcelUtils.exportExcel(list); + if (workbook != null) downLoadExcel(fileName, response, workbook); + } + + public static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) { + try { + response.setCharacterEncoding("UTF-8"); + response.setHeader("content-Type", "application/vnd.ms-excel"); + response.setHeader("Content-Disposition", + "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8")); + response.setHeader("Access-Control-Expose-Headers","Content-Disposition"); + workbook.write(response.getOutputStream()); + } catch (IOException e) { + throw new SysException(e.getMessage()); + } + + } + + + public static List importExcel(String filePath,Integer titleRows,Integer headerRows, Class pojoClass){ + if (StringUtils.isBlank(filePath)){ + return null; + } + ImportParams params = new ImportParams(); + params.setTitleRows(titleRows); + params.setHeadRows(headerRows); + List list = null; + try { + list = ExcelImportUtil.importExcel(new File(filePath), pojoClass, params); + }catch (NoSuchElementException e){ + throw new SysException("模板不能为空"); + } catch (Exception e) { + e.printStackTrace(); + throw new SysException(e.getMessage()); + } + return list; + } + public static List importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class pojoClass, Integer sheetIndex){ + if (file == null){ + return null; + } + ImportParams params = new ImportParams(); + params.setTitleRows(titleRows); + params.setHeadRows(headerRows); + if (ObjectUtil.isNotNull(sheetIndex)){ + params.setStartSheetIndex(sheetIndex); + } + List list = null; + try { + list = ExcelImportUtil.importExcel(file.getInputStream(), pojoClass, params); + }catch (NoSuchElementException e){ + throw new SysException("excel文件不能为空"); + } catch (Exception e) { + throw new SysException(e.getMessage()); + } + return list; + } + public static List importExcel(MultipartFile file, Integer titleRows, Integer headerRows, Class pojoClass){ + return importExcel(file, titleRows, headerRows, pojoClass, null); + } + + + /** + * 得到Workbook对象 + * @param file + * @return + * @throws IOException + */ + public static Workbook getWorkBook(MultipartFile file) { + Workbook hssfWorkbook = null; + try { + //兼容03和07版excel + InputStream is = file.getInputStream(); + try { + hssfWorkbook = new HSSFWorkbook(is); + } catch (Exception ex) { + is =file.getInputStream(); + hssfWorkbook = new XSSFWorkbook(is); + } + } catch (IOException ioex) { + throw new SysException("文件导入IO异常"); + } + return hssfWorkbook; + } + + private static Workbook exportExcel(List> list) { + Workbook workbook = new HSSFWorkbook(); + for (Map map : list) { + createSheetWithList(workbook, (ExportParams) map.get("title"),ExportParams.class, + (List) map.get("entity"), (Collection) map.get("data")); + } + return workbook; + } + + + public static void createSheetWithList(Workbook workbook, ExportParams entity, Class pojoClass, List entityList, Collection dataSet) { + if (log.isDebugEnabled()) { + log.debug("Excel export start ,class is {}", pojoClass); + log.debug("Excel version is {}", + entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); + } + if (workbook == null || entity == null || pojoClass == null || dataSet == null) { + throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); + } + try { + List excelParams = entityList; + // 得到所有字段 + Field[] fileds = PoiPublicUtil.getClassFields(pojoClass); + ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); + String targetId = etarget == null ? null : etarget.value(); + ExcelExportService excelExportService = new ExcelExportService(); + excelExportService.getAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, + null, null); + //获取所有参数后,后面的逻辑判断就一致了 + excelExportService.createSheetForMap(workbook, entity, excelParams, dataSet); + } catch (Exception e) { + log.error(e.getMessage(), e); + throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause()); + } + } + +} + diff --git a/common/core/src/main/java/com/thing/common/core/utils/export/BaseExportService.java b/common/core/src/main/java/com/thing/common/core/utils/export/BaseExportService.java new file mode 100644 index 0000000..b0c8553 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/export/BaseExportService.java @@ -0,0 +1,467 @@ +package com.thing.common.core.utils.export; + +import cn.afterturn.easypoi.cache.ImageCache; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; + +import cn.afterturn.easypoi.excel.entity.vo.BaseEntityTypeConstants; +import cn.afterturn.easypoi.excel.entity.vo.PoiBaseConstants; +import cn.afterturn.easypoi.excel.export.base.ExportCommonService; +import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler; +import cn.afterturn.easypoi.exception.excel.ExcelExportException; +import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum; +import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil; +import cn.afterturn.easypoi.util.PoiMergeCellUtil; +import cn.afterturn.easypoi.util.PoiPublicUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ReflectionToStringBuilder; +import org.apache.poi.hssf.usermodel.HSSFClientAnchor; +import org.apache.poi.hssf.usermodel.HSSFRichTextString; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.xssf.usermodel.XSSFClientAnchor; +import org.apache.poi.xssf.usermodel.XSSFRichTextString; + +import java.nio.charset.StandardCharsets; +import java.text.DecimalFormat; +import java.util.*; + +/** + * 重写导出方法 + * + */ +@SuppressWarnings("unchecked") +public class BaseExportService extends ExportCommonService { + + private int currentIndex = 0; + + protected ExcelType type = ExcelType.HSSF; + + private Map statistics = new HashMap(); + + private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00"); + + protected IExcelExportStyler excelExportStyler; + + /** + * 创建 最主要的 Cells + */ + public int[] createCells(Drawing patriarch, int index, Object t, + List excelParams, Sheet sheet, Workbook workbook, + short rowHeight, int cellNum) { + try { + ExcelExportEntity entity; + Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index); + if (rowHeight != -1) { + row.setHeight(rowHeight); + } + int maxHeight = 1, listMaxHeight = 1; + // 合并需要合并的单元格 + int margeCellNum = cellNum; + int indexKey = createIndexCell(row, index, excelParams.get(0)); + cellNum += indexKey; + for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { + entity = excelParams.get(k); + if (entity.getList() != null) { + Collection list = getListCellValue(entity, t); + int listIndex = 0, tmpListHeight = 0; + if (list != null && list.size() > 0) { + int tempCellNum = 0; + for (Object obj : list) { + int[] temp = createCells(patriarch, index + listIndex, obj, entity.getList(), sheet, workbook, rowHeight, cellNum); + tempCellNum = temp[1]; + tmpListHeight += temp[0]; + listIndex++; + } + cellNum = tempCellNum; + listMaxHeight = Math.max(listMaxHeight, tmpListHeight); + } + } else { + Object value = getCellValue(entity, t); + + if (entity.getType() == BaseEntityTypeConstants.STRING_TYPE) { + createStringCell(row, cellNum++, value == null ? "" : value.toString(), + index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), + entity); + + } else if (entity.getType() == BaseEntityTypeConstants.DOUBLE_TYPE) { + createDoubleCell(row, cellNum++, value == null ? "" : value.toString(), + index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), + entity); + } else { + createImageCell(patriarch, entity, row, cellNum++, + value == null ? "" : value.toString(), t); + } + if (entity.isHyperlink()) { + row.getCell(cellNum - 1) + .setHyperlink(dataHandler.getHyperlink( + row.getSheet().getWorkbook().getCreationHelper(), t, + entity.getName(), value)); + } + } + } + maxHeight += listMaxHeight - 1; + if (indexKey == 1 && excelParams.get(1).isNeedMerge()) { + excelParams.get(0).setNeedMerge(true); + } + for (int k = indexKey, paramSize = excelParams.size(); k < paramSize; k++) { + entity = excelParams.get(k); + if (entity.getList() != null) { + margeCellNum += entity.getList().size(); + } else if (entity.isNeedMerge() && maxHeight > 1) { + for (int i = index + 1; i < index + maxHeight; i++) { + if(sheet.getRow(i) == null ) { + sheet.createRow(i); + } + sheet.getRow(i).createCell(margeCellNum); + sheet.getRow(i).getCell(margeCellNum).setCellStyle(getStyles(false, entity)); + } + PoiMergeCellUtil.addMergedRegion(sheet, index, index + maxHeight - 1, margeCellNum, margeCellNum); + margeCellNum++; + } + } + return new int[]{maxHeight, cellNum}; + } catch (Exception e) { + LOGGER.error("excel cell export error ,data is :{}", ReflectionToStringBuilder.toString(t)); + LOGGER.error(e.getMessage(), e); + throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e); + } + + } + + /** + * 图片类型的Cell + */ + public void createImageCell(Drawing patriarch, ExcelExportEntity entity, Row row, int i, + String imagePath, Object obj) throws Exception { + Cell cell = row.createCell(i); + byte[] value = null; + if (entity.getExportImageType() != 1) { + value = (byte[]) (entity.getMethods() != null + ? getFieldBySomeMethod(entity.getMethods(), obj) + : entity.getMethod().invoke(obj, new Object[]{})); + } + createImageCell(cell, 50 * entity.getHeight(), entity.getExportImageType() == 1 ? imagePath : null, value); + + } + + + /** + * 图片类型的Cell + */ + public void createImageCell(Cell cell, double height, + String imagePath, byte[] data) throws Exception { + if (height > cell.getRow().getHeight()) { + cell.getRow().setHeight((short) height); + } + ClientAnchor anchor; + if (type.equals(ExcelType.HSSF)) { + anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), + cell.getRow().getRowNum() + 1); + } else { + anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1), + cell.getRow().getRowNum() + 1); + } + if (StringUtils.isNotEmpty(imagePath)) { + data = ImageCache.getImage(imagePath); + } + if (data != null) { + PoiExcelGraphDataUtil.getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + + } + + /** + * 图片类型的Cell + */ + public void createImageCell(Cell cell, double height, int rowspan, int colspan, + String imagePath, byte[] data) throws Exception { + if (height > cell.getRow().getHeight()) { + cell.getRow().setHeight((short) height); + } + ClientAnchor anchor; + if (type.equals(ExcelType.HSSF)) { + anchor = new HSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan), + cell.getRow().getRowNum() + rowspan); + } else { + anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(), cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + colspan), + cell.getRow().getRowNum() + rowspan); + } + if (StringUtils.isNotEmpty(imagePath)) { + data = ImageCache.getImage(imagePath); + } + if (data != null) { + PoiExcelGraphDataUtil.getDrawingPatriarch(cell.getSheet()).createPicture(anchor, + cell.getSheet().getWorkbook().addPicture(data, getImageType(data))); + } + + } + + private int createIndexCell(Row row, int index, ExcelExportEntity excelExportEntity) { + if (excelExportEntity.getFormat() != null && excelExportEntity.getFormat().equals(PoiBaseConstants.IS_ADD_INDEX)) { + createStringCell(row, 0, currentIndex + "", + index % 2 == 0 ? getStyles(false, null) : getStyles(true, null), null); + currentIndex = currentIndex + 1; + return 1; + } + return 0; + } + + /** + * 创建List之后的各个Cells + */ + public void createListCells(Drawing patriarch, int index, int cellNum, Object obj, + List excelParams, Sheet sheet, + Workbook workbook, short rowHeight) throws Exception { + ExcelExportEntity entity; + Row row; + if (sheet.getRow(index) == null) { + row = sheet.createRow(index); + if (rowHeight != -1) { + row.setHeight(rowHeight); + } + } else { + row = sheet.getRow(index); + if (rowHeight != -1) { + row.setHeight(rowHeight); + } + } + for (int k = 0, paramSize = excelParams.size(); k < paramSize; k++) { + entity = excelParams.get(k); + Object value = getCellValue(entity, obj); + if (entity.getType() == BaseEntityTypeConstants.STRING_TYPE) { + createStringCell(row, cellNum++, value == null ? "" : value.toString(), + row.getRowNum() % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), + entity); + if (entity.isHyperlink()) { + row.getCell(cellNum - 1) + .setHyperlink(dataHandler.getHyperlink( + row.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(), + value)); + } + } else if (entity.getType() == BaseEntityTypeConstants.DOUBLE_TYPE) { + createDoubleCell(row, cellNum++, value == null ? "" : value.toString(), + index % 2 == 0 ? getStyles(false, entity) : getStyles(true, entity), entity); + if (entity.isHyperlink()) { + row.getCell(cellNum - 1) + .setHyperlink(dataHandler.getHyperlink( + row.getSheet().getWorkbook().getCreationHelper(), obj, entity.getName(), + value)); + } + } else { + createImageCell(patriarch, entity, row, cellNum++, + value == null ? "" : value.toString(), obj); + } + } + } + + /** + * 创建文本类型的Cell + */ + public void createStringCell(Row row, int index, String text, CellStyle style, + ExcelExportEntity entity) { + Cell cell = row.createCell(index); + if (style != null && style.getDataFormat() > 0 && style.getDataFormat() < 12) { + cell.setCellValue(Double.parseDouble(text)); + cell.setCellType(CellType.NUMERIC); + } else { + RichTextString rtext; + if (type.equals(ExcelType.HSSF)) { + rtext = new HSSFRichTextString(text); + } else { + rtext = new XSSFRichTextString(text); + } + cell.setCellValue(rtext); + } + if (style != null) { + cell.setCellStyle(style); + } + addStatisticsData(index, text, entity); + } + + /** + * 创建数字类型的Cell + */ + public void createDoubleCell(Row row, int index, String text, CellStyle style, + ExcelExportEntity entity) { + Cell cell = row.createCell(index); + cell.setCellType(CellType.NUMERIC); + if (text != null && text.length() > 0) { + try { + cell.setCellValue(Double.parseDouble(text)); + } catch (NumberFormatException e) { + cell.setCellType(CellType.STRING); + cell.setCellValue(text); + } + } + + if (style != null) { + cell.setCellStyle(style); + } + addStatisticsData(index, text, entity); + } + + /** + * 创建统计行 + */ + public void addStatisticsRow(CellStyle styles, Sheet sheet) { + if (statistics.size() > 0) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("add statistics data ,size is {}", statistics.size()); + } + Row row = sheet.createRow(sheet.getLastRowNum() + 1); + Set keys = statistics.keySet(); + createStringCell(row, 0, "合计", styles, null); + for (Integer key : keys) { + createStringCell(row, key, DOUBLE_FORMAT.format(statistics.get(key)), styles, null); + } + statistics.clear(); + } + + } + + /** + * 合计统计信息 + */ + private void addStatisticsData(Integer index, String text, ExcelExportEntity 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); + } + } + + /** + * 获取图片类型,设置图片插入类型 + * + * @author JueYue 2013年11月25日 + */ + public int getImageType(byte[] value) { + String type = PoiPublicUtil.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; + } + + private Map getMergeDataMap(List excelParams) { + Map mergeMap = new HashMap(); + // 设置参数顺序,为之后合并单元格做准备 + int i = 0; + for (ExcelExportEntity entity : excelParams) { + if (entity.isMergeVertical()) { + mergeMap.put(i, entity.getMergeRely()); + } + if (entity.getList() != null) { + for (ExcelExportEntity inner : entity.getList()) { + if (inner.isMergeVertical()) { + mergeMap.put(i, inner.getMergeRely()); + } + i++; + } + } else { + i++; + } + } + return mergeMap; + } + + /** + * 获取样式 + */ + public CellStyle getStyles(boolean needOne, ExcelExportEntity entity) { + return excelExportStyler.getStyles(needOne, entity); + } + + /** + * 合并单元格 + */ + public void mergeCells(Sheet sheet, List excelParams, int titleHeight) { + Map mergeMap = getMergeDataMap(excelParams); + PoiMergeCellUtil.mergeCells(sheet, mergeMap, titleHeight); + } + + public void setCellWith(List excelParams, Sheet sheet) { + int index = 0; + for (int i = 0; i < excelParams.size(); i++) { + if (excelParams.get(i).getList() != null) { + List list = excelParams.get(i).getList(); + for (int j = 0; j < list.size(); j++) { + sheet.setColumnWidth(index, (int) (256 * list.get(j).getWidth())); + index++; + } + } else { + sheet.setColumnWidth(index, (int) (256 * excelParams.get(i).getWidth())); + index++; + } + } + } + + public void setColumnWidth(Sheet sheet) { + //sheet的索引从0开始,获取sheet列数 + int maxColumn = sheet.getRow(0).getPhysicalNumberOfCells(); + for (int columnNum = 0; columnNum <= maxColumn; columnNum++) { + int columnWidth = sheet.getColumnWidth(columnNum) / 256; + // 遍历列的数据,获取这一列的最长字符串 + for (int rowNum = 0; rowNum <= sheet.getLastRowNum(); rowNum++) { + Row currentRow; + if (sheet.getRow(rowNum) == null) { + currentRow = sheet.createRow(rowNum); + } else { + currentRow = sheet.getRow(rowNum); + } + if (currentRow.getCell(columnNum) != null) { + Cell currentCell = currentRow.getCell(columnNum); + if (currentCell.getCellType() == CellType.STRING) { + //int length = currentCell.getStringCellValue().getBytes().length; + int length = (currentCell.getStringCellValue().getBytes(StandardCharsets.UTF_8).length + currentCell.toString().length()) / 2; + if (columnWidth < length) { + columnWidth = length; + } + } + } + } + //将最长的length*256设为列宽 + // sheet.setColumnWidth((short)列数,(short)(length*256)); + sheet.setColumnWidth(columnNum, Math.min(columnWidth * 310, 9150)); + } + } + + public void setColumnHidden(List excelParams, Sheet sheet) { + int index = 0; + for (int i = 0; i < excelParams.size(); i++) { + if (excelParams.get(i).getList() != null) { + List list = excelParams.get(i).getList(); + for (int j = 0; j < list.size(); j++) { + sheet.setColumnHidden(index, list.get(j).isColumnHidden()); + index++; + } + } else { + sheet.setColumnHidden(index, excelParams.get(i).isColumnHidden()); + index++; + } + } + } + + public void setCurrentIndex(int currentIndex) { + this.currentIndex = currentIndex; + } + + public void setExcelExportStyler(IExcelExportStyler excelExportStyler) { + this.excelExportStyler = excelExportStyler; + } + + public IExcelExportStyler getExcelExportStyler() { + return excelExportStyler; + } + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/export/ExcelEntityGenerator.java b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelEntityGenerator.java new file mode 100644 index 0000000..39c83a0 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelEntityGenerator.java @@ -0,0 +1,289 @@ +package com.thing.common.core.utils.export; + +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.exception.SysException; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +/** + * Author: SiYang + * Date: 2023/09/26 10:40 + * Description: + * 对于导出行为: 生成 ExcelExportEntity 对象 + * 对于导入行为: 以后再说 + */ +@Slf4j +public class ExcelEntityGenerator { + + /** + * 基于数据列表生成 easyPoi 中的 ExcelExportEntity 对象列表 + * + * @param dataList 数据列表: 想被导出的字段需要使用 @CustomExcel 注解 + * @return ExcelExportEntity 对象列表 + */ + public static List generateExportEntity(List dataList) { + try { + return generateExportEntity(dataList, null, null); + } catch (Exception e) { + log.error("生成动态表头错误: ", e); + throw new SysException("生成动态表头错误: " + e.getMessage()); + } + } + + @SuppressWarnings("rawtypes") + private static List generateExportEntity( + List dataList, List result, Map colKeyNameMap) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + colKeyNameMap = CollectionUtils.isEmpty(colKeyNameMap) ? new HashMap<>() : colKeyNameMap; + result = Optional.ofNullable(result).orElse(new ArrayList<>()); + if (CollectionUtil.isEmpty(dataList)) { + return Collections.emptyList(); + } + Class clazz = dataList.get(0).getClass(); + Field[] fields = clazz.getDeclaredFields(); + for (Field field : fields) { + field.setAccessible(true); + CustomExcel customExcel = field.getAnnotation(CustomExcel.class); + CustomExcelCollection customExcelCollection = field.getAnnotation(CustomExcelCollection.class); + if (Objects.nonNull(customExcel)) { + // 第一优先 keyGenerator如果为空则默认使用 columnNameGenerator + String columnNameGenerator = customExcel.columnNameGenerator(); + String keyGenerator = customExcel.keyGenerator(); + // 第二优先 key为空则默认使用 colName + String colName = customExcel.name(); + String key = StringUtils.isNotBlank(customExcel.key()) ? customExcel.key() : colName; + ExcelExportEntityBuilder builder = new ExcelExportEntityBuilder(); + // 动态key必须与动态列名同时使用,否则无意义 + if (StringUtils.isNotBlank(columnNameGenerator)) { + Method generateNameMethod = clazz.getDeclaredMethod(columnNameGenerator); + Method keyGeneratorMethod = StringUtils.isNotBlank(keyGenerator) ? clazz.getDeclaredMethod(keyGenerator) : null; + generateNameMethod.setAccessible(true); + for (Object data : dataList) { + String dynamicColName = (String) generateNameMethod.invoke(data); + String dynamicKey = Objects.isNull(keyGeneratorMethod) ? dynamicColName : (String) keyGeneratorMethod.invoke(data); + if (colKeyNameMap.containsKey(dynamicKey) || StringUtils.isBlank(dynamicKey)) { + continue; + } + ExcelExportEntity entity = + builder.name(dynamicColName) + .key(dynamicKey) + .width(customExcel.width()) + .height(customExcel.height()) + .needMerge(customExcel.needMerge()) + .orderNum(getOrderNum(clazz, data, customExcel)) + .groupName(getGroupName(clazz, data, customExcel)) + .build(); + result.add(entity); + colKeyNameMap.put(dynamicKey, dynamicColName); + } + } else { + if (colKeyNameMap.containsKey(key) || StringUtils.isBlank(key)) { + continue; + } + ExcelExportEntity entity = + builder.name(colName) + .key(key) + .width(customExcel.width()) + .height(customExcel.height()) + .needMerge(customExcel.needMerge()) + .orderNum(customExcel.orderNum()) + .groupName(customExcel.groupName()) + .build(); + result.add(entity); + colKeyNameMap.put(key, colName); + } + } else if (Objects.nonNull(customExcelCollection)) { + for (Object data : dataList) { + List fieldData = + Optional.ofNullable((List) field.get(data)).orElse(new ArrayList()); + generateExportEntity( + new ArrayList(fieldData), result, colKeyNameMap); + } + } + } + return result; + } + + /** + * 将被 @CustomExcel 注解的属性值提取到map + * + * @param dataList 数据列表 + * @return 键值对列表 + */ + public static List> generateDataCollection(List dataList) { + try { + return generateDataCollection(dataList, null, null, true); + } catch (Exception e) { + log.error("生成excel数据错误: ", e); + throw new SysException("生成excel数据错误: " + e.getMessage()); + } + } + + @SuppressWarnings(value = {"rawtypes", "unchecked"}) + private static List> generateDataCollection( + List dataList, + List> result, + Map dataMap, + boolean isRoot) + throws IllegalAccessException, NoSuchMethodException, InvocationTargetException { + result = Optional.ofNullable(result).orElse(new ArrayList<>()); + if (CollectionUtil.isEmpty(dataList)) { + return Collections.emptyList(); + } + Class clazz = dataList.get(0).getClass(); + Field[] fields = clazz.getDeclaredFields(); + + for (Object data : dataList) { + if (isRoot) { + dataMap = new LinkedHashMap<>(); + result.add(dataMap); + } + for (Field field : fields) { + field.setAccessible(true); + CustomExcel customExcel = field.getAnnotation(CustomExcel.class); + CustomExcelCollection customExcelCollection = field.getAnnotation(CustomExcelCollection.class); + if (Objects.nonNull(customExcel)) { + Object val = field.get(data); + + // 第一优先 + String columnNameGenerator = customExcel.columnNameGenerator(); + String keyGenerator = + StringUtils.isNotBlank(customExcel.keyGenerator()) + ? customExcel.keyGenerator() + : columnNameGenerator; + // 第二优先 + String colName = customExcel.name(); + String key = StringUtils.isNotBlank(customExcel.key()) ? customExcel.key() : colName; + + if (StringUtils.isNotBlank(keyGenerator)) { + Method method = clazz.getDeclaredMethod(keyGenerator); + method.setAccessible(true); + String dynamicKey = (String) method.invoke(data); + if (StringUtils.isBlank(dynamicKey)) { + continue; + } + dataMap.put(dynamicKey, val); + } else if (StringUtils.isNotBlank(key)) { + dataMap.put(key, val); + } + + } else if (Objects.nonNull(customExcelCollection)) { + List fieldData = + Optional.ofNullable((List) field.get(data)).orElse(new ArrayList()); + generateDataCollection( + new ArrayList<>(fieldData), result, dataMap, false); + } + } + } + return result; + } + + /** + * 优先按指定的序号生成器来获取,如果没有指定生成器则使用orderNum + * + * @param clazz 数据类字节码对象 + * @param obj 当前处理的对象 + * @param customExcel 注解对象 + * @return 序号 + */ + private static int getOrderNum(Class clazz, Object obj, CustomExcel customExcel) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + String methodName = customExcel.orderGenerator(); + if (StringUtils.isNotBlank(methodName)) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (Integer) method.invoke(obj); + } else { + return customExcel.orderNum(); + } + } + + /** + * 优先按照group生成策略生成groupName + * + * @param clazz 数据类字节码对象 + * @param obj 当前处理的对象 + * @param customExcel 注解对象 + * @return groupName + */ + private static String getGroupName(Class clazz, Object obj, CustomExcel customExcel) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + String methodName = customExcel.groupGenerator(); + if (StringUtils.isNotBlank(methodName)) { + Method method = clazz.getDeclaredMethod(methodName); + method.setAccessible(true); + return (String) method.invoke(obj); + } else { + return customExcel.groupName(); + } + } + + @NoArgsConstructor + private static class ExcelExportEntityBuilder { + private Object key; + private String name; + private int width = 10; + private int height = 10; + private int orderNum = 0; + private boolean needMerge; + private String groupName; + + public ExcelExportEntityBuilder key(Object key){ + this.key = key; + return this; + } + + public ExcelExportEntityBuilder name(String name){ + this.name = name; + return this; + } + + public ExcelExportEntityBuilder width(int width) { + if (width > 0) { + this.width = width; + } + return this; + } + + public ExcelExportEntityBuilder height(int height){ + if (height > 0) { + this.height = height; + } + return this; + } + + public ExcelExportEntityBuilder orderNum(int orderNum){ + this.orderNum = orderNum; + return this; + } + + public ExcelExportEntityBuilder needMerge(boolean needMerge){ + this.needMerge = needMerge; + return this; + } + + public ExcelExportEntityBuilder groupName(String groupName){ + this.groupName = groupName; + return this; + } + + public ExcelExportEntity build() { + ExcelExportEntity entity = new ExcelExportEntity(name, key, width); + entity.setHeight(height); + entity.setOrderNum(orderNum); + entity.setNeedMerge(needMerge); + entity.setGroupName(groupName); + return entity; + } + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportService.java b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportService.java new file mode 100644 index 0000000..c71d685 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportService.java @@ -0,0 +1,253 @@ +package com.thing.common.core.utils.export; + +import cn.afterturn.easypoi.excel.annotation.ExcelTarget; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.styler.IExcelExportStyler; +import cn.afterturn.easypoi.exception.excel.ExcelExportException; +import cn.afterturn.easypoi.exception.excel.enums.ExcelExportEnum; +import cn.afterturn.easypoi.util.PoiExcelGraphDataUtil; +import cn.afterturn.easypoi.util.PoiMergeCellUtil; +import cn.afterturn.easypoi.util.PoiPublicUtil; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.usermodel.*; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.lang.reflect.Field; +import java.util.*; + +/** + * 重写导出方法 + * + */ +public class ExcelExportService extends BaseExportService { + + /** + * 最大行数,超过自动多Sheet + */ + private static int MAX_NUM = 60000; + + protected int createHeaderAndTitle(ExportParams entity, Sheet sheet, Workbook workbook, + List excelParams) { + int rows = 0, fieldLength = getFieldLength(excelParams); + if (entity.getTitle() != null) { + rows += createTitle2Row(entity, sheet, workbook, fieldLength); + } + createHeaderRow(entity, sheet, workbook, rows, excelParams, 0); + rows += getRowNums(excelParams, true); + if (entity.isFixedTitle()) { + sheet.createFreezePane(0, rows, 0, rows); + } + return rows; + } + + /** + * 创建表头 + */ + private int createHeaderRow(ExportParams title, Sheet sheet, Workbook workbook, int index, + List excelParams, int cellIndex) { + Row row = sheet.getRow(index) == null ? sheet.createRow(index) : sheet.getRow(index); + int rows = getRowNums(excelParams, true); + row.setHeight(title.getHeaderHeight()); + Row listRow = null; + if (rows >= 2) { + listRow = sheet.createRow(index + 1); + listRow.setHeight(title.getHeaderHeight()); + } + int groupCellLength = 0; + CellStyle titleStyle = getExcelExportStyler().getTitleStyle(title.getColor()); + for (int i = 0, exportFieldTitleSize = excelParams.size(); i < exportFieldTitleSize; i++) { + ExcelExportEntity entity = excelParams.get(i); + // 加入换了groupName或者结束就,就把之前的那个换行 + if (StringUtils.isBlank(entity.getGroupName()) || i == 0 || !entity.getGroupName().equals(excelParams.get(i - 1).getGroupName())) { + if (groupCellLength > 1) { + sheet.addMergedRegion(new CellRangeAddress(index, index, cellIndex - groupCellLength, cellIndex - 1)); + } + groupCellLength = 0; + } + if (StringUtils.isNotBlank(entity.getGroupName())) { + createStringCell(row, cellIndex, entity.getGroupName(), titleStyle, entity); + createStringCell(listRow, cellIndex, entity.getName(), titleStyle, entity); + groupCellLength++; + } else if (StringUtils.isNotBlank(entity.getName())) { + createStringCell(row, cellIndex, entity.getName(), titleStyle, entity); + } + if (entity.getList() != null) { + // 保持原来的 + int tempCellIndex = cellIndex; + cellIndex = createHeaderRow(title, sheet, workbook, rows == 1 ? index : index + 1, entity.getList(), cellIndex); + List sTitel = entity.getList(); + if (StringUtils.isNotBlank(entity.getName()) && sTitel.size() > 1) { + PoiMergeCellUtil.addMergedRegion(sheet, index, index, tempCellIndex, tempCellIndex + sTitel.size() - 1); + } + /*for (int j = 0, size = sTitel.size(); j < size; j++) { + + createStringCell(rows == 2 ? listRow : row, cellIndex, sTitel.get(j).getName(), + titleStyle, entity); + cellIndex++; + }*/ + cellIndex--; + } else if (rows > 1 && StringUtils.isBlank(entity.getGroupName())) { + createStringCell(listRow, cellIndex, "", titleStyle, entity); + PoiMergeCellUtil.addMergedRegion(sheet, index, index + rows - 1, cellIndex, cellIndex); + } + cellIndex++; + } + if (groupCellLength > 1) { + PoiMergeCellUtil.addMergedRegion(sheet, index, index, cellIndex - groupCellLength, cellIndex - 1); + } + return cellIndex; + + } + + /** + * 创建 表头改变 + */ + public int createTitle2Row(ExportParams entity, Sheet sheet, Workbook workbook, + int fieldWidth) { + + Row row = sheet.createRow(0); + row.setHeight(entity.getTitleHeight()); + createStringCell(row, 0, entity.getTitle(), + getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); + for (int i = 1; i <= fieldWidth; i++) { + createStringCell(row, i, "", + getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); + } + PoiMergeCellUtil.addMergedRegion(sheet, 0, 0, 0, fieldWidth); + if (entity.getSecondTitle() != null) { + row = sheet.createRow(1); + row.setHeight(entity.getSecondTitleHeight()); + CellStyle style = workbook.createCellStyle(); + style.setAlignment(HorizontalAlignment.RIGHT); + createStringCell(row, 0, entity.getSecondTitle(), style, null); + for (int i = 1; i <= fieldWidth; i++) { + createStringCell(row, i, "", + getExcelExportStyler().getHeaderStyle(entity.getHeaderColor()), null); + } + PoiMergeCellUtil.addMergedRegion(sheet, 1, 1, 0, fieldWidth); + return 2; + } + return 1; + } + + public void createSheet(Workbook workbook, ExportParams entity, Class pojoClass, + Collection dataSet) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Excel export start ,class is {}", pojoClass); + LOGGER.debug("Excel version is {}", + entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); + } + if (workbook == null || entity == null || pojoClass == null || dataSet == null) { + throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); + } + try { + List excelParams = new ArrayList(); + // 得到所有字段 + Field[] fileds = PoiPublicUtil.getClassFields(pojoClass); + ExcelTarget etarget = pojoClass.getAnnotation(ExcelTarget.class); + String targetId = etarget == null ? null : etarget.value(); + getAllExcelField(entity.getExclusions(), targetId, fileds, excelParams, pojoClass, + null, null); + //获取所有参数后,后面的逻辑判断就一致了 + createSheetForMap(workbook, entity, excelParams, dataSet); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause()); + } + } + + public void createSheetForMap(Workbook workbook, ExportParams entity, + List entityList, Collection dataSet) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("Excel version is {}", + entity.getType().equals(ExcelType.HSSF) ? "03" : "07"); + } + if (workbook == null || entity == null || entityList == null || dataSet == null) { + throw new ExcelExportException(ExcelExportEnum.PARAMETER_ERROR); + } + super.type = entity.getType(); + if (type.equals(ExcelType.XSSF)) { + MAX_NUM = 1000000; + } + if (entity.getMaxNum() > 0) { + MAX_NUM = entity.getMaxNum(); + } + Sheet sheet = null; + try { + sheet = workbook.createSheet(entity.getSheetName()); + } catch (Exception e) { + // 重复遍历,出现了重名现象,创建非指定的名称Sheet + sheet = workbook.createSheet(); + } + insertDataToSheet(workbook, entity, entityList, dataSet, sheet); + } + + protected void insertDataToSheet(Workbook workbook, ExportParams entity, + List entityList, Collection dataSet, + Sheet sheet) { + try { + dataHandler = entity.getDataHandler(); + if (dataHandler != null && dataHandler.getNeedHandlerFields() != null) { + needHandlerList = Arrays.asList(dataHandler.getNeedHandlerFields()); + } + dictHandler = entity.getDictHandler(); + i18nHandler = entity.getI18nHandler(); + // 创建表格样式 + setExcelExportStyler((IExcelExportStyler) entity.getStyle() + .getConstructor(Workbook.class).newInstance(workbook)); + Drawing patriarch = PoiExcelGraphDataUtil.getDrawingPatriarch(sheet); + List excelParams = new ArrayList(); + if (entity.isAddIndex()) { + excelParams.add(indexExcelEntity(entity)); + } + excelParams.addAll(entityList); + sortAllParams(excelParams); + int index = entity.isCreateHeadRows() + ? createHeaderAndTitle(entity, sheet, workbook, excelParams) : 0; + int titleHeight = index; + setColumnHidden(excelParams, sheet); + short rowHeight = entity.getHeight() != 0 ? entity.getHeight() : getRowHeight(excelParams); + setCurrentIndex(1); + Iterator its = dataSet.iterator(); + List tempList = new ArrayList(); + while (its.hasNext()) { + Object t = its.next(); + index += createCells(patriarch, index, t, excelParams, sheet, workbook, rowHeight, 0)[0]; + tempList.add(t); + if (index >= MAX_NUM) { + break; + } + } + if (entity.getFreezeCol() != 0) { + sheet.createFreezePane(entity.getFreezeCol(), 0, entity.getFreezeCol(), 0); + } + + mergeCells(sheet, excelParams, titleHeight); + + its = dataSet.iterator(); + for (int i = 0, le = tempList.size(); i < le; i++) { + its.next(); + its.remove(); + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("List data more than max ,data size is {}", + dataSet.size()); + } + // 发现还有剩余list 继续循环创建Sheet + if (dataSet.size() > 0) { + createSheetForMap(workbook, entity, entityList, dataSet); + } else { + // 创建合计信息 + addStatisticsRow(getExcelExportStyler().getStyles(true, null), sheet); + } + setColumnWidth(sheet); + } catch (Exception e) { + LOGGER.error(e.getMessage(), e); + throw new ExcelExportException(ExcelExportEnum.EXPORT_ERROR, e.getCause()); + } + } + + +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportUtil.java b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportUtil.java new file mode 100644 index 0000000..80764bc --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/export/ExcelExportUtil.java @@ -0,0 +1,167 @@ +package com.thing.common.core.utils.export; + +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.TemplateExportParams; +import cn.afterturn.easypoi.excel.entity.enmus.ExcelType; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.afterturn.easypoi.excel.export.ExcelBatchExportService; +import cn.afterturn.easypoi.excel.export.template.ExcelExportOfTemplateUtil; +import cn.afterturn.easypoi.handler.inter.IExcelExportServer; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.xssf.streaming.SXSSFWorkbook; +import org.apache.poi.xssf.usermodel.XSSFWorkbook; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 重写导出方法 + * + */ +public class ExcelExportUtil { + public static int USE_SXSSF_LIMIT = 100000; + public static final String SHEET_NAME = "sheetName"; + + private ExcelExportUtil() { + } + + /** + * 大数据量导出 + * + * @param entity 表格标题属性 + * @param pojoClass Excel对象Class + * @param server 查询数据的接口 + * @param queryParams 查询数据的参数 + */ + public static Workbook exportBigExcel(ExportParams entity, Class pojoClass, + IExcelExportServer server, Object queryParams) { + ExcelBatchExportService batchServer = new ExcelBatchExportService(); + batchServer.init(entity, pojoClass); + return batchServer.exportBigExcel(server, queryParams); + } + + /** + * 大数据量导出 + * + * @param entity + * @param excelParams + * @param server 查询数据的接口 + * @param queryParams 查询数据的参数 + * @return + */ + public static Workbook exportBigExcel(ExportParams entity, List excelParams, + IExcelExportServer server, Object queryParams) { + ExcelBatchExportService batchServer = new ExcelBatchExportService(); + batchServer.init(entity, excelParams); + return batchServer.exportBigExcel(server, queryParams); + } + + + /** + * @param entity 表格标题属性 + * @param pojoClass Excel对象Class + * @param dataSet Excel对象数据List + */ + public static Workbook exportExcel(ExportParams entity, Class pojoClass, + Collection dataSet) { + Workbook workbook = getWorkbook(entity.getType(), dataSet.size()); + new ExcelExportService().createSheet(workbook, entity, pojoClass, dataSet); + return workbook; + } + + private static Workbook getWorkbook(ExcelType type, int size) { + if (ExcelType.HSSF.equals(type)) { + return new HSSFWorkbook(); + } else if (size < USE_SXSSF_LIMIT) { + return new XSSFWorkbook(); + } else { + return new SXSSFWorkbook(); + } + } + + /** + * 根据Map创建对应的Excel + * + * @param entity 表格标题属性 + * @param entityList Map对象列表 + * @param dataSet Excel对象数据List + */ + public static Workbook exportExcel(ExportParams entity, List entityList, + Collection dataSet) { + Workbook workbook = getWorkbook(entity.getType(), dataSet.size()); + new ExcelExportService().createSheetForMap(workbook, entity, entityList, dataSet); + return workbook; + } + + /** + * 根据Map创建对应的Excel(一个excel 创建多个sheet) + * + * @param list 多个Map key title 对应表格Title key entity 对应表格对应实体 key data + * Collection 数据 + * @return + */ + public static Workbook exportExcel(List> list, ExcelType type) { + Workbook workbook = getWorkbook(type, 0); + for (Map map : list) { + ExcelExportService service = new ExcelExportService(); + service.createSheet(workbook, (ExportParams) map.get("title"), + (Class) map.get("entity"), (Collection) map.get("data")); + } + return workbook; + } + + /** + * 导出文件通过模板解析,不推荐这个了,推荐全部通过模板来执行处理 + * + * @param params 导出参数类 + * @param pojoClass 对应实体 + * @param dataSet 实体集合 + * @param map 模板集合 + * @return + */ + @Deprecated + public static Workbook exportExcel(TemplateExportParams params, Class pojoClass, + Collection dataSet, Map map) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, pojoClass, dataSet, + map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcel(TemplateExportParams params, Map map) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, null, null, map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * 每个sheet对应一个map,导出到处,key是sheet的NUM + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcel(Map> map, + TemplateExportParams params) { + return new ExcelExportOfTemplateUtil().createExcelByTemplate(params, map); + } + + /** + * 导出文件通过模板解析只有模板,没有集合 + * 每个sheet对应一个list,按照数量进行导出排序,key是sheet的NUM + * + * @param params 导出参数类 + * @param map 模板集合 + * @return + */ + public static Workbook exportExcelClone(Map>> map, + TemplateExportParams params) { + return new ExcelExportOfTemplateUtil().createExcelCloneByTemplate(params, map); + } +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/params/SortTreeNode.java b/common/core/src/main/java/com/thing/common/core/utils/params/SortTreeNode.java new file mode 100644 index 0000000..3afaba2 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/params/SortTreeNode.java @@ -0,0 +1,24 @@ +package com.thing.common.core.utils.params; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * Author: SiYang + * Date: 2023/10/23 11:51 + * Description: + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class SortTreeNode extends TreeNode implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String sort; + + private int depth; +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/params/SplitedTimeParam.java b/common/core/src/main/java/com/thing/common/core/utils/params/SplitedTimeParam.java new file mode 100644 index 0000000..b3e4de2 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/params/SplitedTimeParam.java @@ -0,0 +1,20 @@ +package com.thing.common.core.utils.params; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 切割完成的时间 + */ +@Data +public class SplitedTimeParam implements Serializable { + + @Serial + private static final long serialVersionUID = 8136854862084752561L; + + private String beginTime; + + private String endTime; +} diff --git a/common/core/src/main/java/com/thing/common/core/utils/params/TreeNode.java b/common/core/src/main/java/com/thing/common/core/utils/params/TreeNode.java new file mode 100644 index 0000000..b845f69 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/utils/params/TreeNode.java @@ -0,0 +1,40 @@ + + +package com.thing.common.core.utils.params; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * 树节点,所有需要实现树节点的,都需要继承该类 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +public class TreeNode implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 主键 + */ + private Long id; + /** + * 上级ID + */ + private Long pid; + + /** + * 能够唯一确定一颗树的顶级节点id + */ + private Long topNodeId; + + /** + * 子节点列表 + */ + private List children = new ArrayList<>(); +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/validator/AssertUtils.java b/common/core/src/main/java/com/thing/common/core/validator/AssertUtils.java new file mode 100644 index 0000000..b13b350 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/AssertUtils.java @@ -0,0 +1,93 @@ + + +package com.thing.common.core.validator; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 校验工具类 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class AssertUtils { + + public static void isBlank(String str, String... params) { + isBlank(str, ErrorCode.NOT_NULL, params); + } + + public static void isBlank(String str, Integer code, String... params) { + if(code == null){ + throw new SysException(ErrorCode.NOT_NULL, "code"); + } + + if (StringUtils.isBlank(str)) { + throw new SysException(code, params); + } + } + + public static void isNull(Object object, String... params) { + isNull(object, ErrorCode.NOT_NULL, params); + } + + public static void isNull(Object object, Integer code, String... params) { + if(code == null){ + throw new SysException(ErrorCode.NOT_NULL, "code"); + } + + if (object == null) { + throw new SysException(code, params); + } + } + + public static void isArrayEmpty(Object[] array, String... params) { + isArrayEmpty(array, ErrorCode.NOT_NULL, params); + } + + public static void isArrayEmpty(Object[] array, Integer code, String... params) { + if(code == null){ + throw new SysException(ErrorCode.NOT_NULL, "code"); + } + + if(ArrayUtil.isEmpty(array)){ + throw new SysException(code, params); + } + } + + public static void isListEmpty(Collection list, String... params) { + isListEmpty(list, ErrorCode.NOT_NULL, params); + } + + public static void isListEmpty(Collection list, Integer code, String... params) { + if(code == null){ + throw new SysException(ErrorCode.NOT_NULL, "code"); + } + + if(CollUtil.isEmpty(list)){ + throw new SysException(code, params); + } + } + + public static void isMapEmpty(Map map, String... params) { + isMapEmpty(map, ErrorCode.NOT_NULL, params); + } + + public static void isMapEmpty(Map map, Integer code, String... params) { + if(code == null){ + throw new SysException(ErrorCode.NOT_NULL, "code"); + } + + if(MapUtil.isEmpty(map)){ + throw new SysException(code, params); + } + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/validator/ValidatorUtils.java b/common/core/src/main/java/com/thing/common/core/validator/ValidatorUtils.java new file mode 100644 index 0000000..73635b3 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/ValidatorUtils.java @@ -0,0 +1,52 @@ + + +package com.thing.common.core.validator; + +import com.thing.common.core.exception.SysException; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.Validation; +import jakarta.validation.Validator; +import org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator; +import org.springframework.context.i18n.LocaleContextHolder; +import org.springframework.context.support.ResourceBundleMessageSource; +import org.springframework.validation.beanvalidation.MessageSourceResourceBundleLocator; + +import java.util.Locale; +import java.util.Set; + +/** + * hibernate-validator校验工具类 + * 参考文档:... + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class ValidatorUtils { + + private static ResourceBundleMessageSource getMessageSource() { + ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource(); + bundleMessageSource.setDefaultEncoding("UTF-8"); + bundleMessageSource.setBasenames("i18n/validation"); + return bundleMessageSource; + } + + /** + * 校验对象 + * + * @param object 待校验对象 + * @param groups 待校验的组 + */ + public static void validateEntity(Object object, Class... groups) + throws SysException { + Locale.setDefault(LocaleContextHolder.getLocale()); + Validator validator = Validation.byDefaultProvider().configure().messageInterpolator( + new ResourceBundleMessageInterpolator(new MessageSourceResourceBundleLocator(getMessageSource()))) + .buildValidatorFactory().getValidator(); + + Set> constraintViolations = validator.validate(object, groups); + if (!constraintViolations.isEmpty()) { + ConstraintViolation constraint = constraintViolations.iterator().next(); + throw new SysException(constraint.getMessage()); + } + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/AddGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/AddGroup.java new file mode 100644 index 0000000..7b0ab8e --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/AddGroup.java @@ -0,0 +1,13 @@ + + +package com.thing.common.core.validator.group; + +/** + * 新增 Group + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface AddGroup { + +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/AliyunGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/AliyunGroup.java new file mode 100644 index 0000000..b694907 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/AliyunGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 阿里云 + * + * @author Mark sunlightcs@gmail.com + */ +public interface AliyunGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/DefaultGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/DefaultGroup.java new file mode 100644 index 0000000..59a5f54 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/DefaultGroup.java @@ -0,0 +1,13 @@ + + +package com.thing.common.core.validator.group; + +/** + * 默认 Group + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface DefaultGroup { + +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/FastDFSGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/FastDFSGroup.java new file mode 100644 index 0000000..188a6d5 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/FastDFSGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * FastDFS + * + * @author Mark sunlightcs@gmail.com + */ +public interface FastDFSGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/Group.java b/common/core/src/main/java/com/thing/common/core/validator/group/Group.java new file mode 100644 index 0000000..f945b0d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/Group.java @@ -0,0 +1,15 @@ +package com.thing.common.core.validator.group; + + +import jakarta.validation.GroupSequence; + +/** + * 定义校验顺序,如果AddGroup组失败,则UpdateGroup组不会再校验 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@GroupSequence({AddGroup.class, UpdateGroup.class}) +public interface Group { + +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/GroupNameGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/GroupNameGroup.java new file mode 100644 index 0000000..5fa589d --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/GroupNameGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 阿里云 + * + * @author Mark sunlightcs@gmail.com + */ +public interface GroupNameGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/LocalGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/LocalGroup.java new file mode 100644 index 0000000..bf130ff --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/LocalGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 本地上传 + * + * @author Mark sunlightcs@gmail.com + */ +public interface LocalGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/MinioGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/MinioGroup.java new file mode 100644 index 0000000..1637674 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/MinioGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * MinIO + * + * @author Mark sunlightcs@gmail.com + */ +public interface MinioGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/QcloudGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/QcloudGroup.java new file mode 100644 index 0000000..7c3b9f8 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/QcloudGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 腾讯云 + * + * @author Mark sunlightcs@gmail.com + */ +public interface QcloudGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/QiniuGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/QiniuGroup.java new file mode 100644 index 0000000..a9cbf4a --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/QiniuGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 七牛 + * + * @author Mark sunlightcs@gmail.com + */ +public interface QiniuGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/UpdateGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/UpdateGroup.java new file mode 100644 index 0000000..3f89bc3 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/UpdateGroup.java @@ -0,0 +1,13 @@ + + +package com.thing.common.core.validator.group; + +/** + * 修改 Group + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface UpdateGroup { + +} diff --git a/common/core/src/main/java/com/thing/common/core/validator/group/WxOpenIdGroup.java b/common/core/src/main/java/com/thing/common/core/validator/group/WxOpenIdGroup.java new file mode 100644 index 0000000..0043202 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/validator/group/WxOpenIdGroup.java @@ -0,0 +1,11 @@ + + +package com.thing.common.core.validator.group; + +/** + * 阿里云 + * + * @author Mark sunlightcs@gmail.com + */ +public interface WxOpenIdGroup { +} diff --git a/common/core/src/main/java/com/thing/common/core/web/constants/HttpStatus.java b/common/core/src/main/java/com/thing/common/core/web/constants/HttpStatus.java new file mode 100644 index 0000000..f899927 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/web/constants/HttpStatus.java @@ -0,0 +1,59 @@ +package com.thing.common.core.web.constants; + +/** + * 返回状态码 + * + * @author ruoyi + */ +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/common/core/src/main/java/com/thing/common/core/web/response/PageData.java b/common/core/src/main/java/com/thing/common/core/web/response/PageData.java new file mode 100644 index 0000000..39189bb --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/web/response/PageData.java @@ -0,0 +1,54 @@ +package com.thing.common.core.web.response; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 分页工具类 + * + * @author Mark sunlightcs@gmail.com + */ +@Accessors(chain = true) +@Data +@Schema( description= "分页数据") +@NoArgsConstructor +public class PageData implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "总记录数") + private int total; + + @Schema(description = "列表数据") + private List list; + + /** + * 分页 + * + * @param list 列表数据 + * @param total 总记录数 + */ + public PageData(List list, long total) { + this.list = list; + if(total<0){ + this.total = 0; + }else { + this.total = (int) total; + } + } + /** + * 默认空分页 + * + */ + public static PageData empty() { + return new PageData<>(List.of(), 0); + } +} \ No newline at end of file diff --git a/common/core/src/main/java/com/thing/common/core/web/response/Result.java b/common/core/src/main/java/com/thing/common/core/web/response/Result.java new file mode 100644 index 0000000..fdd5061 --- /dev/null +++ b/common/core/src/main/java/com/thing/common/core/web/response/Result.java @@ -0,0 +1,83 @@ +package com.thing.common.core.web.response; + +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.utils.MessageUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 响应数据 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Getter +@Schema( description= "响应") +public class Result implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 编码:0表示成功,其他值表示失败 + */ + @Schema(description = "编码:0表示成功,其他值表示失败") + private int code = 0; + /** + * 消息内容 + */ + @Schema(description = "消息内容") + private String msg = "success"; + /** + * 响应数据 + */ + @Schema(description = "响应数据") + private T data; + + public Result ok(T data) { + this.setData(data); + return this; + } + + public boolean success(){ + return code == 0; + } + + public Result error() { + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = MessageUtils.getMessage(this.code); + return this; + } + + public Result error(int code) { + this.code = code; + this.msg = MessageUtils.getMessage(this.code); + return this; + } + + public Result error(int code, String msg) { + this.code = code; + this.msg = msg; + return this; + } + + public Result error(String msg) { + this.code = ErrorCode.INTERNAL_SERVER_ERROR; + this.msg = msg; + return this; + } + + public void setCode(int code) { + this.code = code; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public void setData(T data) { + this.data = data; + } +} diff --git a/common/core/src/main/resources/i18n/messages.properties b/common/core/src/main/resources/i18n/messages.properties new file mode 100644 index 0000000..3605a22 --- /dev/null +++ b/common/core/src/main/resources/i18n/messages.properties @@ -0,0 +1,53 @@ +#Default +500=\u670D\u52A1\u5668\u5185\u90E8\u5F02\u5E38 +401=\u672A\u6388\u6743 +403=\u62D2\u7EDD\u8BBF\u95EE\uFF0C\u6CA1\u6709\u6743\u9650 +10001={0}\u4E0D\u80FD\u4E3A\u7A7A +10002=\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728\u8BE5\u8BB0\u5F55 +10003=\u83B7\u53D6\u53C2\u6570\u5931\u8D25 +10004=\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF +10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528 +10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A +10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E +10008=\u5148\u5220\u9664\u5B50\u83DC\u5355\u6216\u6309\u94AE +10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E +10010=\u8D26\u53F7\u4E0D\u5B58\u5728 +10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF +10012=\u4E0A\u7EA7\u83DC\u5355\u4E0D\u80FD\u4E3A\u81EA\u8EAB +10013=\u6570\u636e\u6743\u9650\u6821\u9a8c\u6ca1\u6709\u7b26\u5408\u7c7b\u578b\u7684\u53c2\u6570\u53ef\u4ee5\u5904\u7406 +10014=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u90E8\u95E8 +10015=\u8BF7\u5148\u5220\u9664\u90E8\u95E8\u4E0B\u7684\u7528\u6237 +10016=\u90E8\u7F72\u5931\u8D25\uFF0C\u6CA1\u6709\u6D41\u7A0B +10017=\u6A21\u578B\u56FE\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5 +10018=\u5BFC\u51FA\u5931\u8D25\uFF0C\u6A21\u578BID\u4E3A{0} +10019=\u8BF7\u4E0A\u4F20\u6587\u4EF6 +10020=token\u4E0D\u80FD\u4E3A\u7A7A +10021=token\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +10022=\u8D26\u53F7\u5DF2\u88AB\u9501\u5B9A +10023=\u8BF7\u4E0A\u4F20zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6 +10024=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25{0} +10025=\u53D1\u9001\u77ED\u4FE1\u5931\u8D25{0} +10026=\u90AE\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728 +10027=Redis\u670D\u52A1\u5F02\u5E38 +10028=\u5B9A\u65F6\u4EFB\u52A1\u5931\u8D25 +10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 +10030=\u53C2\u6570\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u4F7F\u7528JSON\u683C\u5F0F +10031=\u8BF7\u5148\u5B8C\u6210\u77ED\u4FE1\u914D\u7F6E +10200=\u8D26\u53F7\u5DF2\u5B58\u5728 +10032=\u4EFB\u52A1\u5DF2\u88AB\u7B7E\u6536\uFF0C\u64CD\u4F5C\u5931\u8D25 +10033=\u4E0D\u5B58\u5728\u7684\u6D41\u7A0B\u5B9A\u4E49 +10034=\u4E0A\u7EA7\u8282\u70B9\u4E0D\u5B58\u5728 +10035=\u9A73\u56DE +10036=\u56DE\u9000 +10037=\u4EFB\u52A1\u6CA1\u6709\u5206\u7EC4\uFF0C\u65E0\u6CD5\u53D6\u6D88\u8BA4\u9886 +10038=\u4E0A\u7EA7\u533A\u57DF\u9009\u62E9\u9519\u8BEF +10039=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u533A\u57DF +10040=\u6D41\u7A0B\u5DF2\u6302\u8D77\uFF0C\u4E0D\u80FD\u542F\u52A8\u5B9E\u4F8B +10041=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u9A73\u56DE +10042=\u5B58\u5728\u591A\u4E2A\u5904\u7406\u4E2D\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u9A73\u56DE +10043=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u7EC8\u6B62 +10044=\u5B58\u5728\u591A\u4E2A\u5904\u7406\u4E2D\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u7EC8\u6B62\u6D41\u7A0B +10045=\u7EC8\u6B62 +10046=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u56DE\u9000 +10047=\u5B58\u5728\u591A\u4E2A\u5E76\u884C\u6267\u884C\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u56DE\u9000 +10048=\u767B\u5F55\u8D26\u53F7\uFF0C\u65E0\u6743\u5220\u9664 \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/messages_en_US.properties b/common/core/src/main/resources/i18n/messages_en_US.properties new file mode 100644 index 0000000..861bb86 --- /dev/null +++ b/common/core/src/main/resources/i18n/messages_en_US.properties @@ -0,0 +1,53 @@ +#English +500=Server internal exception +401=Unauthorized +403=Access denied, no permissions +10001={0} cannot be empty +10002=The record already exists in the database +10003=Failed to get parameters +10004=The account number or password is incorrect. +10005=Account has been deactivated +10006=Unique ID cannot be empty +10007=The verification code is incorrect +10008=First delete submenu or button +10009=The original password is incorrect +10010=The account does not exist +10011=The superior department made a wrong choice +10012=Upper menu cannot be for itself +10013=Data permission interface, which can only be a Map type parameter. +10014=Please delete the subordinate department first +10015=Please delete the user under the department first +10016=Deployment failed, no process +10017=Model diagram is incorrect, please check +10018=The export failed with the model ID {0} +10019=Please upload a file +10020=token cannot be empty +10021=token is invalid, please log in again +10022=The account has been locked +10023=Please upload zip, bar, bpmn, bpmn20.xml format file +10024=Failed to upload file {0} +10025=Failed to send SMS {0} +10026=Mail template does not exist +10027=Redis service exception +10028=Timed task failed +10029=Cannot contain illegal characters +10030=The parameter format is incorrect. Please use JSON format. +10031=Please complete the SMS configuration first. +10200=Account already exists +10032=Task has been signed and operation failed +10033=Non-existent process definition +10034=Superior node does not exist +10035=Reject +10036=Rollback +10037=Tasks are not grouped and cannot be cancelled +10038=Upper area selection error +10039=Please delete the subordinate area first +10040=The process has been suspended and the instance cannot be started +10041=Multi-instance tasks cannot be rejected +10042=Tasks in multiple processes cannot be rejected +10043=Multi-instance tasks cannot be terminated +10044=There are tasks in multiple processes that cannot terminate the process +10045=END +10046=Multi-instance tasks cannot be rolled back +10047=There are multiple parallel tasks that cannot be rolled back +10048 = Login account, no right to delete \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/messages_zh_CN.properties b/common/core/src/main/resources/i18n/messages_zh_CN.properties new file mode 100644 index 0000000..acd058f --- /dev/null +++ b/common/core/src/main/resources/i18n/messages_zh_CN.properties @@ -0,0 +1,53 @@ +#\u7B80\u4F53\u4E2D\u6587 +500=\u670D\u52A1\u5668\u5185\u90E8\u5F02\u5E38 +401=\u672A\u6388\u6743 +403=\u62D2\u7EDD\u8BBF\u95EE\uFF0C\u6CA1\u6709\u6743\u9650 +10001={0}\u4E0D\u80FD\u4E3A\u7A7A +10002=\u6570\u636E\u5E93\u4E2D\u5DF2\u5B58\u5728\u8BE5\u8BB0\u5F55 +10003=\u83B7\u53D6\u53C2\u6570\u5931\u8D25 +10004=\u8D26\u53F7\u6216\u5BC6\u7801\u9519\u8BEF +10005=\u8D26\u53F7\u5DF2\u88AB\u505C\u7528 +10006=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A +10007=\u9A8C\u8BC1\u7801\u4E0D\u6B63\u786E +10008=\u5148\u5220\u9664\u5B50\u83DC\u5355\u6216\u6309\u94AE +10009=\u539F\u5BC6\u7801\u4E0D\u6B63\u786E +10010=\u8D26\u53F7\u4E0D\u5B58\u5728 +10011=\u4E0A\u7EA7\u90E8\u95E8\u9009\u62E9\u9519\u8BEF +10012=\u4E0A\u7EA7\u83DC\u5355\u4E0D\u80FD\u4E3A\u81EA\u8EAB +10013=\u6570\u636e\u6743\u9650\u6821\u9a8c\u6ca1\u6709\u7b26\u5408\u7c7b\u578b\u7684\u53c2\u6570\u53ef\u4ee5\u5904\u7406 +10014=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u90E8\u95E8 +10015=\u8BF7\u5148\u5220\u9664\u90E8\u95E8\u4E0B\u7684\u7528\u6237 +10016=\u90E8\u7F72\u5931\u8D25\uFF0C\u6CA1\u6709\u6D41\u7A0B +10017=\u6A21\u578B\u56FE\u4E0D\u6B63\u786E\uFF0C\u8BF7\u68C0\u67E5 +10018=\u5BFC\u51FA\u5931\u8D25\uFF0C\u6A21\u578BID\u4E3A{0} +10019=\u8BF7\u4E0A\u4F20\u6587\u4EF6 +10020=token\u4E0D\u80FD\u4E3A\u7A7A +10021=token\u5931\u6548\uFF0C\u8BF7\u91CD\u65B0\u767B\u5F55 +10022=\u8D26\u53F7\u5DF2\u88AB\u9501\u5B9A +10023=\u8BF7\u4E0A\u4F20zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6 +10024=\u4E0A\u4F20\u6587\u4EF6\u5931\u8D25{0} +10025=\u53D1\u9001\u77ED\u4FE1\u5931\u8D25{0} +10026=\u90AE\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728 +10027=Redis\u670D\u52A1\u5F02\u5E38 +10028=\u5B9A\u65F6\u4EFB\u52A1\u5931\u8D25 +10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 +10030=\u53C2\u6570\u683C\u5F0F\u4E0D\u6B63\u786E\uFF0C\u8BF7\u4F7F\u7528JSON\u683C\u5F0F +10031=\u8BF7\u5148\u5B8C\u6210\u77ED\u4FE1\u914D\u7F6E +10200=\u8D26\u53F7\u5DF2\u5B58\u5728 +10032=\u4EFB\u52A1\u5DF2\u88AB\u7B7E\u6536\uFF0C\u64CD\u4F5C\u5931\u8D25 +10033=\u4E0D\u5B58\u5728\u7684\u6D41\u7A0B\u5B9A\u4E49 +10034=\u4E0A\u7EA7\u8282\u70B9\u4E0D\u5B58\u5728 +10035=\u9A73\u56DE +10036=\u56DE\u9000 +10037=\u4EFB\u52A1\u6CA1\u6709\u5206\u7EC4\uFF0C\u65E0\u6CD5\u53D6\u6D88\u8BA4\u9886 +10038=\u4E0A\u7EA7\u533A\u57DF\u9009\u62E9\u9519\u8BEF +10039=\u8BF7\u5148\u5220\u9664\u4E0B\u7EA7\u533A\u57DF +10040=\u6D41\u7A0B\u5DF2\u6302\u8D77\uFF0C\u4E0D\u80FD\u542F\u52A8\u5B9E\u4F8B +10041=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u9A73\u56DE +10042=\u5B58\u5728\u591A\u4E2A\u5904\u7406\u4E2D\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u9A73\u56DE +10043=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u7EC8\u6B62 +10044=\u5B58\u5728\u591A\u4E2A\u5904\u7406\u4E2D\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u7EC8\u6B62\u6D41\u7A0B +10045=\u7EC8\u6B62 +10046=\u591A\u5B9E\u4F8B\u4EFB\u52A1\u4E0D\u80FD\u56DE\u9000 +10047=\u5B58\u5728\u591A\u4E2A\u5E76\u884C\u6267\u884C\u7684\u4EFB\u52A1\uFF0C\u4E0D\u80FD\u56DE\u9000 +10048=\u767B\u5F55\u8D26\u53F7\uFF0C\u65E0\u6743\u5220\u9664 \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/messages_zh_TW.properties b/common/core/src/main/resources/i18n/messages_zh_TW.properties new file mode 100644 index 0000000..fc50c39 --- /dev/null +++ b/common/core/src/main/resources/i18n/messages_zh_TW.properties @@ -0,0 +1,53 @@ +#\u7E41\u4F53\u4E2D\u6587 +500=\u670D\u52D9\u5668\u5167\u90E8\u7570\u5E38 +401=\u672A\u6388\u6B0A +403=\u62D2\u7D55\u8A2A\u554F\uFF0C\u6C92\u6709\u6B0A\u9650 +10001={0}\u4E0D\u80FD\u70BA\u7A7A +10002=\u6578\u64DA\u5EAB\u4E2D\u5DF2\u5B58\u5728\u8A72\u8A18\u9304 +10003=\u7372\u53D6\u53C3\u6578\u5931\u6557 +10004=\u8CEC\u865F\u6216\u5BC6\u78BC\u932F\u8AA4 +10005=\u8CEC\u865F\u5DF2\u88AB\u505C\u7528 +10006=\u552F\u4E00\u6A19\u8B58\u4E0D\u80FD\u70BA\u7A7A +10007=\u9A57\u8B49\u78BC\u4E0D\u6B63\u78BA +10008=\u5148\u522A\u9664\u5B50\u83DC\u55AE\u6216\u6309\u9215 +10009=\u539F\u5BC6\u78BC\u4E0D\u6B63\u78BA +10010=\u8CEC\u865F\u4E0D\u5B58\u5728 +10011=\u4E0A\u7D1A\u90E8\u9580\u9078\u64C7\u932F\u8AA4 +10012=\u4E0A\u7D1A\u83DC\u55AE\u4E0D\u80FD\u70BA\u81EA\u8EAB +10013=\u6578\u64DA\u6B0A\u9650\u63A5\u53E3\uFF0C\u53EA\u80FD\u662FMap\u985E\u578B\u53C3\u6578 +10014=\u8ACB\u5148\u522A\u9664\u4E0B\u7D1A\u90E8\u9580 +10015=\u8ACB\u5148\u522A\u9664\u90E8\u9580\u4E0B\u7684\u7528\u6236 +10016=\u90E8\u7F72\u5931\u6557\uFF0C\u6C92\u6709\u6D41\u7A0B +10017=\u6A21\u578B\u5716\u4E0D\u6B63\u78BA\uFF0C\u8ACB\u6AA2\u67E5 +10018=\u5C0E\u51FA\u5931\u6557\uFF0C\u6A21\u578BID\u70BA{0} +10019=\u8ACB\u4E0A\u50B3\u6587\u4EF6 +10020=token\u4E0D\u80FD\u70BA\u7A7A +10021=token\u5931\u6548\uFF0C\u8ACB\u91CD\u65B0\u767B\u9304 +10022=\u8CEC\u865F\u5DF2\u88AB\u9396\u5B9A +10023=\u8ACB\u4E0A\u50B3zip\u3001bar\u3001bpmn\u3001bpmn20.xml\u683C\u5F0F\u6587\u4EF6 +10024=\u4E0A\u50B3\u6587\u4EF6\u5931\u6557{0} +10025=\u767C\u9001\u77ED\u4FE1\u5931\u6557{0} +10026=\u90F5\u4EF6\u6A21\u677F\u4E0D\u5B58\u5728 +10027=Redis\u670D\u52D9\u7570\u5E38 +10028=\u5B9A\u6642\u4EFB\u52D9\u5931\u6557 +10029=\u4E0D\u80FD\u5305\u542B\u975E\u6CD5\u5B57\u7B26 +10030=\u53C3\u6578\u683C\u5F0F\u4E0D\u6B63\u78BA\uFF0C\u8ACB\u4F7F\u7528JSON\u683C\u5F0F +10031=\u8ACB\u5148\u5B8C\u6210\u77ED\u4FE1\u914D\u7F6E +10200=\u8CEC\u865F\u5DF2\u5B58\u5728 +10032=\u4EFB\u52D9\u5DF2\u88AB\u7C3D\u6536\uFF0C\u64CD\u4F5C\u5931\u6557 +10033=\u4E0D\u5B58\u5728\u7684\u6D41\u7A0B\u5B9A\u7FA9 +10034=\u4E0A\u7D1A\u7BC0\u9EDE\u4E0D\u5B58\u5728 +10035=\u99C1\u56DE +10036=\u56DE\u9000 +10037=\u4EFB\u52D9\u6C92\u6709\u5206\u7D44\uFF0C\u7121\u6CD5\u53D6\u6D88\u8A8D\u9818 +10038=\u4E0A\u7D1A\u5340\u57DF\u9078\u64C7\u932F\u8AA4 +10039=\u8ACB\u5148\u522A\u9664\u4E0B\u7D1A\u5340\u57DF +10040=\u6D41\u7A0B\u5DF2\u639B\u8D77\uFF0C\u4E0D\u80FD\u555F\u52D5\u5BE6\u4F8B +10041=\u591A\u5BE6\u4F8B\u4EFB\u52D9\u4E0D\u80FD\u99C1\u56DE +10042=\u5B58\u5728\u591A\u500B\u8655\u7406\u4E2D\u7684\u4EFB\u52D9\uFF0C\u4E0D\u80FD\u99C1\u56DE +10043=\u591A\u5BE6\u4F8B\u4EFB\u52D9\u4E0D\u80FD\u7D42\u6B62 +10044=\u5B58\u5728\u591A\u500B\u8655\u7406\u4E2D\u7684\u4EFB\u52D9\uFF0C\u4E0D\u80FD\u7D42\u6B62\u6D41\u7A0B +10045=\u7D42\u6B62 +10046=\u591A\u5BE6\u4F8B\u4EFB\u52D9\u4E0D\u80FD\u56DE\u9000 +10047=\u5B58\u5728\u591A\u500B\u4E26\u884C\u57F7\u884C\u7684\u4EFB\u52D9\uFF0C\u4E0D\u80FD\u56DE\u9000 +10048=\u767B\u5165\u5E33\u865F\uFF0C\u7121\u6B0A\u5220\u9664 \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/validation.properties b/common/core/src/main/resources/i18n/validation.properties new file mode 100644 index 0000000..0e3439f --- /dev/null +++ b/common/core/src/main/resources/i18n/validation.properties @@ -0,0 +1,115 @@ +#\u7B80\u4F53\u4E2D\u6587 +id.require=ID\u4E0D\u80FD\u4E3A\u7A7A +id.null=ID\u5FC5\u987B\u4E3A\u7A7A +pid.require=\u4E0A\u7EA7ID\uFF0C\u4E0D\u80FD\u4E3A\u7A7A +sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0 + +sysparams.paramcode.require=\u53C2\u6570\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysparams.paramvalue.require=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A + +sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2 +sysuser.email.require=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A +sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E +sysuser.mobile.require=\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A +sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A +sysuser.superadmin.range=\u8D85\u7EA7\u7BA1\u7406\u5458\u53D6\u503C\u8303\u56F40~1 +sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A + +sysmenu.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u83DC\u5355 +sysmenu.name.require=\u83DC\u5355\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysmenu.type.range=\u83DC\u5355\u7C7B\u578B\u53D6\u503C\u8303\u56F40~1 +sysmenu.openstyle.range=\u83DC\u5355\u6253\u5F00\u65B9\u5F0F\u53D6\u503C\u8303\u56F40~1 + +sysdept.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u90E8\u95E8 +sysdept.name.require=\u90E8\u95E8\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysrole.name.require=\u89D2\u8272\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A +sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A + +schedule.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +schedule.cron.require=cron\u8868\u8FBE\u5F0F\u4E0D\u80FD\u4E3A\u7A7A +schedule.bean.require=bean\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +oss.type.range=\u7C7B\u578B\u53D6\u503C\u8303\u56F41~6 + +aliyun.accesskeyid.require=\u963F\u91CC\u4E91AccessKeyId\u4E0D\u80FD\u4E3A\u7A7A +aliyun.accesskeysecret.require=\u963F\u91CC\u4E91AccessKeySecret\u4E0D\u80FD\u4E3A\u7A7A +aliyun.signname.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.templatecode.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u6A21\u677F\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.require=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.url=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +aliyun.endPoint.require=\u963F\u91CC\u4E91EndPoint\u4E0D\u80FD\u4E3A\u7A7A +aliyun.bucketname.require=\u963F\u91CC\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A + +qcloud.appid.require=\u817E\u8BAF\u4E91AppId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.appkey.require=\u817E\u8BAF\u4E91AppKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretId.require=\u817E\u8BAF\u4E91SecretId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretkey.require=\u817E\u8BAF\u4E91SecretKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.signname.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.templateid.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.require=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.url=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qcloud.bucketname.require=\u817E\u8BAF\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A +qcloud.region.require=\u6240\u5C5E\u5730\u533A\u4E0D\u80FD\u4E3A\u7A7A + +qiniu.domain.require=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.domain.url=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qiniu.accesskey.require=\u4E03\u725BAccessKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.secretkey.require=\u4E03\u725BSecretKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.bucketname.require=\u4E03\u725B\u7A7A\u95F4\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.templateId.require=\u4E03\u725B\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A + +fastdfs.domain.require=FastDFS\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +fastdfs.domain.url=FastDFS\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E + +local.domain.require=\u672C\u5730\u4E0A\u4F20\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +local.domain.url=\u672C\u5730\u4E0A\u4F20\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +local.path.url=\u5B58\u50A8\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A + +minio.endPoint.require=Minio EndPoint\u4E0D\u80FD\u4E3A\u7A7A +minio.accesskey.require=Minio AccessKey\u4E0D\u80FD\u4E3A\u7A7A +minio.secretkey.require=Minio SecretKey\u4E0D\u80FD\u4E3A\u7A7A +minio.bucketname.require=Minio BucketName\u4E0D\u80FD\u4E3A\u7A7A + +sms.platform.range=\u5E73\u53F0\u7C7B\u578B\u53D6\u503C\u8303\u56F41~3 +email.smtp.require=SMTP\u4E0D\u80FD\u4E3A\u7A7A +email.port.require=\u7AEF\u53E3\u53F7\u4E0D\u80FD\u4E3A\u7A7A +email.username.require=\u90AE\u7BB1\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A +email.password.require=\u90AE\u7BB1\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A + +mail.name.require=\u6A21\u677F\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +mail.subject.require=\u90AE\u4EF6\u4E3B\u9898\u4E0D\u80FD\u4E3A\u7A7A +mail.content.require=\u90AE\u4EF6\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A + +model.name.require=\u6A21\u578B\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +model.key.require=\u6A21\u578B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A + +news.title.require=\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A +news.content.require=\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A +news.pubdate.require=\u53D1\u5E03\u65F6\u95F4\u4E0D\u80FD\u4E3A\u7A7A + +tenant.tenantCode.require=\u79DF\u6237\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A +tenant.tenantName.require=\u79DF\u6237\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +tenant.username.require=\u767B\u5F55\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A +tenant.password.require=\u767B\u5F55\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +tenant.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 + +region.id.require=\u533A\u57DF\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A +region.pid.require=\u4E0A\u7EA7\u533A\u57DF\u4E0D\u80FD\u4E3A\u7A7A +region.name.require=\u533A\u57DF\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +processBizRoute.procDefId.require=\u6D41\u7A0B\u5B9A\u4E49ID\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.bizRoute.require=\u4E1A\u52A1\u8DEF\u7531\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.procDefKey.require=\u6D41\u7A0B\u5B9A\u4E49KEY\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.version.require=\u6D41\u7A0B\u5B9A\u4E49\u7248\u672C\u53F7\u4E0D\u80FD\u4E3A\u7A7A + +ProcessStart.processDefinitionKey.require=\u6D41\u7A0B\u5B9A\u4E49KEY\u4E0D\u80FD\u4E3A\u7A7A +ProcessStart.businessKey.require=\u4E1A\u52A1KEY\u4E0D\u80FD\u4E3A\u7A7A \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/validation_en_US.properties b/common/core/src/main/resources/i18n/validation_en_US.properties new file mode 100644 index 0000000..b7b9e9d --- /dev/null +++ b/common/core/src/main/resources/i18n/validation_en_US.properties @@ -0,0 +1,116 @@ +#English +id.require=ID can not be empty +id.null=ID has to be empty +pid.require=Parent ID, cannot be empty +sort.number=The sort value cannot be less than 0 + +sysparams.paramcode.require=Parameter encoding cannot be empty +sysparams.paramvalue.require=Parameter values cannot be empty + +sysuser.username.require=The username cannot be empty +sysuser.password.require=The password cannot be empty +sysuser.realname.require=The realname cannot be empty +sysuser.gender.range=Gender ranges from 0 to 2 +sysuser.email.require=Mailbox cannot be empty +sysuser.email.error=Incorrect email format +sysuser.mobile.require=The phone number cannot be empty +sysuser.deptId.require=Departments cannot be empty +sysuser.superadmin.range=Super administrator values range from 0 to 1 +sysuser.status.range=State ranges from 0 to 1 +sysuser.captcha.require=The captcha cannot be empty +sysuser.uuid.require=The unique identifier cannot be empty + +sysmenu.pid.require=Please select superior menu +sysmenu.name.require=Menu name cannot be empty +sysmenu.type.range=Menu type ranges from 0 to 1 +sysmenu.openstyle.range=Menu open style ranges from 0 to 1 + +sysdept.pid.require=Please select superior department +sysdept.name.require=The department name cannot be empty + +sysrole.name.require=The role name cannot be empty + +sysdict.type.require=The dictionary type cannot be empty +sysdict.name.require=The dictionary name cannot be empty +sysdict.label.require=Dictionary tag cannot be empty + +schedule.status.range=Status ranges from 0 to 1 +schedule.cron.require=Cron expression cannot be empty +schedule.bean.require=Bean name cannot be empty + +oss.type.range=Type ranges from 1 to 6 + +aliyun.accesskeyid.require=Aliyun AccessKeyId cannot be empty +aliyun.accesskeysecret.require=Aliyun AccessKeySecret cannot be empty +aliyun.signname.require=Aliyun message signature cannot be empty +aliyun.templatecode.require=Aliyun message template cannot be empty +aliyun.domain.require=Aliyun bound domain name cannot be empty +aliyun.domain.url=Aliyun binding domain name format is incorrect +aliyun.endPoint.require=The aliyun EndPoint cannot be empty +aliyun.bucketname.require=Aliyun BucketName cannot be empty + +qcloud.appid.require=Tencent cloud AppId cannot be empty +qcloud.appkey.require=Tencent cloud AppKey cannot be empty +qcloud.secretId.require=Tencent cloud SecretId cannot be empty +qcloud.secretkey.require=Tencent cloud SecretKey cannot be empty +qcloud.signname.require=Tencent cloud SMS signature cannot be empty +qcloud.templateid.require=Tencent cloud SMS template ID cannot be empty +qcloud.domain.require=Tencent cloud bound domain name cannot be empty +qcloud.domain.url=Tencent cloud binding domain name format is incorrect +qcloud.bucketname.require=Tencent cloud BucketName cannot be empty +qcloud.region.require=The region cannot be empty + +qiniu.domain.require=Bound domain name cannot be empty +qiniu.domain.url=Binding domain name format is incorrect +qiniu.accesskey.require=The AccessKey cannot be empty +qiniu.secretkey.require=The SecretKey of seven cows cannot be empty +qiniu.bucketname.require=Space names cannot be empty +qiniu.templateId.require=Template ID cannot be empty + +fastdfs.domain.require=FastDFS bound domain name cannot be empty +fastdfs.domain.url=FastDFS bound domain name format is incorrect + +local.domain.require=Local upload bound domain name cannot be empty +local.domain.url=The domain name bound for local upload is not formatted correctly +local.path.url=The storage directory cannot be empty + +minio.endPoint.require=Minio EndPoint cannot be empty +minio.accesskey.require=Minio AccessKey cannot be empty +minio.secretkey.require=Minio SecretKey cannot be empty +minio.bucketname.require=Minio BucketName cannot be empty + +sms.platform.range=Platform type ranges from 1 to 2 + +email.smtp.require=SMTP cannot be empty +email.port.require=Port number cannot be empty +email.username.require=The email account cannot be empty +email.password.require=The password cannot be empty + +mail.name.require=The template name cannot be empty +mail.subject.require=Mail subject cannot be empty +mail.content.require=Message text cannot be empty + +model.name.require=The model name cannot be empty +model.key.require=The model key cannot be empty + +news.title.require=The title cannot be empty +news.content.require=Content cannot be empty +news.pubdate.require=The release time cannot be empty + +tenant.tenantCode.require=The tenant code cannot be empty +tenant.tenantName.require=The tenant name cannot be empty +tenant.username.require=The login account cannot be empty +tenant.password.require=The login password cannot be empty +tenant.status.range=Status range 0~1 + +region.id.require=Region ID cannot be empty +region.pid.require=Upper area cannot be empty +region.name.require=Region name cannot be empty + +processBizRoute.procDefId.require=Process Definition ID cannot be empty +processBizRoute.bizRoute.require=Service routing cannot be empty +processBizRoute.procDefKey.require=Process Definition KEY cannot be empty +processBizRoute.version.require=Process Definition Version number cannot be empty + +ProcessStart.processDefinitionKey.require=Process Definition KEY cannot be empty +ProcessStart.businessKey.require=Business KEY cannot be empty \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/validation_zh_CN.properties b/common/core/src/main/resources/i18n/validation_zh_CN.properties new file mode 100644 index 0000000..0e3439f --- /dev/null +++ b/common/core/src/main/resources/i18n/validation_zh_CN.properties @@ -0,0 +1,115 @@ +#\u7B80\u4F53\u4E2D\u6587 +id.require=ID\u4E0D\u80FD\u4E3A\u7A7A +id.null=ID\u5FC5\u987B\u4E3A\u7A7A +pid.require=\u4E0A\u7EA7ID\uFF0C\u4E0D\u80FD\u4E3A\u7A7A +sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u4E8E0 + +sysparams.paramcode.require=\u53C2\u6570\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysparams.paramvalue.require=\u53C2\u6570\u503C\u4E0D\u80FD\u4E3A\u7A7A + +sysuser.username.require=\u7528\u6237\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.password.require=\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u4E3A\u7A7A +sysuser.gender.range=\u6027\u522B\u53D6\u503C\u8303\u56F40~2 +sysuser.email.require=\u90AE\u7BB1\u4E0D\u80FD\u4E3A\u7A7A +sysuser.email.error=\u90AE\u7BB1\u683C\u5F0F\u4E0D\u6B63\u786E +sysuser.mobile.require=\u624B\u673A\u53F7\u4E0D\u80FD\u4E3A\u7A7A +sysuser.deptId.require=\u90E8\u95E8\u4E0D\u80FD\u4E3A\u7A7A +sysuser.superadmin.range=\u8D85\u7EA7\u7BA1\u7406\u5458\u53D6\u503C\u8303\u56F40~1 +sysuser.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +sysuser.captcha.require=\u9A8C\u8BC1\u7801\u4E0D\u80FD\u4E3A\u7A7A +sysuser.uuid.require=\u552F\u4E00\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A + +sysmenu.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u83DC\u5355 +sysmenu.name.require=\u83DC\u5355\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysmenu.type.range=\u83DC\u5355\u7C7B\u578B\u53D6\u503C\u8303\u56F40~1 +sysmenu.openstyle.range=\u83DC\u5355\u6253\u5F00\u65B9\u5F0F\u53D6\u503C\u8303\u56F40~1 + +sysdept.pid.require=\u8BF7\u9009\u62E9\u4E0A\u7EA7\u90E8\u95E8 +sysdept.name.require=\u90E8\u95E8\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysrole.name.require=\u89D2\u8272\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +sysdict.type.require=\u5B57\u5178\u7C7B\u578B\u4E0D\u80FD\u4E3A\u7A7A +sysdict.name.require=\u5B57\u5178\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +sysdict.label.require=\u5B57\u5178\u6807\u7B7E\u4E0D\u80FD\u4E3A\u7A7A + +schedule.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 +schedule.cron.require=cron\u8868\u8FBE\u5F0F\u4E0D\u80FD\u4E3A\u7A7A +schedule.bean.require=bean\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +oss.type.range=\u7C7B\u578B\u53D6\u503C\u8303\u56F41~6 + +aliyun.accesskeyid.require=\u963F\u91CC\u4E91AccessKeyId\u4E0D\u80FD\u4E3A\u7A7A +aliyun.accesskeysecret.require=\u963F\u91CC\u4E91AccessKeySecret\u4E0D\u80FD\u4E3A\u7A7A +aliyun.signname.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.templatecode.require=\u963F\u91CC\u4E91\u77ED\u4FE1\u6A21\u677F\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.require=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +aliyun.domain.url=\u963F\u91CC\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +aliyun.endPoint.require=\u963F\u91CC\u4E91EndPoint\u4E0D\u80FD\u4E3A\u7A7A +aliyun.bucketname.require=\u963F\u91CC\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A + +qcloud.appid.require=\u817E\u8BAF\u4E91AppId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.appkey.require=\u817E\u8BAF\u4E91AppKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretId.require=\u817E\u8BAF\u4E91SecretId\u4E0D\u80FD\u4E3A\u7A7A +qcloud.secretkey.require=\u817E\u8BAF\u4E91SecretKey\u4E0D\u80FD\u4E3A\u7A7A +qcloud.signname.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u7B7E\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.templateid.require=\u817E\u8BAF\u4E91\u77ED\u4FE1\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.require=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qcloud.domain.url=\u817E\u8BAF\u4E91\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qcloud.bucketname.require=\u817E\u8BAF\u4E91BucketName\u4E0D\u80FD\u4E3A\u7A7A +qcloud.region.require=\u6240\u5C5E\u5730\u533A\u4E0D\u80FD\u4E3A\u7A7A + +qiniu.domain.require=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.domain.url=\u4E03\u725B\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +qiniu.accesskey.require=\u4E03\u725BAccessKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.secretkey.require=\u4E03\u725BSecretKey\u4E0D\u80FD\u4E3A\u7A7A +qiniu.bucketname.require=\u4E03\u725B\u7A7A\u95F4\u540D\u4E0D\u80FD\u4E3A\u7A7A +qiniu.templateId.require=\u4E03\u725B\u6A21\u677FID\u4E0D\u80FD\u4E3A\u7A7A + +fastdfs.domain.require=FastDFS\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +fastdfs.domain.url=FastDFS\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E + +local.domain.require=\u672C\u5730\u4E0A\u4F20\u7ED1\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u4E3A\u7A7A +local.domain.url=\u672C\u5730\u4E0A\u4F20\u7ED1\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u786E +local.path.url=\u5B58\u50A8\u76EE\u5F55\u4E0D\u80FD\u4E3A\u7A7A + +minio.endPoint.require=Minio EndPoint\u4E0D\u80FD\u4E3A\u7A7A +minio.accesskey.require=Minio AccessKey\u4E0D\u80FD\u4E3A\u7A7A +minio.secretkey.require=Minio SecretKey\u4E0D\u80FD\u4E3A\u7A7A +minio.bucketname.require=Minio BucketName\u4E0D\u80FD\u4E3A\u7A7A + +sms.platform.range=\u5E73\u53F0\u7C7B\u578B\u53D6\u503C\u8303\u56F41~3 +email.smtp.require=SMTP\u4E0D\u80FD\u4E3A\u7A7A +email.port.require=\u7AEF\u53E3\u53F7\u4E0D\u80FD\u4E3A\u7A7A +email.username.require=\u90AE\u7BB1\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A +email.password.require=\u90AE\u7BB1\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A + +mail.name.require=\u6A21\u677F\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +mail.subject.require=\u90AE\u4EF6\u4E3B\u9898\u4E0D\u80FD\u4E3A\u7A7A +mail.content.require=\u90AE\u4EF6\u6B63\u6587\u4E0D\u80FD\u4E3A\u7A7A + +model.name.require=\u6A21\u578B\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +model.key.require=\u6A21\u578B\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A + +news.title.require=\u6807\u9898\u4E0D\u80FD\u4E3A\u7A7A +news.content.require=\u5185\u5BB9\u4E0D\u80FD\u4E3A\u7A7A +news.pubdate.require=\u53D1\u5E03\u65F6\u95F4\u4E0D\u80FD\u4E3A\u7A7A + +tenant.tenantCode.require=\u79DF\u6237\u7F16\u7801\u4E0D\u80FD\u4E3A\u7A7A +tenant.tenantName.require=\u79DF\u6237\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A +tenant.username.require=\u767B\u5F55\u8D26\u53F7\u4E0D\u80FD\u4E3A\u7A7A +tenant.password.require=\u767B\u5F55\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A +tenant.status.range=\u72B6\u6001\u53D6\u503C\u8303\u56F40~1 + +region.id.require=\u533A\u57DF\u6807\u8BC6\u4E0D\u80FD\u4E3A\u7A7A +region.pid.require=\u4E0A\u7EA7\u533A\u57DF\u4E0D\u80FD\u4E3A\u7A7A +region.name.require=\u533A\u57DF\u540D\u79F0\u4E0D\u80FD\u4E3A\u7A7A + +processBizRoute.procDefId.require=\u6D41\u7A0B\u5B9A\u4E49ID\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.bizRoute.require=\u4E1A\u52A1\u8DEF\u7531\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.procDefKey.require=\u6D41\u7A0B\u5B9A\u4E49KEY\u4E0D\u80FD\u4E3A\u7A7A +processBizRoute.version.require=\u6D41\u7A0B\u5B9A\u4E49\u7248\u672C\u53F7\u4E0D\u80FD\u4E3A\u7A7A + +ProcessStart.processDefinitionKey.require=\u6D41\u7A0B\u5B9A\u4E49KEY\u4E0D\u80FD\u4E3A\u7A7A +ProcessStart.businessKey.require=\u4E1A\u52A1KEY\u4E0D\u80FD\u4E3A\u7A7A \ No newline at end of file diff --git a/common/core/src/main/resources/i18n/validation_zh_TW.properties b/common/core/src/main/resources/i18n/validation_zh_TW.properties new file mode 100644 index 0000000..57a5d6b --- /dev/null +++ b/common/core/src/main/resources/i18n/validation_zh_TW.properties @@ -0,0 +1,116 @@ +#\u7E41\u4F53\u4E2D\u6587 +id.require=ID\u4E0D\u80FD\u70BA\u7A7A +id.null=ID\u5FC5\u9808\u70BA\u7A7A +pid.require=\u4E0A\u7D1AID\uFF0C\u4E0D\u80FD\u70BA\u7A7A +sort.number=\u6392\u5E8F\u503C\u4E0D\u80FD\u5C0F\u65BC0 + +sysparams.paramcode.require=\u53C3\u6578\u7DE8\u78BC\u4E0D\u80FD\u70BA\u7A7A +sysparams.paramvalue.require=\u53C3\u6578\u503C\u4E0D\u80FD\u70BA\u7A7A + +sysuser.username.require=\u7528\u6236\u540D\u4E0D\u80FD\u70BA\u7A7A +sysuser.password.require=\u5BC6\u78BC\u4E0D\u80FD\u70BA\u7A7A +sysuser.realname.require=\u59D3\u540D\u4E0D\u80FD\u70BA\u7A7A +sysuser.gender.range=\u6027\u5225\u53D6\u503C\u7BC4\u570D0~2 +sysuser.email.require=\u90F5\u7BB1\u4E0D\u80FD\u70BA\u7A7A +sysuser.email.error=\u90F5\u7BB1\u683C\u5F0F\u4E0D\u6B63\u78BA +sysuser.mobile.require=\u624B\u6A5F\u865F\u4E0D\u80FD\u70BA\u7A7A +sysuser.deptId.require=\u90E8\u9580\u4E0D\u80FD\u70BA\u7A7A +sysuser.superadmin.range=\u8D85\u7D1A\u7BA1\u7406\u54E1\u53D6\u503C\u7BC4\u570D0~1 +sysuser.status.range=\u72C0\u614B\u53D6\u503C\u7BC4\u570D0~1 +sysuser.captcha.require=\u9A57\u8B49\u78BC\u4E0D\u80FD\u70BA\u7A7A +sysuser.uuid.require=\u552F\u4E00\u6A19\u8B58\u4E0D\u80FD\u70BA\u7A7A + +sysmenu.pid.require=\u8ACB\u9078\u64C7\u4E0A\u7D1A\u83DC\u55AE +sysmenu.name.require=\u83DC\u55AE\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A +sysmenu.type.range=\u83DC\u55AE\u985E\u578B\u53D6\u503C\u7BC4\u570D0~1 +sysmenu.openstyle.range=\u83DC\u55AE\u6253\u958B\u65B9\u5F0F\u53D6\u503C\u7BC4\u570D0~1 + +sysdept.pid.require=\u8ACB\u9078\u64C7\u4E0A\u7D1A\u90E8\u9580 +sysdept.name.require=\u90E8\u9580\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A + +sysrole.name.require=\u89D2\u8272\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A + +sysdict.type.require=\u5B57\u5178\u985E\u578B\u4E0D\u80FD\u70BA\u7A7A +sysdict.name.require=\u5B57\u5178\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A +sysdict.label.require=\u5B57\u5178\u6A19\u7C64\u4E0D\u80FD\u70BA\u7A7A + +schedule.status.range=\u72C0\u614B\u53D6\u503C\u7BC4\u570D0~1 +schedule.cron.require=cron\u8868\u9054\u5F0F\u4E0D\u80FD\u70BA\u7A7A +schedule.bean.require=bean\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A + +oss.type.range=\u985E\u578B\u53D6\u503C\u7BC4\u570D1~6 + +aliyun.accesskeyid.require=\u963F\u91CC\u96F2AccessKeyId\u4E0D\u80FD\u70BA\u7A7A +aliyun.accesskeysecret.require=\u963F\u91CC\u96F2AccessKeySecret\u4E0D\u80FD\u70BA\u7A7A +aliyun.signname.require=\u963F\u91CC\u96F2\u77ED\u4FE1\u7C3D\u540D\u4E0D\u80FD\u70BA\u7A7A +aliyun.templatecode.require=\u963F\u91CC\u96F2\u77ED\u4FE1\u6A21\u677F\u4E0D\u80FD\u70BA\u7A7A +aliyun.domain.require=\u963F\u91CC\u96F2\u7D81\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u70BA\u7A7A +aliyun.domain.url=\u963F\u91CC\u96F2\u7D81\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u78BA +aliyun.endPoint.require=\u963F\u91CC\u96F2EndPoint\u4E0D\u80FD\u70BA\u7A7A +aliyun.bucketname.require=\u963F\u91CC\u96F2BucketName\u4E0D\u80FD\u70BA\u7A7A + +qcloud.appid.require=\u9A30\u8A0A\u96F2AppId\u4E0D\u80FD\u70BA\u7A7A +qcloud.appkey.require=\u9A30\u8A0A\u96F2AppKey\u4E0D\u80FD\u70BA\u7A7A +qcloud.secretId.require=\u9A30\u8A0A\u96F2SecretId\u4E0D\u80FD\u70BA\u7A7A +qcloud.secretkey.require=\u9A30\u8A0A\u96F2SecretKey\u4E0D\u80FD\u70BA\u7A7A +qcloud.signname.require=\u9A30\u8A0A\u96F2\u77ED\u4FE1\u7C3D\u540D\u4E0D\u80FD\u70BA\u7A7A +qcloud.templateid.require=\u9A30\u8A0A\u96F2\u77ED\u4FE1\u6A21\u677FID\u4E0D\u80FD\u70BA\u7A7A +qcloud.domain.require=\u9A30\u8A0A\u96F2\u7D81\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u70BA\u7A7A +qcloud.domain.url=\u9A30\u8A0A\u96F2\u7D81\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u78BA +qcloud.bucketname.require=\u9A30\u8A0A\u96F2BucketName\u4E0D\u80FD\u70BA\u7A7A +qcloud.region.require=\u6240\u5C6C\u5730\u5340\u4E0D\u80FD\u70BA\u7A7A + +qiniu.domain.require=\u4E03\u725B\u7D81\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u70BA\u7A7A +qiniu.domain.url=\u4E03\u725B\u7D81\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u78BA +qiniu.accesskey.require=\u4E03\u725BAccessKey\u4E0D\u80FD\u70BA\u7A7A +qiniu.secretkey.require=\u4E03\u725BSecretKey\u4E0D\u80FD\u70BA\u7A7A +qiniu.bucketname.require=\u4E03\u725B\u7A7A\u9593\u540D\u4E0D\u80FD\u70BA\u7A7A +qiniu.templateId.require=\u4E03\u725B\u6A21\u677FID\u4E0D\u80FD\u70BA\u7A7A + +fastdfs.domain.require=FastDFS\u7D81\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u70BA\u7A7A +fastdfs.domain.url=FastDFS\u7D81\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u78BA + +local.domain.require=\u672C\u5730\u4E0A\u50B3\u7D81\u5B9A\u7684\u57DF\u540D\u4E0D\u80FD\u70BA\u7A7A +local.domain.url=\u672C\u5730\u4E0A\u50B3\u7D81\u5B9A\u7684\u57DF\u540D\u683C\u5F0F\u4E0D\u6B63\u78BA +local.path.url=\u5B58\u5132\u76EE\u9304\u4E0D\u80FD\u70BA\u7A7A + +minio.endPoint.require=Minio EndPoint\u4E0D\u80FD\u70BA\u7A7A +minio.accesskey.require=Minio AccessKey\u4E0D\u80FD\u70BA\u7A7A +minio.secretkey.require=Minio SecretKey\u4E0D\u80FD\u70BA\u7A7A +minio.bucketname.require=Minio BucketName\u4E0D\u80FD\u70BA\u7A7A + +sms.platform.range=\u5E73\u53F0\u985E\u578B\u53D6\u503C\u7BC4\u570D1~3 + +email.smtp.require=SMTP\u4E0D\u80FD\u70BA\u7A7A +email.port.require=\u7AEF\u53E3\u865F\u4E0D\u80FD\u70BA\u7A7A +email.username.require=\u90F5\u7BB1\u8CEC\u865F\u4E0D\u80FD\u70BA\u7A7A +email.password.require=\u90F5\u7BB1\u5BC6\u78BC\u4E0D\u80FD\u70BA\u7A7A + +mail.name.require=\u6A21\u677F\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A +mail.subject.require=\u90F5\u4EF6\u4E3B\u984C\u4E0D\u80FD\u70BA\u7A7A +mail.content.require=\u90F5\u4EF6\u6B63\u6587\u4E0D\u80FD\u70BA\u7A7A + +model.name.require=\u6A21\u578B\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A +model.key.require=\u6A21\u578B\u6A19\u8B58\u4E0D\u80FD\u70BA\u7A7A + +news.title.require=\u6A19\u984C\u4E0D\u80FD\u70BA\u7A7A +news.content.require=\u5167\u5BB9\u4E0D\u80FD\u70BA\u7A7A +news.pubdate.require=\u767C\u4F48\u6642\u9593\u4E0D\u80FD\u70BA\u7A7A + +tenant.tenantCode.require=\u79DF\u6236\u7DE8\u78BC\u4E0D\u80FD\u70BA\u7A7A +tenant.tenantName.require=\u79DF\u6236\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A +tenant.username.require=\u767B\u9304\u8CEC\u865F\u4E0D\u80FD\u70BA\u7A7A +tenant.password.require=\u767B\u9304\u5BC6\u78BC\u4E0D\u80FD\u70BA\u7A7A +tenant.status.range=\u72C0\u614B\u53D6\u503C\u7BC4\u570D0~1 + +region.id.require=\u5340\u57DF\u6A19\u8B58\u4E0D\u80FD\u70BA\u7A7A +region.pid.require=\u4E0A\u7D1A\u5340\u57DF\u4E0D\u80FD\u70BA\u7A7A +region.name.require=\u5340\u57DF\u540D\u7A31\u4E0D\u80FD\u70BA\u7A7A + +processBizRoute.procDefId.require=\u6D41\u7A0B\u5B9A\u7FA9ID\u4E0D\u80FD\u70BA\u7A7A +processBizRoute.bizRoute.require=\u696D\u52D9\u8DEF\u7531\u4E0D\u80FD\u70BA\u7A7A +processBizRoute.procDefKey.require=\u6D41\u7A0B\u5B9A\u7FA9KEY\u4E0D\u80FD\u70BA\u7A7A +processBizRoute.version.require=\u6D41\u7A0B\u5B9A\u7FA9\u7248\u672C\u865F\u4E0D\u80FD\u70BA\u7A7A + +ProcessStart.processDefinitionKey.require=\u6D41\u7A0B\u5B9A\u7FA9KEY\u4E0D\u80FD\u70BA\u7A7A +ProcessStart.businessKey.require=\u696D\u52D9KEY\u4E0D\u80FD\u70BA\u7A7A \ No newline at end of file diff --git a/common/core/src/main/resources/keys/rsa_private.pem b/common/core/src/main/resources/keys/rsa_private.pem new file mode 100644 index 0000000..d4dd38f --- /dev/null +++ b/common/core/src/main/resources/keys/rsa_private.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANA+dOo5OBwL/65X +xR8xPQ6yZO4RnFjGjhSa2XpTfSM+eZK4aY8y6/KuLk4pW7F52dIU1PH+JypkkmRX +tKlTwEfZUA9il+eYJBPWRu2ISupCbXVVkDWsMybp2I6DtGwJbCqfIrW/bCwe2dc8 +aFRNAoO/HTgqXqoJ/sIaeHTh1kXfAgMBAAECgYBUjnfEv5Uj9k9MiIEXKFQlipRn +AqmcdossnK/f9tSiIfUVeWILbl7WPJm6zRpZVyP2JNVOqadXlFzI97/43XP+PHPk +QpiMrqQ94oKMKPVzB5g5TK+bQ53NxXx4i+QBdPtuabX1WMOWb5gwL0qkzqvqLL0u +lH0DuUh+BECjJCr8cQJBAPtwkFh1K5jCzwgyv+2tfwnhYmBaSlA7sii/aGy9rLBF +6QENd/TgdZ7DutqTl/C7gVW/FQTTT3ov6H0cxcxgU1cCQQDUBVW0//IKloyhg7KC +mOqvwbxjOg519Z1qY94eTuv51bGm0H7nD3BR8USgwfJiQQ5ae2YE3mvOxPUtvVpS +utS5AkEArRapF2vZjjiVAsTE4N4tdOTFrySYwbDGjUxum2TbCyGlK8dUy6r07U06 +RRuTXfIGL7vxwTeiH6/5MJ9s/qtU0wJAaVRVJdxZNGVibALvNe4d+T64BlubP4LL +t0Tx3gC7Ppyo6wR9ZvDGjg7cVzjC34kORmkzX4vX5xr16sk9DAiBEQJAF4p+TT+7 +L4GSnhbns0cD4p+lSa5OavukCq/SK67B6EYu+ThqYlJfYs6AAo2JDVDb255oyYy/ +Y78CPACksFUG9A== +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/common/core/src/main/resources/keys/rsa_public.pem b/common/core/src/main/resources/keys/rsa_public.pem new file mode 100644 index 0000000..0a47a41 --- /dev/null +++ b/common/core/src/main/resources/keys/rsa_public.pem @@ -0,0 +1,6 @@ +-----BEGIN PUBLIC KEY----- +MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDQPnTqOTgcC/+uV8UfMT0OsmTu +EZxYxo4Umtl6U30jPnmSuGmPMuvyri5OKVuxednSFNTx/icqZJJkV7SpU8BH2VAP +YpfnmCQT1kbtiErqQm11VZA1rDMm6diOg7RsCWwqnyK1v2wsHtnXPGhUTQKDvx04 +Kl6qCf7CGnh04dZF3wIDAQAB +-----END PUBLIC KEY----- \ No newline at end of file diff --git a/common/data/pom.xml b/common/data/pom.xml new file mode 100644 index 0000000..8f74a8a --- /dev/null +++ b/common/data/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + com.thing.common + data + jar + ThingBI Server Common Data + + + ${basedir}/../.. + + + + + org.projectlombok + lombok + + + + com.github.xiaoymin + knife4j-openapi3-jakarta-spring-boot-starter + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + + + com.thing.common + util + + + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.5.0 + + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + test-compile + + + + + + + diff --git a/common/data/src/main/java/com/thing/common/data/dto/AttrCache.java b/common/data/src/main/java/com/thing/common/data/dto/AttrCache.java new file mode 100644 index 0000000..a04cd30 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/AttrCache.java @@ -0,0 +1,40 @@ +package com.thing.common.data.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; + +import java.util.List; + +/** + * @author zhenghh. 2022-07-25 + **/ +@Getter +public class AttrCache { + + @Schema(description = "code:attr") + private final String key; + + @Schema(description = "物编码") + private final String code; + + @Schema(description = "属性名称") + private final String attr; + + @Schema(description = "设置主键") + private final List list; + + public AttrCache(String key, List list) { + this.key = key; + String[] split = key.split(":"); + this.code = split[0]; + this.attr = split[1]; + this.list = list; + } + + public AttrCache(String code, String attr, List list) { + this.key = code + ":" + attr; + this.code = code; + this.attr = attr; + this.list = list; + } +} diff --git a/common/data/src/main/java/com/thing/common/data/dto/QueueMsgDTO.java b/common/data/src/main/java/com/thing/common/data/dto/QueueMsgDTO.java new file mode 100644 index 0000000..5799ac9 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/QueueMsgDTO.java @@ -0,0 +1,60 @@ +package com.thing.common.data.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author zhenghh. 2022-10-13 + **/ +@Data +public class QueueMsgDTO implements Serializable { + + private static final long serialVersionUID = 2053221377003286494L; + /** + * 物编码 + */ + private String thingCode; + /** + * 属性key + */ + private String attrKey; + /** + * ts时间戳 + */ + private Long ts; + /** + * 遥测值 + */ + private String val; + /** + * ts时间 + */ + private Date dayTime; + /** + * 更新时间 + */ + private Long createTime; + /** + * 数据来源 + */ + private String origin; + + private final Long tenantCode; + private final Long companyId; + private final Long deptId; + + public QueueMsgDTO(String thingCode, String attrKey, Long ts, String val, String origin, Long tenantCode, Long companyId, Long deptId) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.val = val; + this.origin = origin; + this.tenantCode = tenantCode; + this.companyId = companyId; + this.deptId = deptId; + this.dayTime = new Date(ts); + this.createTime = System.currentTimeMillis(); + } +} diff --git a/common/data/src/main/java/com/thing/common/data/dto/TelemetryDeleteDTO.java b/common/data/src/main/java/com/thing/common/data/dto/TelemetryDeleteDTO.java new file mode 100644 index 0000000..5b3de62 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/TelemetryDeleteDTO.java @@ -0,0 +1,31 @@ +package com.thing.common.data.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * 删除 + * @author zhenghh. 2022-11-18 + **/ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class TelemetryDeleteDTO { + + /** + * 物编码 + */ + private String thingCode; + /** + * 时间 + */ + private Long ts; + /** + * 属性 + */ + private String attrKey; + +} diff --git a/common/data/src/main/java/com/thing/common/data/dto/TelemetrySaveDTO.java b/common/data/src/main/java/com/thing/common/data/dto/TelemetrySaveDTO.java new file mode 100644 index 0000000..8c5391e --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/TelemetrySaveDTO.java @@ -0,0 +1,27 @@ +package com.thing.common.data.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * 保存 + * @author zhenghh. 2022-11-18 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TelemetrySaveDTO { + + /** + * 设备编码 + */ + private String thingCode; + /** + * 遥测值列表 + */ + private List tsKvList; + +} diff --git a/common/data/src/main/java/com/thing/common/data/dto/ThingAttrParam.java b/common/data/src/main/java/com/thing/common/data/dto/ThingAttrParam.java new file mode 100644 index 0000000..d4bdbf5 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/ThingAttrParam.java @@ -0,0 +1,37 @@ +package com.thing.common.data.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@NoArgsConstructor +public class ThingAttrParam implements Serializable { + + @Serial + private static final long serialVersionUID = -3058300917822517764L; + + private Long thingId; + /** + * 设备编码 + */ + private String thingCode; + /** + * 设备属性列表 + */ + private List attrList; + + public ThingAttrParam(String thingCode, List attrList) { + this.thingCode = thingCode; + this.attrList = attrList; + } + + public ThingAttrParam(Long thingId, String thingCode, List attrList) { + this.thingId = thingId; + this.thingCode = thingCode; + this.attrList = attrList; + } +} diff --git a/common/data/src/main/java/com/thing/common/data/dto/TsKvReqParam.java b/common/data/src/main/java/com/thing/common/data/dto/TsKvReqParam.java new file mode 100644 index 0000000..17a32b9 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/dto/TsKvReqParam.java @@ -0,0 +1,37 @@ +package com.thing.common.data.dto; + +import com.google.common.collect.Maps; +import lombok.Data; +import org.apache.commons.lang3.math.NumberUtils; + +import java.io.Serializable; +import java.util.Map; + +@Data +public class TsKvReqParam implements Serializable { + + private static final long serialVersionUID = -4578664072250985018L; + + /** + * 时间 + */ + private Long ts; + /** + * 属性-值 + */ + private Map values; + + public TsKvReqParam() { + } + + public TsKvReqParam(Long ts, Map values) { + this.ts = ts; + Map result = Maps.newHashMapWithExpectedSize(values.size()); + values.forEach((key, value) -> { + if (key != null && value != null) { + result.put(key, NumberUtils.isCreatable(value.toString()) ? Double.valueOf(value.toString()) : value); + } + }); + this.values = result; + } +} diff --git a/common/data/src/main/java/com/thing/common/data/event/AbstractQueueEvent.java b/common/data/src/main/java/com/thing/common/data/event/AbstractQueueEvent.java new file mode 100644 index 0000000..4825ad3 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/AbstractQueueEvent.java @@ -0,0 +1,33 @@ +package com.thing.common.data.event; + + + +import org.springframework.context.ApplicationEvent; +import java.io.Serial; +import java.time.Clock; +import java.util.List; + + + +public abstract class AbstractQueueEvent extends ApplicationEvent implements QueueEvent { + + @Serial + private static final long serialVersionUID = 3884264064887765146L; + private final List list; + + public AbstractQueueEvent(Object source, List list) { + super(source); + this.list = list; + } + + public AbstractQueueEvent(Object source, Clock clock, List list) { + super(source, clock); + this.list = list; + } + + @Override + public List getList() { + return list; + } + +} diff --git a/common/data/src/main/java/com/thing/common/data/event/DataFilterEvent.java b/common/data/src/main/java/com/thing/common/data/event/DataFilterEvent.java new file mode 100644 index 0000000..df462bd --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/DataFilterEvent.java @@ -0,0 +1,18 @@ +package com.thing.common.data.event; + + +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.time.Clock; +import java.util.List; + +public class DataFilterEvent extends AbstractQueueEvent { + + public DataFilterEvent(Object source, List protoList) { + super(source, protoList); + } + + public DataFilterEvent(Object source, Clock clock, List protoList) { + super(source, clock, protoList); + } +} diff --git a/common/data/src/main/java/com/thing/common/data/event/EventPublisher.java b/common/data/src/main/java/com/thing/common/data/event/EventPublisher.java new file mode 100644 index 0000000..2cd17fb --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/EventPublisher.java @@ -0,0 +1,37 @@ +package com.thing.common.data.event; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class EventPublisher { + private Long requestId = 1L; + private final PublisherExecutor publisherExecutor; + private final ApplicationEventPublisher eventPublisher; + + @Autowired + public EventPublisher(PublisherExecutor publisherExecutor, ApplicationEventPublisher eventPublisher) { + this.publisherExecutor = publisherExecutor; + this.eventPublisher = eventPublisher; + } + + public void publishAsync(Object event) { + publisherExecutor.submit(() -> { + eventPublisher.publishEvent(event); + }); + } + + public String getRequestId() { + requestId = requestId + 1; + return "/" + requestId; + } + + public void publish(Object event) { + eventPublisher.publishEvent(event); + } + + +} diff --git a/common/data/src/main/java/com/thing/common/data/event/PublisherExecutor.java b/common/data/src/main/java/com/thing/common/data/event/PublisherExecutor.java new file mode 100644 index 0000000..0f5586f --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/PublisherExecutor.java @@ -0,0 +1,19 @@ +package com.thing.common.data.event; + +import com.thing.common.util.thread.AbstractListeningExecutor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Slf4j +@Component +public class PublisherExecutor extends AbstractListeningExecutor { + + @Value("${calculate.maximumPoolSize:16}") + private int poolSize; + + @Override + protected int getThreadPollSize() { + return this.poolSize; + } +} diff --git a/common/data/src/main/java/com/thing/common/data/event/QueueConsumerEvent.java b/common/data/src/main/java/com/thing/common/data/event/QueueConsumerEvent.java new file mode 100644 index 0000000..c273815 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/QueueConsumerEvent.java @@ -0,0 +1,17 @@ +package com.thing.common.data.event; + + +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.time.Clock; +import java.util.List; + +public class QueueConsumerEvent extends AbstractQueueEvent { + public QueueConsumerEvent(Object source, List protoList) { + super(source, protoList); + } + + public QueueConsumerEvent(Object source, Clock clock, List protoList) { + super(source, clock, protoList); + } +} diff --git a/common/data/src/main/java/com/thing/common/data/event/QueueEvent.java b/common/data/src/main/java/com/thing/common/data/event/QueueEvent.java new file mode 100644 index 0000000..2a468bb --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/event/QueueEvent.java @@ -0,0 +1,15 @@ +package com.thing.common.data.event; + +import java.util.List; + +public interface QueueEvent { + + + /** + * 数据列表 + * + * @return list + */ + List getList(); + +} diff --git a/common/data/src/main/java/com/thing/common/data/tskv/TsKvBaseEntity.java b/common/data/src/main/java/com/thing/common/data/tskv/TsKvBaseEntity.java new file mode 100644 index 0000000..8d0b848 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/tskv/TsKvBaseEntity.java @@ -0,0 +1,51 @@ +package com.thing.common.data.tskv; + +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + + +@Data +@ToString +@NoArgsConstructor +public class TsKvBaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String thingCode; + private String attrKey; + private Long ts; + private String val; + + + public TsKvBaseEntity(String thingCode, String attrKey, Long ts, String val) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.val = val; + } + + public int hashCode() { + return Objects.hash(this.thingCode, this.attrKey, this.ts); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TsKvBaseEntity tsKvEntity = (TsKvBaseEntity) obj; + return Objects.equals(thingCode, tsKvEntity.getThingCode()) && Objects.equals(attrKey, tsKvEntity.getAttrKey()) && Objects.equals(ts, tsKvEntity.getTs()); + } + + + + +} diff --git a/common/data/src/main/java/com/thing/common/data/tskv/TsKvDTO.java b/common/data/src/main/java/com/thing/common/data/tskv/TsKvDTO.java new file mode 100644 index 0000000..9488066 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/tskv/TsKvDTO.java @@ -0,0 +1,161 @@ +package com.thing.common.data.tskv; + + +import lombok.extern.slf4j.Slf4j; +import com.google.gson.JsonObject; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.proto.QueueProto.TsKvProto; + +import java.util.List; +import java.util.ArrayList; +import java.util.stream.Collectors; + +@Slf4j +public class TsKvDTO extends TsKvEntity { + + public String toString() { + return "TsKvDTO{" + + "thingCode='" + super.getThingCode() + '\'' + + ", attrKey='" + super.getAttrKey() + '\'' + + ", ts=" + super.getTs() + + ", val='" + super.getVal() + '\'' + + '}'; + } + + public TsKvDTO() { + super(); + } + public TsKvDTO(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + public static DataProto toDataProto(TsKvDTO kvDTO) { + return DataProto.newBuilder().setTskvProto(toTsKvProto(kvDTO)).build(); + } + + public static TsKvProto toTsKvProto(TsKvDTO kvDTO) { + return TsKvProto.newBuilder().setThingCode(kvDTO.getThingCode()).setKey(kvDTO.getAttrKey()).setTs(kvDTO.getTs()).setVal(kvDTO.getVal()).build(); + } + + public static TsKvProto tsKvProto(String thingCode, String attrKey, Long ts, String val) { + return TsKvProto.newBuilder().setThingCode(thingCode).setKey(attrKey).setTs(ts).setVal(val).build(); + } + + public static TsKvDTO toTsKvDTO(DataProto kvProto) { + return toTsKvDTO(kvProto.getTskvProto()); + } + + public static TsKvDTO toTsKvDTO(TsKvProto kvProto) { + return new TsKvDTO(kvProto.getThingCode(), kvProto.getKey(), kvProto.getTs(), kvProto.getVal()); + } + + public static List toTsKvDTOs(List dataProtoList) { + List list = new ArrayList<>(); + dataProtoList.forEach(dataProto -> list.add(toTsKvDTO(dataProto))); + return list; + } + + public static List toTsKvDTOList(List tsKvProtoList) { + List list = new ArrayList<>(); + tsKvProtoList.forEach(tsKvProto -> list.add(toTsKvDTO(tsKvProto))); + return list; + } + + + public static List toDataProtoList(List kvDTOList) { + List list = new ArrayList<>(); + kvDTOList.forEach(kvDTO -> list.add(toDataProto(kvDTO))); + return list; + } + + public static List toTsKvProtoList(List kvDTOList) { + List list = new ArrayList<>(); + kvDTOList.forEach(kvDTO -> list.add(toTsKvProto(kvDTO))); + return list; + } + + /* + { + "A_11000668_1": { + "A29": { + "1706761800000": 0.11 + }, + } + } + */ + public static JsonObject protoToJson(List protoList) { + JsonObject jsonCode = new JsonObject(); + protoList.forEach(dataProto -> { + TsKvProto tsKvProto = dataProto.getTskvProto(); + String code = tsKvProto.getThingCode(); + String key = tsKvProto.getKey(); + String ts = String.valueOf(tsKvProto.getTs()); + String val = tsKvProto.getVal(); + if (jsonCode.get(code) != null) { + if (jsonCode.get(code).getAsJsonObject().get(key) != null) { + jsonCode.get(code).getAsJsonObject().get(key).getAsJsonObject().addProperty(ts, val); + } else { + JsonObject jsonVal = new JsonObject(); + jsonVal.addProperty(ts, val); + jsonCode.get(code).getAsJsonObject().add(key, jsonVal); + } + } else { + JsonObject jsonVal = new JsonObject(); + jsonVal.addProperty(ts, val); + JsonObject jsonKeys = new JsonObject(); + jsonKeys.add(key, jsonVal); + jsonCode.add(code, jsonKeys); + } + }); + return jsonCode; + } + + public static JsonObject DTOToJson(List tsKvDTOList) { + JsonObject jsonCode = new JsonObject(); + tsKvDTOList.forEach(kvDTO -> { + String code = kvDTO.getThingCode(); + String key = kvDTO.getAttrKey(); + String ts = kvDTO.getTs().toString(); + String val = kvDTO.getVal(); + if (jsonCode.get(code) != null) { + if (jsonCode.get(code).getAsJsonObject().get(key) != null) { + jsonCode.get(code).getAsJsonObject().get(key).getAsJsonObject().addProperty(ts, val); + } else { + JsonObject jsonVal = new JsonObject(); + jsonVal.addProperty(ts, val); + jsonCode.get(code).getAsJsonObject().add(key, jsonVal); + } + } else { + JsonObject jsonVal = new JsonObject(); + jsonVal.addProperty(ts, val); + JsonObject jsonKeys = new JsonObject(); + jsonKeys.add(key, jsonVal); + jsonCode.add(code, jsonKeys); + } + }); + return jsonCode; + } + + + /** + * 根据 thingCode和 attrKey 去重并保留 ts 最大 + * + * @param tsKvDTOList 数据集合 + * @return + */ + public static List removeDuplicates(List tsKvDTOList) { + return tsKvDTOList.stream().collect(Collectors.collectingAndThen( + Collectors.toCollection(() -> new ArrayList<>(tsKvDTOList)), + list -> list.stream() + .filter(tsKvDTO -> list.stream() + .filter(t -> t.getThingCode().equals(tsKvDTO.getThingCode()) && t.getAttrKey().equals(tsKvDTO.getAttrKey())) + .mapToLong(TsKvDTO::getTs) + .max() + .orElse(tsKvDTO.getTs()) == tsKvDTO.getTs()) + .distinct() + .collect(Collectors.toList()) + )); + } +} + + diff --git a/common/data/src/main/java/com/thing/common/data/tskv/TsKvEntity.java b/common/data/src/main/java/com/thing/common/data/tskv/TsKvEntity.java new file mode 100644 index 0000000..4547485 --- /dev/null +++ b/common/data/src/main/java/com/thing/common/data/tskv/TsKvEntity.java @@ -0,0 +1,58 @@ +package com.thing.common.data.tskv; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.ToString; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + + +@Data +@ToString +@AllArgsConstructor +@NoArgsConstructor +public abstract class TsKvEntity extends TsKvBaseEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private String thingCode; + private String attrKey; + private Long ts; + private String val; + private Integer year; + private Integer month; + private Integer day; + private Integer hour; + private Integer minute; + + + public TsKvEntity(String thingCode, String attrKey, Long ts, String val) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.val = val; + } + + public int hashCode() { + return Objects.hash(this.thingCode, this.attrKey, this.ts); + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null || getClass() != obj.getClass()) { + return false; + } + TsKvEntity tsKvEntity = (TsKvEntity) obj; + return Objects.equals(thingCode, tsKvEntity.getThingCode()) && Objects.equals(attrKey, tsKvEntity.getAttrKey()) && Objects.equals(ts, tsKvEntity.getTs()); + } + + + + +} diff --git a/common/data/src/main/proto/queue.proto b/common/data/src/main/proto/queue.proto new file mode 100644 index 0000000..1be0644 --- /dev/null +++ b/common/data/src/main/proto/queue.proto @@ -0,0 +1,22 @@ +syntax = "proto3"; +package queue; + +option java_package = "com.thing.common.data.proto"; +option java_outer_classname = "QueueProto"; + +message DataProto { + TsKvProto tskvProto = 1; +} + +message TsKvProto { + string thingCode = 1; + int64 ts = 2; + string key = 3; + string val = 4; + int32 year = 5; + int32 month = 6; + int32 day = 7; + int32 hour = 8; + int32 minute = 9; +} + diff --git a/common/orm/pom.xml b/common/orm/pom.xml new file mode 100644 index 0000000..4e8e241 --- /dev/null +++ b/common/orm/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + orm + ThingBI Server Common Orm + jar + com.thing.common + + + ${basedir}/../.. + + + + com.thing.common + core + + + org.yaml + snakeyaml + + + + com.zaxxer + HikariCP + + + + com.mybatis-flex + mybatis-flex-spring-boot-starter + + + + org.mybatis + mybatis-spring + + + + + org.postgresql + postgresql + + + + com.clickhouse + clickhouse-jdbc + all + + + + com.microsoft.sqlserver + mssql-jdbc + + + \ No newline at end of file diff --git a/common/orm/src/main/java/com/thing/common/orm/annotation/DataFilter.java b/common/orm/src/main/java/com/thing/common/orm/annotation/DataFilter.java new file mode 100644 index 0000000..4572ce2 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/annotation/DataFilter.java @@ -0,0 +1,63 @@ + + +package com.thing.common.orm.annotation; + +import java.lang.annotation.*; +import java.util.Map; + +/** + * 数据过滤注解 + * + * @author Mark sunlightcs@gmail.com + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface DataFilter { + /** + * 表的别名 + */ + String tableAlias() default ""; + + /** + * 用户ID + */ + String userId() default "creator"; + + /** + * 部门ID + */ + String deptId() default "dept_id"; + + /** + * 公司Id + */ + String companyId() default "company_id"; + + /** + * 租户code + */ + String tenantCode() default "tenant_code"; + + /** + * 租户组管理员以及超级管理员查看所有数据还是当前管理员所在的租户的数据 以and拼接 + */ + boolean completeDataMark() default true; + + /** + * 租户组管理员以及超级管理员查看所有数据还是当前管理员所在的租户的数据 以or拼接 + */ + boolean oRcompleteDataMark() default true; + + /** + * 参数获取 + * @return + */ + Class parameterFetch() default Map.class; + + /** + * 限制查询记录数量, 默认不限制 + */ + int limitSize() default 0; + +} \ No newline at end of file diff --git a/common/orm/src/main/java/com/thing/common/orm/config/MyBatisFlexConfig.java b/common/orm/src/main/java/com/thing/common/orm/config/MyBatisFlexConfig.java new file mode 100644 index 0000000..dc4f6da --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/config/MyBatisFlexConfig.java @@ -0,0 +1,61 @@ +package com.thing.common.orm.config; + +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.core.FlexGlobalConfig; +import com.mybatisflex.core.audit.AuditManager; +import com.mybatisflex.core.audit.ConsoleMessageCollector; +import com.mybatisflex.core.keygen.KeyGenerators; +import com.mybatisflex.core.mybatis.FlexConfiguration; +import com.mybatisflex.spring.boot.ConfigurationCustomizer; +import com.mybatisflex.spring.boot.MyBatisFlexCustomizer; +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import com.thing.common.orm.entity.BaseTenantEntity; +import com.thing.common.orm.listener.EntityInsertListener; +import com.thing.common.orm.listener.EntityUpdateListener; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.logging.nologging.NoLoggingImpl; +import org.apache.ibatis.logging.stdout.StdOutImpl; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.context.annotation.Configuration; + + +/** + * mybatis-flex配置 + * + * @author dataprince数据小王子 + */ +@Slf4j +@Configuration +@MapperScan(value = {"com.thing.**.mapper"}) +public class MyBatisFlexConfig implements ConfigurationCustomizer, MyBatisFlexCustomizer { + @Override + public void customize(FlexConfiguration configuration) { + configuration.setLogImpl(NoLoggingImpl.class); + } + + /** + * Mybatis-Flex自定义初始化配置 + * + * @param globalConfig 全局配置 + */ + @Override + public void customize(FlexGlobalConfig globalConfig) { + //统一设置数据库表主键为雪花算法 + FlexGlobalConfig.KeyConfig keyConfig = new FlexGlobalConfig.KeyConfig(); + keyConfig.setKeyType(KeyType.Generator); + keyConfig.setValue(KeyGenerators.snowFlakeId); + keyConfig.setBefore(true); + globalConfig.setKeyConfig(keyConfig); + + // 注册全局数据填充监听器 + globalConfig.registerInsertListener(new EntityInsertListener(), BaseEntity.class, BaseDateEntity.class, BaseInfoEntity.class, BaseTenantEntity.class); + globalConfig.registerUpdateListener(new EntityUpdateListener(), BaseEntity.class, BaseDateEntity.class, BaseInfoEntity.class, BaseTenantEntity.class); + + // 开启审计功能 + AuditManager.setAuditEnable(true); + AuditManager.setMessageCollector(new ConsoleMessageCollector()); + } + +} diff --git a/common/orm/src/main/java/com/thing/common/orm/dto/BaseDTO.java b/common/orm/src/main/java/com/thing/common/orm/dto/BaseDTO.java new file mode 100644 index 0000000..037cfe0 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/dto/BaseDTO.java @@ -0,0 +1,8 @@ +package com.thing.common.orm.dto; + +import java.io.Serial; +import java.io.Serializable; + +public class BaseDTO implements Serializable { + @Serial private static final long serialVersionUID = 3008256691667608639L; +} diff --git a/common/orm/src/main/java/com/thing/common/orm/dto/BaseThingDTO.java b/common/orm/src/main/java/com/thing/common/orm/dto/BaseThingDTO.java new file mode 100644 index 0000000..c0d78ff --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/dto/BaseThingDTO.java @@ -0,0 +1,27 @@ +package com.thing.common.orm.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +@Data +@EqualsAndHashCode(callSuper = true) +public class BaseThingDTO extends BaseDTO { + + @Serial private static final long serialVersionUID = 3008256691667608639L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "名称") + private String name; + + @Schema(description = "编码") + private String code; + + @Schema(description = "扩展字段") + private String extendData; +} diff --git a/common/orm/src/main/java/com/thing/common/orm/dto/UserDetail.java b/common/orm/src/main/java/com/thing/common/orm/dto/UserDetail.java new file mode 100644 index 0000000..0c997fa --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/dto/UserDetail.java @@ -0,0 +1,42 @@ +package com.thing.common.orm.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class UserDetail { + private Long id; + private String username; + private String realName; + private String headUrl; + private Integer gender; + private String email; + private String mobile; + private Long deptId; + private String password; + private Integer status; + private Integer superAdmin; + private Integer superTenant; + private Integer tenantGroup; + private String url; + + /** 租户编码 */ + private Long tenantCode; + + /** 企业顶级节点Id */ + private Long companyId; + + /** 企业顶级节点名称 */ + private String companyName; + + /** 部门数据权限 */ + private List deptIdList; + + /** 子租户列表,superAdmin = 0 且 tenantGroup = 1 时存在 */ + private List tenantCodeList; + /** + * 扩展字段,pid=0时的deptId公司id + */ + private Long consumerPid; +} \ No newline at end of file diff --git a/common/orm/src/main/java/com/thing/common/orm/entity/BaseDateEntity.java b/common/orm/src/main/java/com/thing/common/orm/entity/BaseDateEntity.java new file mode 100644 index 0000000..c478352 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/entity/BaseDateEntity.java @@ -0,0 +1,55 @@ +package com.thing.common.orm.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * Author: SiYang + * Date: 2023/12/11 10:56 + * Description: 需要自动注入创建&修改人信息的实体 + */ +@Data +@Accessors(chain = true) +public class BaseDateEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + + /** + * 修改时间 + */ + private Long updateDate; + + public BaseDateEntity(Long id) { + this.id = id; + } + + public BaseDateEntity() { + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/entity/BaseEntity.java b/common/orm/src/main/java/com/thing/common/orm/entity/BaseEntity.java new file mode 100644 index 0000000..8d4afe6 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/entity/BaseEntity.java @@ -0,0 +1,50 @@ +package com.thing.common.orm.entity; + + +import com.google.common.collect.Maps; +import com.mybatisflex.annotation.Id; + +import lombok.Data; +import lombok.experimental.Accessors; + +import org.springframework.util.CollectionUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 基础实体类,所有实体都需要继承 TODO 等着处理删除 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +public abstract class BaseEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + public static Map toIdMap(List list) { + if (CollectionUtils.isEmpty(list)) { + return Maps.newHashMap(); + } + return list.stream().collect(Collectors.toMap(T::getId, Function.identity())); + } + +} \ No newline at end of file diff --git a/common/orm/src/main/java/com/thing/common/orm/entity/BaseInfoEntity.java b/common/orm/src/main/java/com/thing/common/orm/entity/BaseInfoEntity.java new file mode 100644 index 0000000..705de26 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/entity/BaseInfoEntity.java @@ -0,0 +1,65 @@ +package com.thing.common.orm.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * Author: SiYang + * Date: 2023/12/11 11:04 + * Description: + */ +@Data +@Accessors(chain = true) +public class BaseInfoEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 公司id + */ + private Long companyId; + + /** + * 部门id + */ + private Long deptId; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Long updateDate; + +} diff --git a/common/orm/src/main/java/com/thing/common/orm/entity/BaseTenantEntity.java b/common/orm/src/main/java/com/thing/common/orm/entity/BaseTenantEntity.java new file mode 100644 index 0000000..b8a9325 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/entity/BaseTenantEntity.java @@ -0,0 +1,66 @@ +package com.thing.common.orm.entity; + +import com.google.common.collect.Maps; +import com.mybatisflex.annotation.Id; + +import lombok.Data; +import lombok.experimental.Accessors; + +import org.springframework.util.CollectionUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Author: SiYang TODO 等着处理删除 + * Date: 2023/12/11 11:04 + * Description: + */ +@Data +@Accessors(chain = true) +public class BaseTenantEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Long updateDate; + + public static Map toIdMap(List list) { + if (CollectionUtils.isEmpty(list)) { + return Maps.newHashMap(); + } + return list.stream().collect(Collectors.toMap(T::getId, Function.identity())); + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/handler/MybatisExceptionHandler.java b/common/orm/src/main/java/com/thing/common/orm/handler/MybatisExceptionHandler.java new file mode 100644 index 0000000..0d5161f --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/handler/MybatisExceptionHandler.java @@ -0,0 +1,43 @@ +package com.thing.common.orm.handler; + +import com.thing.common.core.web.response.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.mybatis.spring.MyBatisSystemException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Objects; + +@Slf4j +@RestControllerAdvice +public class MybatisExceptionHandler { + + /** + * 主键或UNIQUE索引,数据重复异常 + */ + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException( + DuplicateKeyException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + log.error("请求地址'{}',数据库中已存在记录'{}'", requestURI, e.getMessage()); + return new Result().error("数据库中已存在该记录,请联系管理员确认"); + } + + /** + * Mybatis系统异常 通用处理 + */ + @ExceptionHandler(MyBatisSystemException.class) + public Result handleCannotFindDataSourceException( + MyBatisSystemException e, HttpServletRequest request) { + String requestURI = request.getRequestURI(); + String message = e.getMessage(); + if (Objects.nonNull(message) && message.contains("CannotFindDataSourceException")) { + log.error("请求地址'{}', 未找到数据源", requestURI); + return new Result().error("未找到数据源,请联系管理员确认"); + } + log.error("请求地址'{}', Mybatis系统异常", requestURI, e); + return new Result().error(message); + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/listener/AuthInfoReceiver.java b/common/orm/src/main/java/com/thing/common/orm/listener/AuthInfoReceiver.java new file mode 100644 index 0000000..d20fd65 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/listener/AuthInfoReceiver.java @@ -0,0 +1,51 @@ +package com.thing.common.orm.listener; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.orm.dto.UserDetail; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; + +import java.util.Objects; + +/** + * @author SiYang + * @date 2024/01/11 15:20 + * @description 基于当前token, 从缓存中获取用户信息 + */ +@Slf4j +public class AuthInfoReceiver { + private CacheManager cacheManager; + + protected UserDetail getCacheUser() { + if (Objects.isNull(cacheManager)) { + this.cacheManager = SpringContextUtils.getBean("cacheManager", CacheManager.class); + } + String key = HttpContextUtils.getToken(); + if (Objects.isNull(key)) { + //log.warn("==========>> get null security key"); + return null; + } + Cache localCache = Objects.requireNonNull(cacheManager.getCache("security")); + Cache.ValueWrapper valueWrapper = localCache.get(key); + if (Objects.nonNull(valueWrapper)) { + return parseCacheAuthInfo(valueWrapper); + } + return null; + } + + private UserDetail parseCacheAuthInfo(Cache.ValueWrapper valueWrapper) { + Object authInfo = valueWrapper.get(); + if (Objects.isNull(authInfo)) { + return null; + } + String authInfoJson = JSON.toJSONString(authInfo); + JSONObject authInfoJsonObj = JSONObject.parseObject(authInfoJson); + return authInfoJsonObj + .getJSONObject("principals") + .getObject("primaryPrincipal", UserDetail.class); + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/listener/EntityInsertListener.java b/common/orm/src/main/java/com/thing/common/orm/listener/EntityInsertListener.java new file mode 100644 index 0000000..b94b537 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/listener/EntityInsertListener.java @@ -0,0 +1,101 @@ +package com.thing.common.orm.listener; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.mybatisflex.annotation.InsertListener; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.TenantGroupEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.orm.dto.UserDetail; +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; + +import java.util.Optional; + +/** + * Entity实体类全局插入数据监听器 + * + * @author dataprince数据小王子 + */ +@Slf4j +public class EntityInsertListener extends AuthInfoReceiver implements InsertListener { + + @Override + public void onInsert(Object entity) { + try { + if (ObjectUtil.isNotNull(entity)) { + Long now = System.currentTimeMillis(); + UserDetail loginUser = Optional.ofNullable(getCacheUser()).orElse(new UserDetail()); + handleBaseEntity(entity, now, loginUser); + handleBaseDateEntity(entity, now, loginUser); + handleBaseInfoEntity(entity, now, loginUser); + handleBaseTenantEntity(entity, now, loginUser); + } + } catch (Exception e) { + throw new SysException("全局插入数据监听器注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + } + + private void handleBaseEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseEntity baseEntity) { + baseEntity.setCreateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setCreator(loginUser.getId()); + } + } + + private void handleBaseDateEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseDateEntity baseEntity) { + baseEntity.setCreateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setCreator(loginUser.getId()); + baseEntity.setUpdater(loginUser.getId()); + } + } + + @SuppressWarnings("Duplicates") + private void handleBaseInfoEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseInfoEntity baseEntity) { + baseEntity.setCreateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setCreator(loginUser.getId()); + baseEntity.setUpdater(loginUser.getId()); + baseEntity.setTenantCode(Optional.ofNullable(baseEntity.getTenantCode()).orElse(getTenantCode(loginUser))); + baseEntity.setCompanyId(Optional.ofNullable(baseEntity.getCompanyId()).orElse(baseEntity.getTenantCode())); + baseEntity.setDeptId(Optional.ofNullable(baseEntity.getDeptId()).orElse(baseEntity.getTenantCode())); + } + } + + @SuppressWarnings("Duplicates") + private void handleBaseTenantEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseTenantEntity baseEntity) { + baseEntity.setCreateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setCreator(loginUser.getId()); + baseEntity.setUpdater(loginUser.getId()); + baseEntity.setTenantCode(Optional.ofNullable(baseEntity.getTenantCode()).orElse(getTenantCode(loginUser))); + } + } + + + + private Long getTenantCode( UserDetail user){ + if(user.getTenantCode() == null){ + return null; + } + Long tenantCode = HttpContextUtils.getTenantCode(); + //超级管理员,才可以切换租户 + if(user.getSuperAdmin() == SuperAdminEnum.YES.value() + || user.getTenantGroup() == TenantGroupEnum.YES.value()){ + if(ObjectUtils.isNotEmpty(tenantCode)){ + return tenantCode; + } + } + return user.getTenantCode(); + + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/listener/EntityUpdateListener.java b/common/orm/src/main/java/com/thing/common/orm/listener/EntityUpdateListener.java new file mode 100644 index 0000000..9fa39ff --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/listener/EntityUpdateListener.java @@ -0,0 +1,58 @@ +package com.thing.common.orm.listener; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.http.HttpStatus; +import com.mybatisflex.annotation.UpdateListener; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.dto.UserDetail; +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import com.thing.common.orm.entity.BaseTenantEntity; + +import java.util.Optional; + +/** + * Entity实体类全局更新数据监听器 + * + * @author dataprince数据小王子 + */ +public class EntityUpdateListener extends AuthInfoReceiver implements UpdateListener { + + @Override + public void onUpdate(Object entity) { + try { + if (ObjectUtil.isNotNull(entity)) { + Long now = System.currentTimeMillis(); + + UserDetail loginUser = Optional.ofNullable(getCacheUser()).orElse(new UserDetail()); + handleBaseDateEntity(entity, now, loginUser); + handleBaseInfoEntity(entity, now, loginUser); + handleBaseTenantEntity(entity, now, loginUser); + } + } catch (Exception e) { + throw new SysException("全局更新数据监听器注入异常 => " + e.getMessage(), HttpStatus.HTTP_UNAUTHORIZED); + } + + } + + private void handleBaseDateEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseDateEntity baseEntity) { + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdater(loginUser.getId()); + } + } + + private void handleBaseInfoEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseInfoEntity baseEntity) { + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdater(loginUser.getId()); + } + } + + private void handleBaseTenantEntity(Object entity, Long now, UserDetail loginUser) { + if (entity instanceof BaseTenantEntity baseEntity) { + baseEntity.setUpdateDate(Optional.ofNullable(baseEntity.getCreateDate()).orElse(now)); + baseEntity.setUpdater(loginUser.getId()); + } + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/mapper/PowerBaseMapper.java b/common/orm/src/main/java/com/thing/common/orm/mapper/PowerBaseMapper.java new file mode 100644 index 0000000..573c3e4 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/mapper/PowerBaseMapper.java @@ -0,0 +1,67 @@ +package com.thing.common.orm.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.annotation.DataFilter; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 基础Dao + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + * + * 人人框架中原名为BaseDao, 为了迎合 mybatisFlex 对 mapper 文件的命名要求,更名为 PowerBaseMapper + * 毕竟子类都叫 xxxMapper 了,父类再叫 BaseDao 多少有些别扭 + */ +public interface PowerBaseMapper extends BaseMapper { + + String WRAPPER = "ew"; + + /** + * 根据 entity 条件,查询一条记录 + *

查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常

+ * + * @param queryWrapper 实体对象封装操作类(可以为 null) + */ + @DataFilter(completeDataMark = false, parameterFetch = QueryWrapper.class, limitSize = 1) + default T selectOneExt(@Param(WRAPPER) QueryWrapper queryWrapper) { + return selectOneByQuery(queryWrapper); + } + + /** + * 根据 Wrapper 条件,判断是否存在记录 + * + * @param queryWrapper 实体对象封装操作类 + */ + @DataFilter(completeDataMark = false, parameterFetch = QueryWrapper.class) + default boolean existsExt(QueryWrapper queryWrapper) { + return selectCountByQuery(queryWrapper) > 0; + } + + /** + * 根据 entity 条件,查询全部记录 + * + * @param queryWrapper 实体对象封装操作类(可以为 null) + */ + @DataFilter(completeDataMark = false, parameterFetch = QueryWrapper.class) + default List selectListExt(@Param(WRAPPER) QueryWrapper queryWrapper) { + return selectListByQuery(queryWrapper); + } + + /** + * 根据 entity 条件,查询全部记录(并翻页) + * + * @param page 分页查询条件(可以为 RowBounds.DEFAULT) + * @param queryWrapper 实体对象封装操作类(可以为 null) + */ + @DataFilter(completeDataMark = false, parameterFetch = QueryWrapper.class) + default Page selectPageExt(Page page, @Param(WRAPPER) QueryWrapper queryWrapper) { + return paginate(page, queryWrapper); + } + +} diff --git a/common/orm/src/main/java/com/thing/common/orm/service/IBaseService.java b/common/orm/src/main/java/com/thing/common/orm/service/IBaseService.java new file mode 100644 index 0000000..152e57e --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/service/IBaseService.java @@ -0,0 +1,40 @@ +package com.thing.common.orm.service; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.service.IService; +import com.thing.common.core.web.response.PageData; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** 自定义的服务基类接口 */ +public interface IBaseService extends IService { + + PageData getPageData(Map params); + + PageData getPageData(Page page, Class target); + + PageData getPageData(Map params, Class target); + + Page getPage(Map params); + + Page getPage(Map params, Class target); + + E getByIdAs(Serializable id, Class target); + + List getListByIdsAs(Collection ids, Class target); + + List listAs(Map params, Class target); + + List startPage(List list, Integer pageNum, Integer pageSize); + + void batchDelete(Long[] ids); + + int updateDto(Object dto); + + void saveDto(Object dto); + +} diff --git a/common/orm/src/main/java/com/thing/common/orm/service/impl/BaseServiceImpl.java b/common/orm/src/main/java/com/thing/common/orm/service/impl/BaseServiceImpl.java new file mode 100644 index 0000000..d96bf68 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/service/impl/BaseServiceImpl.java @@ -0,0 +1,210 @@ +package com.thing.common.orm.service.impl; + +import cn.hutool.core.util.ObjectUtil; + +import com.google.common.collect.Lists; +import com.mybatisflex.core.BaseMapper; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; + +import java.io.Serializable; +import java.lang.reflect.ParameterizedType; +import java.util.*; + +/** + * 自定义的服务基类接口实现 + */ +@Slf4j +public abstract class BaseServiceImpl, T> extends ServiceImpl implements IBaseService { + + public abstract QueryWrapper getWrapper(Map params); + + @Autowired + @SuppressWarnings("unused") + private JdbcTemplate jdbcTemplate; + + /** + * 构造基本查询条件 + * @return QueryWrapper + */ + public QueryWrapper buildOrderWrapper(Map params, String defaultOrderField, boolean isAsc) { + QueryWrapper queryWrapper = getWrapper(params); + + //排序字段 + String orderField = (String) params.get(Constant.ORDER_FIELD); + String order = (String) params.get(Constant.ORDER); + if(StringUtils.isNotBlank(order)){ + isAsc = "asc".equalsIgnoreCase(order); + } + + if (StringUtils.isNotBlank(orderField)) { + queryWrapper.orderBy(orderField, isAsc); + } else if (!StringUtils.isBlank(defaultOrderField)) { + queryWrapper.orderBy(defaultOrderField, isAsc); + } + + return queryWrapper; + } + + /** + * 获取分页对象 + * @param params 分页查询参数 + */ + @Override + public Page getPage(Map params) { + Pair pageLimitPair = getPageLimitPair(params); + Page page = new Page<>(pageLimitPair.getLeft(), pageLimitPair.getRight()); + params.put(Constant.PAGE, page); + return page; + } + + @Override + public Page getPage(Map params, Class target) { + Pair pageLimitPair = getPageLimitPair(params); + Page page = new Page<>(pageLimitPair.getLeft(), pageLimitPair.getRight()); + params.put(Constant.PAGE, page); + return page; + } + + private Pair getPageLimitPair(Map params){ + long curPage = 1; + long limit = 10; + + if(params.get(Constant.PAGE) != null){ + curPage = Long.parseLong(String.valueOf(params.get(Constant.PAGE))); + } + if(params.get(Constant.LIMIT) != null){ + limit = Long.parseLong(String.valueOf(params.get(Constant.LIMIT))); + } + return Pair.of(curPage, limit); + } + + @Override + public E getByIdAs(Serializable id, Class target) { + if(null==id){ + return null; + } + T entity = getById(id); + return ConvertUtils.convertWithTypeAdapt(entity, target); + } + + @Override + public List getListByIdsAs(Collection ids, Class target) { + List list = mapper.selectListByIds(ids); + return ConvertUtils.convertWithTypeAdapt(list, target); + } + + public PageData getPageData(Map params) { + Class entityClass = getModelClass(); + return getPageData(params, entityClass); + } + + public PageData getPageData(Map params, Class target, boolean isAsc) { + Page page = + mapper.paginate( + getPage(params), + buildOrderWrapper( + params, + StringUtils.isNotBlank((String) params.get(Constant.ORDER_FIELD)) + ? (String) params.get(Constant.ORDER_FIELD) : null, + isAsc)); + return getPageData(page, target); + } + + public PageData getPageData(Map params, Class target) { + return getPageData(params, target, false); + } + + public PageData getPageData(List list, long total, Class target) { + List targetList = ConvertUtils.convertWithTypeAdapt(list, target); + + return new PageData<>(targetList, total); + } + + public PageData getPageData(Page page, Class target) { + return getPageData(page.getRecords(), page.getTotalRow(), target); + } + + protected void paramsToLike(Map params, String... likes){ + for (String like : likes){ + String val = (String)params.get(like); + if (StringUtils.isNotBlank(val)){ + params.put(like, "%" + val + "%"); + }else { + params.put(like, null); + } + } + } + + public List listAs(Map params, Class target) { + return listAs(getWrapper(params), target); + } + + @Override + public List startPage(List list, Integer pageNum, Integer pageSize) { + if (list == null || ObjectUtil.isNull(pageNum) || ObjectUtil.isNull(pageSize) || pageNum < 1 || pageSize < 1) { + return null; + } + if((list.isEmpty() || (list.size()/pageSize + 1) * pageSize < pageNum * pageSize)){ + return Lists.newArrayList(); + } + + Integer count = list.size(); + int pageCount; + if (count % pageSize == 0) { + pageCount = count / pageSize; + } else { + pageCount = count / pageSize + 1; + } + + int fromIndex; + int toIndex; + pageNum = Math.min(pageNum, pageCount); + if (!pageNum.equals(pageCount)) { + fromIndex = (pageNum - 1) * pageSize; + toIndex = fromIndex + pageSize; + } else { + fromIndex = (pageNum - 1) * pageSize; + toIndex = count; + } + + return list.subList(fromIndex, Math.min(toIndex, list.size())); + } + + @Override + public void batchDelete(Long[] ids) { + mapper.deleteBatchByIds(Arrays.asList(ids)); + } + + @Override + public int updateDto(Object dto) { + return mapper.update(ConvertUtils.convertWithTypeAdapt(dto, getModelClass())); + } + + @Override + public void saveDto(Object dto) { + Class entityClass = getModelClass(); + T entity = ConvertUtils.convertWithTypeAdapt(dto, entityClass); + save(entity); + } + + @SuppressWarnings("unchecked") + private Class getModelClass(){ + return (Class) + ((ParameterizedType) getClass().getGenericSuperclass()) + .getActualTypeArguments()[1]; + } + +} diff --git a/common/orm/src/main/java/com/thing/common/orm/utils/IdGenerator.java b/common/orm/src/main/java/com/thing/common/orm/utils/IdGenerator.java new file mode 100644 index 0000000..0352570 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/utils/IdGenerator.java @@ -0,0 +1,47 @@ +package com.thing.common.orm.utils; + +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; +import java.util.Vector; + +/** + * @author siyang + * @date 2024/4/24 09:50 + * @description 雪花id生成器 + */ +@Slf4j +public class IdGenerator { + + private static final SnowFlakeIDKeyGenerator flexIdGenerator = new SnowFlakeIDKeyGenerator(); + private static final SnowflakeIdWorker snowFlakeIDKeyGenerator = new SnowflakeIdWorker(0, 0); + + public static Long nextId() { + return snowFlakeIDKeyGenerator.nextId(); + } + + public static void main(String[] args) { + List ids1 = new Vector<>(); + List ids2 = new Vector<>(); + List ids3 = new Vector<>(); + for (int i = 0; i < 10000; i++) { + new Thread( + () -> { + ids1.add(new SnowFlakeIDKeyGenerator().nextId()); + ids2.add(flexIdGenerator.nextId()); + ids3.add(snowFlakeIDKeyGenerator.nextId()); + }) + .start(); + } + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + log.info("{} 方案 ==> 总量:{}, 去重后数量:{}", "new SnowFlakeIDKeyGenerator()", ids1.size(), new ArrayList<>(ids1).stream().distinct().count()); + log.info("{} 方案 ==> 总量:{}, 去重后数量:{}", "flexIdGenerator单例", ids2.size(), new ArrayList<>(ids2).stream().distinct().count()); + log.info("{} 方案 ==> 总量:{}, 去重后数量:{}", "snowFlakeIDKeyGenerator单例", ids3.size(), new ArrayList<>(ids3).stream().distinct().count()); + } +} diff --git a/common/orm/src/main/java/com/thing/common/orm/utils/SnowflakeIdWorker.java b/common/orm/src/main/java/com/thing/common/orm/utils/SnowflakeIdWorker.java new file mode 100644 index 0000000..ea1b215 --- /dev/null +++ b/common/orm/src/main/java/com/thing/common/orm/utils/SnowflakeIdWorker.java @@ -0,0 +1,159 @@ +package com.thing.common.orm.utils; + +public class SnowflakeIdWorker { + + // ==============================Fields=========================================== + /** + * 开始时间截 2021-01-01 00:00:00 + * https://tool.lu/timestamp/ + */ + private final long twepoch = 1609430400000L; + + /** + * 机器id所占的位数 + */ + private final long workerIdBits = 5L; + + /** + * 数据标识id所占的位数 + */ + private final long datacenterIdBits = 5L; + + /** + * 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) + */ + private final long maxWorkerId = -1L ^ (-1L << workerIdBits); + + /** + * 支持的最大数据标识id,结果是31 + */ + private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits); + + /** + * 序列在id中占的位数 + */ + private final long sequenceBits = 12L; + + /** + * 机器ID向左移12位 + */ + private final long workerIdShift = sequenceBits; + + /** + * 数据标识id向左移17位(12+5) + */ + private final long datacenterIdShift = sequenceBits + workerIdBits; + + /** + * 时间截向左移22位(5+5+12) + */ + private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits; + + /** + * 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) + */ + private final long sequenceMask = -1L ^ (-1L << sequenceBits); + + /** + * 工作机器ID(0~31) + */ + private long workerId; + + /** + * 数据中心ID(0~31) + */ + private long datacenterId; + + /** + * 毫秒内序列(0~4095) + */ + private long sequence = 0L; + + /** + * 上次生成ID的时间截 + */ + private long lastTimestamp = -1L; + + //==============================Constructors===================================== + + /** + * 构造函数 + * + * @param workerId 工作ID (0~31) + * @param datacenterId 数据中心ID (0~31) + */ + public SnowflakeIdWorker(long workerId, long datacenterId) { + if (workerId > maxWorkerId || workerId < 0) { + throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId)); + } + if (datacenterId > maxDatacenterId || datacenterId < 0) { + throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId)); + } + this.workerId = workerId; + this.datacenterId = datacenterId; + } + + // ==============================Methods========================================== + + /** + * 获得下一个ID (该方法是线程安全的) + * + * @return SnowflakeId + */ + public synchronized long nextId() { + long timestamp = timeGen(); + + //如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常 + if (timestamp < lastTimestamp) { + throw new RuntimeException( + String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", lastTimestamp - timestamp)); + } + + //如果是同一时间生成的,则进行毫秒内序列 + if (lastTimestamp == timestamp) { + sequence = (sequence + 1) & sequenceMask; + //毫秒内序列溢出 + if (sequence == 0) { + //阻塞到下一个毫秒,获得新的时间戳 + timestamp = tilNextMillis(lastTimestamp); + } + } + //时间戳改变,毫秒内序列重置 + else { + sequence = 0L; + } + + //上次生成ID的时间截 + lastTimestamp = timestamp; + + //移位并通过或运算拼到一起组成64位的ID + return ((timestamp - twepoch) << timestampLeftShift) // + | (datacenterId << datacenterIdShift) // + | (workerId << workerIdShift) // + | sequence; + } + + /** + * 阻塞到下一个毫秒,直到获得新的时间戳 + * + * @param lastTimestamp 上次生成ID的时间截 + * @return 当前时间戳 + */ + protected long tilNextMillis(long lastTimestamp) { + long timestamp = timeGen(); + while (timestamp <= lastTimestamp) { + timestamp = timeGen(); + } + return timestamp; + } + + /** + * 返回以毫秒为单位的当前时间 + * + * @return 当前时间(毫秒) + */ + protected long timeGen() { + return System.currentTimeMillis(); + } + +} diff --git a/common/pom.xml b/common/pom.xml new file mode 100644 index 0000000..52f33fe --- /dev/null +++ b/common/pom.xml @@ -0,0 +1,29 @@ + + + 4.0.0 + + com.thing + thingbi + 5.1 + + pom + common + ThingBI Server Commons + + ${basedir}/.. + + + cache + core + data + orm + queue + script + security + transport + tskv + util + + diff --git a/common/queue/pom.xml b/common/queue/pom.xml new file mode 100644 index 0000000..11f75fe --- /dev/null +++ b/common/queue/pom.xml @@ -0,0 +1,109 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + queue + ThingBI Server Common Queue + jar + com.thing.common + + ${basedir}/../.. + + + + + + com.lmax + disruptor + + + + com.google.protobuf + protobuf-java + + + com.google.protobuf + protobuf-java-util + + + com.rabbitmq + amqp-client + + + + org.projectlombok + lombok + + + cn.hutool + hutool-all + + + org.springframework + spring-context + + + javax.annotation + javax.annotation-api + + + org.springframework.boot + spring-boot-autoconfigure + + + org.apache.commons + commons-lang3 + + + com.thing.common + data + + + com.thing.common + util + + + + + + + kr.motd.maven + os-maven-plugin + 1.5.0.Final + + + + + org.xolstice.maven.plugins + protobuf-maven-plugin + 0.5.0 + + + com.google.protobuf:protoc:${protobuf.version}:exe:${os.detected.classifier} + grpc-java + io.grpc:protoc-gen-grpc-java:1.0.0:exe:${os.detected.classifier} + + + + + compile + compile-custom + test-compile + + + + + + + + diff --git a/common/queue/src/main/java/com/thing/queue/QueueAdmin.java b/common/queue/src/main/java/com/thing/queue/QueueAdmin.java new file mode 100644 index 0000000..e5914cd --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueAdmin.java @@ -0,0 +1,23 @@ +package com.thing.queue; + +public interface QueueAdmin { + + /** + * topic不存在创建 + * + * @param topic topic + */ + void createTopicIfNotExists(String topic); + + /** + * 销毁 + */ + void destroy(); + + /** + * 删除指定topic + * + * @param topic topic + */ + void deleteTopic(String topic); +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueCallback.java b/common/queue/src/main/java/com/thing/queue/QueueCallback.java new file mode 100644 index 0000000..5f8e41b --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueCallback.java @@ -0,0 +1,33 @@ +package com.thing.queue; + +/** + * @author zhenghh. 2022-08-30 + **/ +public interface QueueCallback { + + /** + * 成功回调 + * + * @param metadata metadata + */ + void onSuccess(QueueMsgMetadata metadata); + + /** + * 失败回调 + * + * @param t 错误 + */ + void onFailure(Throwable t); + + QueueCallback EMPTY = new QueueCallback() { + @Override + public void onSuccess(QueueMsgMetadata msg) { + + } + + @Override + public void onFailure(Throwable e) { + + } + }; +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueConsumer.java b/common/queue/src/main/java/com/thing/queue/QueueConsumer.java new file mode 100644 index 0000000..02a1c88 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueConsumer.java @@ -0,0 +1,70 @@ +package com.thing.queue; + +import com.thing.queue.partition.TopicPartitionInfo; + +import java.util.List; +import java.util.Set; + +/** + * 队列消费者 + * + * @param 消息 + */ +public interface QueueConsumer { + + /** + * 获取主题 + * + * @return topic + */ + String getTopic(); + + /** + * 开始订阅 + */ + void subscribe(); + + /** + * 重置订阅 + * + * @param partitions 订阅分区 + */ + void subscribe(Set partitions); + + /** + * 新增订阅 + * + * @param partition 订阅分区 + */ + void subscribe(TopicPartitionInfo partition); + + /** + * 取消订阅 + */ + void unsubscribe(); + + /** + * 获取消息集合 + * + * @param durationInMillis 时间间隔 + * @return 消息集合 + */ + List poll(long durationInMillis); + + /** + * 队列总长度 + * @return size + */ + long count(); + + /** + * commit + */ + void commit(); + + /** + * 是否停止 + * @return boolean + */ + boolean isStopped(); +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueMsg.java b/common/queue/src/main/java/com/thing/queue/QueueMsg.java new file mode 100644 index 0000000..1b616a5 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueMsg.java @@ -0,0 +1,8 @@ +package com.thing.queue; + +public interface QueueMsg { + + QueueMsgHeaders getHeaders(); + + byte[] getData(); +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueMsgDecoder.java b/common/queue/src/main/java/com/thing/queue/QueueMsgDecoder.java new file mode 100644 index 0000000..2cd2618 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueMsgDecoder.java @@ -0,0 +1,15 @@ +package com.thing.queue; + +import com.google.protobuf.InvalidProtocolBufferException; + +public interface QueueMsgDecoder { + + /** + * 解码 + * + * @param msg 消息 + * @return 内容 + * @throws InvalidProtocolBufferException e + */ + T decode(QueueMsg msg) throws InvalidProtocolBufferException; +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueMsgHeaders.java b/common/queue/src/main/java/com/thing/queue/QueueMsgHeaders.java new file mode 100644 index 0000000..a0f1fb3 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueMsgHeaders.java @@ -0,0 +1,12 @@ +package com.thing.queue; + +import java.util.Map; + +public interface QueueMsgHeaders { + + byte[] put(String key, byte[] value); + + byte[] get(String key); + + Map getData(); +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueMsgMetadata.java b/common/queue/src/main/java/com/thing/queue/QueueMsgMetadata.java new file mode 100644 index 0000000..c01f26f --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueMsgMetadata.java @@ -0,0 +1,7 @@ +package com.thing.queue; + +/** + * @author zhenghh + */ +public interface QueueMsgMetadata { +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueProducer.java b/common/queue/src/main/java/com/thing/queue/QueueProducer.java new file mode 100644 index 0000000..0b026f7 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueProducer.java @@ -0,0 +1,37 @@ +package com.thing.queue; + +import com.thing.queue.partition.TopicPartitionInfo; + +/** + * 队列消息生产者 + * + * @param + */ +public interface QueueProducer { + + /** + * 初始化 + */ + void init(); + + /** + * 获取默认主题 + * + * @return topic + */ + String getDefaultTopic(); + + /** + * 推送消息至队列 + * + * @param tpi 主题分区 + * @param msg 消息 + * @param callback 回调 + */ + void send(TopicPartitionInfo tpi, T msg, QueueCallback callback); + + /** + * stop + */ + void stop(); +} diff --git a/common/queue/src/main/java/com/thing/queue/QueueThreadFactory.java b/common/queue/src/main/java/com/thing/queue/QueueThreadFactory.java new file mode 100644 index 0000000..a3ada63 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/QueueThreadFactory.java @@ -0,0 +1,37 @@ +package com.thing.queue; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author zhenghh. 2022-06-14 + **/ +public class QueueThreadFactory implements ThreadFactory { + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public static QueueThreadFactory forName(String name) { + return new QueueThreadFactory(name); + } + + private QueueThreadFactory(String name) { + this.group = Thread.currentThread().getThreadGroup(); + this.namePrefix = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-"; + } + + @Override + public Thread newThread(Runnable runnable) { + Thread t = new Thread(this.group, runnable, this.namePrefix + this.threadNumber.getAndIncrement(), 0L); + if (t.isDaemon()) { + t.setDaemon(false); + } + + if (t.getPriority() != 5) { + t.setPriority(5); + } + + return t; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/cluster/ClusterService.java b/common/queue/src/main/java/com/thing/queue/cluster/ClusterService.java new file mode 100644 index 0000000..38c86a6 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/cluster/ClusterService.java @@ -0,0 +1,48 @@ +package com.thing.queue.cluster; + +import com.thing.queue.QueueCallback; +import com.thing.queue.partition.TopicPartitionInfo; +import com.thing.gen.queue.QueueProto.TransportMsg; + +import java.util.UUID; + +/** + * @author zhenghh. 2022-08-24 + **/ +public interface ClusterService { + + /** + * 发送消息至队列 + * + * @param tpi 主体分区信息 + * @param msg 消息主体 + * @param callback 回调 + */ + void pushMsgToCore(TopicPartitionInfo tpi, TransportMsg msg, QueueCallback callback); + + /** + * 发送消息至队列 + * + * @param sessionId sessionId + * @param msg 消息主体 + * @param callback 回调 + */ + void pushMsgToCore(UUID sessionId, TransportMsg msg, QueueCallback callback); + + /** + * 获取发送数量 + * @return 数量 + */ + Integer getSendNum(); + + /** + * 获取发送来源数量 + * @return 来源数量 + */ + Integer getSendOrigin(); + + /** + * 重置 + */ + void reset(); +} diff --git a/common/queue/src/main/java/com/thing/queue/cluster/ClusterServiceImpl.java b/common/queue/src/main/java/com/thing/queue/cluster/ClusterServiceImpl.java new file mode 100644 index 0000000..784d1c1 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/cluster/ClusterServiceImpl.java @@ -0,0 +1,91 @@ +package com.thing.queue.cluster; + +import cn.hutool.core.collection.ConcurrentHashSet; +import com.thing.queue.QueueCallback; +import com.thing.queue.message.ServiceType; +import com.thing.queue.partition.PartitionService; +import com.thing.queue.partition.TopicPartitionInfo; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.queue.provider.QueueProducerProvider; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class ClusterServiceImpl implements ClusterService { + + private final QueueProducerProvider producerProvider; + private final PartitionService partitionService; + /** + * 发送数据量 + */ + private final AtomicInteger sendNum = new AtomicInteger(0); + /** + * 发送数据来源 + */ + private final Set sendOrigin = new ConcurrentHashSet<>(); + + /** + * 发送消息至队列 + * + * @param tpi 主体分区信息 + * @param msg 消息主体 + */ + @Override + public void pushMsgToCore(TopicPartitionInfo tpi, TransportMsg msg, QueueCallback callback) { + producerProvider.getCoreMsgProducer().send(tpi, new ProtoQueueMsg<>(msg), callback); + sendNum.incrementAndGet(); + sendOrigin.add(msg.getOrigin()); + } + + /** + * 发送消息至队列 + * + * @param sessionId 设备编码 + * @param msg 消息主体 + */ + @Override + public void pushMsgToCore(UUID sessionId, TransportMsg msg, QueueCallback callback) { + TopicPartitionInfo tpi = partitionService.resolve(ServiceType.QUEUE_CORE, sessionId); + pushMsgToCore(tpi, msg, callback); + } + + /** + * 获取发送数量 + * + * @return 数量 + */ + @Override + public Integer getSendNum() { + return sendNum.get(); + } + + /** + * 获取发送来源数量 + * + * @return 来源数量 + */ + @Override + public Integer getSendOrigin() { + return this.sendOrigin.size(); + } + + /** + * 重置 + */ + @Override + public void reset() { + this.sendNum.getAndSet(0); + this.sendOrigin.clear(); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/common/AbstractQueueConsumerTemplate.java b/common/queue/src/main/java/com/thing/queue/common/AbstractQueueConsumerTemplate.java new file mode 100644 index 0000000..048d01c --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/common/AbstractQueueConsumerTemplate.java @@ -0,0 +1,190 @@ +package com.thing.queue.common; + +import com.thing.queue.QueueConsumer; +import com.thing.queue.QueueMsg; +import com.thing.queue.partition.TopicPartitionInfo; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; + +@Slf4j +public abstract class AbstractQueueConsumerTemplate implements QueueConsumer { + + public static final long ONE_MILLISECOND_IN_NANOS = TimeUnit.MILLISECONDS.toNanos(1); + private volatile boolean subscribed; + protected volatile boolean stopped = false; + protected volatile Set partitions; + protected final ReentrantLock consumerLock = new ReentrantLock(); + final Queue> subscribeQueue = new ConcurrentLinkedQueue<>(); + + @Getter + private final String topic; + + public AbstractQueueConsumerTemplate(String topic) { + this.topic = topic; + } + + @Override + public void subscribe() { + log.info("enqueue topic subscribe {} ", topic); + if (stopped) { + log.error("trying subscribe, but consumer stopped for topic {}", topic); + return; + } + subscribeQueue.add(Collections.singleton(new TopicPartitionInfo(topic, null))); + } + + @Override + public void subscribe(Set partitions) { + log.info("enqueue topics subscribe {} ", partitions); + if (stopped) { + log.error("trying subscribe, but consumer stopped for topic {}", topic); + return; + } + subscribeQueue.add(partitions); + } + + @Override + public void subscribe(TopicPartitionInfo partition) { + subscribeQueue.add(Collections.singleton(partition)); + } + + @Override + public List poll(long durationInMillis) { + List records; + long startNanos = System.nanoTime(); + if (stopped) { + return errorAndReturnEmpty(); + } + if (!subscribed && partitions == null && subscribeQueue.isEmpty()) { + return sleepAndReturnEmpty(startNanos, durationInMillis); + } + + if (consumerLock.isLocked()) { + log.error("poll. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + } + + consumerLock.lock(); + try { + while (!subscribeQueue.isEmpty()) { + subscribed = false; + partitions = subscribeQueue.poll(); + } + if (!subscribed) { + List topicNames = partitions.stream().map(TopicPartitionInfo::getFullTopicName).collect(Collectors.toList()); + doSubscribe(topicNames); + subscribed = true; + } + records = partitions.isEmpty() ? emptyList() : doPoll(durationInMillis); + } finally { + consumerLock.unlock(); + } + + if (records.isEmpty()) { + return sleepAndReturnEmpty(startNanos, durationInMillis); + } + + return decodeRecords(records); + } + + /** + * 队列总长度 + * + * @return size + */ + @Override + public long count() { + return doCount(); + } + + @Nonnull + List decodeRecords(@Nonnull List records) { + List result = new ArrayList<>(records.size()); + records.forEach(record -> { + try { + if (record != null) { + result.add(decode(record)); + } + } catch (IOException e) { + log.error("Failed decode record: [{}]", record); + throw new RuntimeException("Failed to decode record: ", e); + } + }); + return result; + } + + List errorAndReturnEmpty() { + log.error("poll invoked but consumer stopped for topic" + topic, new RuntimeException("stacktrace")); + return emptyList(); + } + + List sleepAndReturnEmpty(final long startNanos, final long durationInMillis) { + long durationNanos = TimeUnit.MILLISECONDS.toNanos(durationInMillis); + long spentNanos = System.nanoTime() - startNanos; + long nanosLeft = durationNanos - spentNanos; + if (nanosLeft >= ONE_MILLISECOND_IN_NANOS) { + try { + long sleepMs = TimeUnit.NANOSECONDS.toMillis(nanosLeft); + log.trace("Going to sleep after poll: topic {} for {}ms", topic, sleepMs); + Thread.sleep(sleepMs); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Failed to wait", e); + } + } + } + return emptyList(); + } + + @Override + public void commit() { + if (consumerLock.isLocked()) { + log.error("commit. consumerLock is locked. will wait with no timeout. it looks like a race conditions or deadlock topic " + topic, new RuntimeException("stacktrace")); + } + consumerLock.lock(); + try { + doCommit(); + } finally { + consumerLock.unlock(); + } + } + + @Override + public void unsubscribe() { + log.info("unsubscribe topic and stop consumer {}", getTopic()); + stopped = true; + consumerLock.lock(); + try { + doUnsubscribe(); + } finally { + consumerLock.unlock(); + } + } + + @Override + public boolean isStopped() { + return stopped; + } + + abstract protected Integer doCount(); + + abstract protected List doPoll(long durationInMillis); + + abstract protected T decode(R record) throws IOException; + + abstract protected void doSubscribe(List topicNames); + + abstract protected void doCommit(); + + abstract protected void doUnsubscribe(); + +} diff --git a/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueConsumer.java b/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueConsumer.java new file mode 100644 index 0000000..66f0e60 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueConsumer.java @@ -0,0 +1,129 @@ +package com.thing.queue.memory; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.ConcurrentHashSet; +import com.thing.queue.QueueConsumer; +import com.thing.queue.QueueMsg; +import com.thing.queue.partition.TopicPartitionInfo; +import lombok.extern.slf4j.Slf4j; + +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 内存队列消费者 + * + * @param 消息 + */ +@Deprecated +@Slf4j +public class InMemoryQueueConsumer implements QueueConsumer { + private final InMemoryStorage storage = InMemoryStorage.getInstance(); + private volatile boolean stopped; + private volatile boolean subscribed; + private volatile Set partitions = new ConcurrentHashSet<>(); + + public InMemoryQueueConsumer(String topic) { + this.topic = topic; + stopped = false; + } + + private final String topic; + + @Override + public String getTopic() { + return topic; + } + + @Override + public void subscribe() { + subscribed = true; + } + + /** + * 修改订阅 + * + * @param partitions 订阅分区 + */ + @Override + public void subscribe(Set partitions) { + this.partitions = partitions; + } + + /** + * 新增订阅 + * + * @param partition 订阅分区 + */ + @Override + public void subscribe(TopicPartitionInfo partition) { + this.partitions.add(partition); + } + + @Override + public void unsubscribe() { + stopped = true; + } + + @Override + public List poll(long durationInMillis) { + if (subscribed) { + if(CollectionUtil.isNotEmpty(partitions)) { + List messages = partitions + .stream() + .map(tpi -> { + try { + return storage.get(tpi.getFullTopicName()); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Queue was interrupted.", e); + } + return Collections.emptyList(); + } + }) + .flatMap(List::stream) + .map(msg -> (T) msg).collect(Collectors.toList()); + if (messages.size() > 0) { + return messages; + } + } + try { + Thread.sleep(durationInMillis); + } catch (InterruptedException e) { + if (!stopped) { + log.error("Failed to sleep.", e); + } + } + } + return Collections.emptyList(); + } + + /** + * 队列总长度 + * + * @return size + */ + @Override + public long count() { + if (subscribed && CollectionUtil.isNotEmpty(partitions)) { + return partitions.stream().mapToLong(tpi -> storage.count(tpi.getFullTopicName())).sum(); + } + return 0; + } + + @Override + public void commit() { + } + + /** + * 是否停止 + * + * @return boolean + */ + @Override + public boolean isStopped() { + return stopped; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueProducer.java b/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueProducer.java new file mode 100644 index 0000000..c228edd --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memory/InMemoryQueueProducer.java @@ -0,0 +1,49 @@ +package com.thing.queue.memory; + +import com.thing.queue.QueueProducer; +import com.thing.queue.QueueCallback; +import com.thing.queue.QueueMsg; +import com.thing.queue.partition.TopicPartitionInfo; +import lombok.Data; + +/** + * 内存队列生产者 + * + * @param 消息 + */ +@Deprecated +@Data +public class InMemoryQueueProducer implements QueueProducer { + + private final InMemoryStorage storage = InMemoryStorage.getInstance(); + + private final String defaultTopic; + + public InMemoryQueueProducer(String defaultTopic) { + this.defaultTopic = defaultTopic; + } + + @Override + public void init() { + + } + + @Override + public void send(TopicPartitionInfo tpi, T msg, QueueCallback callback) { + boolean result = storage.put(tpi.getFullTopicName(), msg); + if (result) { + if (callback != null) { + callback.onSuccess(null); + } + } else { + if (callback != null) { + callback.onFailure(new RuntimeException("Failure add msg to InMemoryQueue")); + } + } + } + + @Override + public void stop() { + + } +} diff --git a/common/queue/src/main/java/com/thing/queue/memory/InMemoryStorage.java b/common/queue/src/main/java/com/thing/queue/memory/InMemoryStorage.java new file mode 100644 index 0000000..14fdea5 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memory/InMemoryStorage.java @@ -0,0 +1,71 @@ +package com.thing.queue.memory; + +import com.thing.queue.QueueMsg; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; + +@Deprecated +@Slf4j +public final class InMemoryStorage { + private static InMemoryStorage instance; + private final ConcurrentHashMap> storage; + + private InMemoryStorage() { + storage = new ConcurrentHashMap<>(); + } + + public static InMemoryStorage getInstance() { + if (instance == null) { + synchronized (InMemoryStorage.class) { + if (instance == null) { + instance = new InMemoryStorage(); + } + } + } + return instance; + } + + public boolean put(String topic, QueueMsg msg) { + return storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).add(msg); + } + + public List getKeys() { + Enumeration keys = storage.keys(); + List list = new ArrayList<>(); + while (keys.hasMoreElements()) { + list.add(keys.nextElement()); + } + return list; + } + + public List get(String topic) throws InterruptedException { + if (storage.containsKey(topic)) { + List entities; + T first = (T) storage.get(topic).poll(); + if (first != null) { + entities = new ArrayList<>(); + entities.add(first); + List otherList = new ArrayList<>(); + storage.get(topic).drainTo(otherList, 999); + for (QueueMsg other : otherList) { + entities.add((T) other); + } + } else { + entities = Collections.emptyList(); + } + return entities; + } + return Collections.emptyList(); + } + + public int count(String topic) { + return storage.containsKey(topic) ? storage.get(topic).size() : 0; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryQueueService.java b/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryQueueService.java new file mode 100644 index 0000000..2de82d5 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryQueueService.java @@ -0,0 +1,16 @@ +package com.thing.queue.memoryto; + + +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.util.List; + +public interface InMemoryQueueService { + + boolean send(String topic, DataProto msg); + + void send(String topic, List list); + + List poll(String topic, long durationInMillis); + +} diff --git a/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryStorage.java b/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryStorage.java new file mode 100644 index 0000000..4c17dee --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memoryto/InMemoryStorage.java @@ -0,0 +1,54 @@ +package com.thing.queue.memoryto; + +import com.thing.common.data.proto.QueueProto.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; + +@Slf4j +@Component +public final class InMemoryStorage { + + @Value("${queue.inMemory.maxSize:999}") + private int maxSize; + + private final ConcurrentHashMap> storage = new ConcurrentHashMap<>(); + + public void printStats() { + if (log.isDebugEnabled()) { + storage.forEach((topic, queue) -> { + if (!queue.isEmpty()) { + log.debug("[{}] 队列数据量 [{}]", topic, queue.size()); + } + }); + } + } + + public int getLagTotal() { + return storage.values().stream().map(BlockingQueue::size).reduce(0, Integer::sum); + } + + public boolean send(String topic, DataProto msg) { + return storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).add(msg); + } + + public void send(String topic, List list) { + storage.computeIfAbsent(topic, (t) -> new LinkedBlockingQueue<>()).addAll(list); + } + + public Collection get(String topic) { + final BlockingQueue queue = storage.get(topic); + if (!queue.isEmpty()) { + final List entities = new ArrayList<>(Math.min(queue.size(), maxSize) + 1); + queue.drainTo(entities, maxSize); + return new LinkedHashSet<>(entities).stream().toList(); + } + return Collections.emptyList(); + } + +} diff --git a/common/queue/src/main/java/com/thing/queue/memoryto/MemoryConsumerService.java b/common/queue/src/main/java/com/thing/queue/memoryto/MemoryConsumerService.java new file mode 100644 index 0000000..cad4322 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memoryto/MemoryConsumerService.java @@ -0,0 +1,67 @@ +package com.thing.queue.memoryto; + +import com.thing.common.data.event.QueueConsumerEvent; +import com.thing.common.data.proto.QueueProto.*; +import com.thing.common.util.AfterStartUp; +import com.thing.queue.util.Topics; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import javax.annotation.PreDestroy; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +@Slf4j +@Service +@ConditionalOnExpression("'${queue.type}'=='in-memory'") +public class MemoryConsumerService { + + @Value("${queue.type}") + private String type; + @Value("${queue.inMemory.pollInterval:10}") + private long pollDuration; + private final InMemoryQueueService mainConsumer; + private final ApplicationEventPublisher eventPublisher; + private ExecutorService executor = Executors.newSingleThreadExecutor(); + + @Autowired + public MemoryConsumerService(InMemoryQueueService inMemoryQueueService, ApplicationEventPublisher eventPublisher) { + this.mainConsumer = inMemoryQueueService; + this.eventPublisher = eventPublisher; + } + + @AfterStartUp(order = AfterStartUp.STARTUP_SERVICE) + protected void launchMainConsumers() { + if (type.equals("in-memory")) { + pollConsumer(); + } + } + + /** + * 定时定量轮询消费 + */ + @SneakyThrows + public void pollConsumer() { + Thread.sleep(1000); + executor.submit(() -> { + while (true) { + List list = mainConsumer.poll(Topics.V1_TSKV_HISTORY.getValue(), pollDuration); + if (!list.isEmpty()) { + eventPublisher.publishEvent(new QueueConsumerEvent(Topics.V1_TSKV_HISTORY.getValue(), list)); + } + } + }); + } + + @PreDestroy + public void destroy() { + executor.shutdown(); + } + +} diff --git a/common/queue/src/main/java/com/thing/queue/memoryto/QueueProducer.java b/common/queue/src/main/java/com/thing/queue/memoryto/QueueProducer.java new file mode 100644 index 0000000..a54fa3c --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memoryto/QueueProducer.java @@ -0,0 +1,37 @@ +package com.thing.queue.memoryto; + +import com.thing.common.data.proto.QueueProto.DataProto; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Slf4j +@Component +public class QueueProducer { + + @Value("${queue.type:in-memory}") + private String queueType; + + @Getter + private InMemoryQueueService inMemoryQueueService; + + public void send(String topic, List list) { + if (queueType.equals("in-memory")) { + inMemoryQueueService.send(topic, list); + } + } + + + @Autowired + @ConditionalOnProperty(name = "queue.type", havingValue = "in-memory") + public void setInMemoryQueueService(InMemoryQueueService inMemoryQueueService) { + this.inMemoryQueueService = inMemoryQueueService; + } + + +} diff --git a/common/queue/src/main/java/com/thing/queue/memoryto/impl/InMemoryQueueServiceImpl.java b/common/queue/src/main/java/com/thing/queue/memoryto/impl/InMemoryQueueServiceImpl.java new file mode 100644 index 0000000..ea80130 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/memoryto/impl/InMemoryQueueServiceImpl.java @@ -0,0 +1,51 @@ +package com.thing.queue.memoryto.impl; + +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.queue.memoryto.InMemoryQueueService; +import com.thing.queue.memoryto.InMemoryStorage; +import lombok.Data; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.Collections; +import java.util.List; + +@Slf4j +@Data +@Component +public class InMemoryQueueServiceImpl implements InMemoryQueueService { + + + private final InMemoryStorage storage; + + public InMemoryQueueServiceImpl(InMemoryStorage storage) { + this.storage = storage; + } + + @Override + public boolean send(String topic, DataProto msg) { + return storage.send(topic, msg); + } + + @Override + public void send(String topic, List list) { + storage.send(topic, list); + } + + @Override + @SneakyThrows + public List poll(String topic, long durationInMillis) { + List list = storage.get(topic).stream().toList(); + if (!list.isEmpty()) { + return list; + } + try { + Thread.sleep(durationInMillis); + } catch (InterruptedException e) { + log.error("-------Failed to sleep------->{}", e.getMessage()); + } + return Collections.emptyList(); + } + +} diff --git a/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsg.java b/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsg.java new file mode 100644 index 0000000..e197f66 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsg.java @@ -0,0 +1,18 @@ +package com.thing.queue.message; + +import com.thing.queue.QueueMsg; +import lombok.Data; + +@Data +public class DefaultQueueMsg implements QueueMsg { + private final byte[] data; + private final DefaultQueueMsgHeaders headers; + + public DefaultQueueMsg(QueueMsg msg) { + this.data = msg.getData(); + DefaultQueueMsgHeaders headers = new DefaultQueueMsgHeaders(); + msg.getHeaders().getData().forEach(headers::put); + this.headers = headers; + } + +} diff --git a/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsgHeaders.java b/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsgHeaders.java new file mode 100644 index 0000000..b9f910c --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/message/DefaultQueueMsgHeaders.java @@ -0,0 +1,27 @@ +package com.thing.queue.message; + + +import com.thing.queue.QueueMsgHeaders; + +import java.util.HashMap; +import java.util.Map; + +public class DefaultQueueMsgHeaders implements QueueMsgHeaders { + + protected final Map data = new HashMap<>(); + + @Override + public byte[] put(String key, byte[] value) { + return data.put(key, value); + } + + @Override + public byte[] get(String key) { + return data.get(key); + } + + @Override + public Map getData() { + return data; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/message/ProtoQueueMsg.java b/common/queue/src/main/java/com/thing/queue/message/ProtoQueueMsg.java new file mode 100644 index 0000000..d62385f --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/message/ProtoQueueMsg.java @@ -0,0 +1,34 @@ +package com.thing.queue.message; + +import com.thing.queue.QueueMsg; +import com.thing.queue.QueueMsgHeaders; +import lombok.Data; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Data +public class ProtoQueueMsg implements QueueMsg { + + protected final T value; + private final QueueMsgHeaders headers; + + public ProtoQueueMsg(T value) { + this(value, new DefaultQueueMsgHeaders()); + } + + public ProtoQueueMsg(T value, QueueMsgHeaders headers) { + this.value = value; + this.headers = headers; + } + + @Override + public QueueMsgHeaders getHeaders() { + return headers; + } + + @Override + public byte[] getData() { + return value.toByteArray(); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/message/ServiceQueue.java b/common/queue/src/main/java/com/thing/queue/message/ServiceQueue.java new file mode 100644 index 0000000..aaee1ee --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/message/ServiceQueue.java @@ -0,0 +1,50 @@ +package com.thing.queue.message; + +import lombok.ToString; + +import java.util.Objects; + +@ToString +public class ServiceQueue { + + public static final String MAIN = "Main"; + + private final ServiceType type; + private final String queue; + + public ServiceQueue(ServiceType type) { + this.type = type; + this.queue = MAIN; + } + + public ServiceQueue(ServiceType type, String queue) { + this.type = type; + this.queue = queue != null ? queue : MAIN; + } + + public ServiceType getType() { + return type; + } + + public String getQueue() { + return queue; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ServiceQueue that = (ServiceQueue) o; + return type == that.type && queue.equals(that.queue); + } + + @Override + public int hashCode() { + return Objects.hash(type, queue); + } + +} diff --git a/common/queue/src/main/java/com/thing/queue/message/ServiceType.java b/common/queue/src/main/java/com/thing/queue/message/ServiceType.java new file mode 100644 index 0000000..75f36db --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/message/ServiceType.java @@ -0,0 +1,13 @@ +package com.thing.queue.message; + +public enum ServiceType { + + /** + * 核心队列 + */ + QUEUE_CORE; + + public static ServiceType of(String serviceType) { + return ServiceType.valueOf(serviceType.replace("-", "_").toUpperCase()); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/HashPartitionServiceImpl.java b/common/queue/src/main/java/com/thing/queue/partition/HashPartitionServiceImpl.java new file mode 100644 index 0000000..96843c5 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/HashPartitionServiceImpl.java @@ -0,0 +1,83 @@ +package com.thing.queue.partition; + +import com.google.common.hash.HashFunction; +import com.google.common.hash.Hashing; +import com.thing.queue.message.ServiceQueue; +import com.thing.queue.message.ServiceType; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Service +public class HashPartitionServiceImpl implements PartitionService { + + @Value("${queue.core.topic}") + private String coreTopic; + @Value("${queue.core.partitions:100}") + private Integer corePartitions; + @Value("${queue.partitions.hash_function_name:murmur3_128}") + private String hashFunctionName; + + private HashFunction hashFunction; + private final ApplicationEventPublisher applicationEventPublisher; + private final ConcurrentMap partitionTopics = new ConcurrentHashMap<>(); + private final ConcurrentMap partitionSizes = new ConcurrentHashMap<>(); + + public HashPartitionServiceImpl(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + @PostConstruct + public void init() { + this.hashFunction = forName(hashFunctionName); + partitionSizes.put(new ServiceQueue(ServiceType.QUEUE_CORE), corePartitions); + partitionTopics.put(new ServiceQueue(ServiceType.QUEUE_CORE), coreTopic); + } + + @Override + public TopicPartitionInfo resolve(ServiceType serviceType, UUID sessionId) { + return resolve(new ServiceQueue(serviceType), sessionId); + } + + @Override + public TopicPartitionInfo resolve(ServiceType serviceType, String queueName, UUID sessionId) { + return resolve(new ServiceQueue(serviceType, queueName), sessionId); + } + + private TopicPartitionInfo resolve(ServiceQueue serviceQueue, UUID sessionId) { + int hash = hashFunction.newHasher() + .putLong(sessionId.getMostSignificantBits()) + .putLong(sessionId.getLeastSignificantBits()).hash().asInt(); + Integer partitionSize = partitionSizes.get(serviceQueue); + int partition; + if (partitionSize != null) { + partition = Math.abs(hash % partitionSize); + } else { + partition = 0; + } + TopicPartitionInfo topicPartitionInfo = buildTopicPartitionInfo(serviceQueue, partition); + applicationEventPublisher.publishEvent(new PartitionChangeEvent(this, serviceQueue, topicPartitionInfo)); + return topicPartitionInfo; + } + + private TopicPartitionInfo buildTopicPartitionInfo(ServiceQueue serviceQueue, int partition) { + return new TopicPartitionInfo(partitionTopics.get(serviceQueue), partition); + } + + public static HashFunction forName(String name) { + return switch (name) { + case "murmur3_32" -> Hashing.murmur3_32_fixed(); + case "murmur3_128" -> Hashing.murmur3_128(); + case "sha256" -> Hashing.sha256(); + default -> throw new IllegalArgumentException("Can't find hash function with name " + name); + }; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/PartitionChangeEvent.java b/common/queue/src/main/java/com/thing/queue/partition/PartitionChangeEvent.java new file mode 100644 index 0000000..e2cd78d --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/PartitionChangeEvent.java @@ -0,0 +1,29 @@ +package com.thing.queue.partition; + +import com.thing.queue.message.ServiceQueue; +import com.thing.queue.message.ServiceType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +/** + * @author zhenghh + */ +public class PartitionChangeEvent extends ApplicationEvent { + + private static final long serialVersionUID = 7402864379797047343L; + + @Getter + private final ServiceQueue serviceQueue; + @Getter + private final TopicPartitionInfo partition; + + public PartitionChangeEvent(Object source, ServiceQueue serviceQueue, TopicPartitionInfo partition) { + super(source); + this.serviceQueue = serviceQueue; + this.partition = partition; + } + + public ServiceType getServiceType() { + return serviceQueue.getType(); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/PartitionResetEvent.java b/common/queue/src/main/java/com/thing/queue/partition/PartitionResetEvent.java new file mode 100644 index 0000000..03e37a2 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/PartitionResetEvent.java @@ -0,0 +1,30 @@ +package com.thing.queue.partition; + +import com.thing.queue.message.ServiceQueue; +import com.thing.queue.message.ServiceType; +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +import java.util.Set; + +/** + * @author zhenghh + */ +public class PartitionResetEvent extends ApplicationEvent { + + private static final long serialVersionUID = 3805883313775943373L; + @Getter + private final ServiceQueue serviceQueue; + @Getter + private final Set partitions; + + public PartitionResetEvent(Object source, ServiceQueue serviceQueue, Set partitions) { + super(source); + this.serviceQueue = serviceQueue; + this.partitions = partitions; + } + + public ServiceType getServiceType() { + return serviceQueue.getType(); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/PartitionService.java b/common/queue/src/main/java/com/thing/queue/partition/PartitionService.java new file mode 100644 index 0000000..247f8cb --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/PartitionService.java @@ -0,0 +1,31 @@ +package com.thing.queue.partition; + +import com.thing.queue.message.ServiceType; + +import java.util.UUID; + +/** + * @author zhenghh. 2022-08-24 + **/ +public interface PartitionService { + + /** + * 转换分区 + * + * @param serviceType 服务类型 + * @param sessionId sessionId + * @return TopicPartitionInfo + */ + TopicPartitionInfo resolve(ServiceType serviceType, UUID sessionId); + + /** + * 转换分区 + * + * @param serviceType 服务类型 + * @param queueName 队列名称 + * @param sessionId sessionId + * @return TopicPartitionInfo + */ + TopicPartitionInfo resolve(ServiceType serviceType, String queueName, UUID sessionId); + +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfo.java b/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfo.java new file mode 100644 index 0000000..982c1dd --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfo.java @@ -0,0 +1,50 @@ +package com.thing.queue.partition; + +import lombok.Getter; +import lombok.ToString; + +import java.util.Objects; +import java.util.Optional; + +@ToString +public class TopicPartitionInfo { + + @Getter + private final String topic; + private final Integer partition; + @Getter + private final String fullTopicName; + + public TopicPartitionInfo(String topic, Integer partition) { + this.topic = topic; + this.partition = partition; + String tmp = topic; + if (partition != null) { + tmp += "." + partition; + } + this.fullTopicName = tmp; + } + + public Optional getPartition() { + return Optional.ofNullable(partition); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TopicPartitionInfo that = (TopicPartitionInfo) o; + return topic.equals(that.topic) && + Objects.equals(partition, that.partition) && + fullTopicName.equals(that.fullTopicName); + } + + @Override + public int hashCode() { + return Objects.hash(fullTopicName); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfoKey.java b/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfoKey.java new file mode 100644 index 0000000..3ea5f22 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/partition/TopicPartitionInfoKey.java @@ -0,0 +1,29 @@ +package com.thing.queue.partition; + +import com.thing.queue.message.ServiceQueue; +import lombok.AllArgsConstructor; + +import java.util.Objects; + +@AllArgsConstructor +public class TopicPartitionInfoKey { + private final ServiceQueue serviceQueue; + private final int partition; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TopicPartitionInfoKey that = (TopicPartitionInfoKey) o; + return partition == that.partition && serviceQueue.equals(that.serviceQueue); + } + + @Override + public int hashCode() { + return Objects.hash(serviceQueue, partition); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/provider/CoreQueueFactory.java b/common/queue/src/main/java/com/thing/queue/provider/CoreQueueFactory.java new file mode 100644 index 0000000..acf38e9 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/provider/CoreQueueFactory.java @@ -0,0 +1,16 @@ +package com.thing.queue.provider; + +import com.thing.queue.QueueConsumer; +import com.thing.queue.QueueProducer; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.gen.queue.QueueProto.TransportMsg; + +/** + * @author zhenghh. 2022-08-24 + **/ +public interface CoreQueueFactory { + + QueueProducer> createCoreMsgProducer(); + + QueueConsumer> createCoreMsgConsumer(); +} diff --git a/common/queue/src/main/java/com/thing/queue/provider/CoreQueueProducerProvider.java b/common/queue/src/main/java/com/thing/queue/provider/CoreQueueProducerProvider.java new file mode 100644 index 0000000..9bafb0d --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/provider/CoreQueueProducerProvider.java @@ -0,0 +1,30 @@ +package com.thing.queue.provider; + +import com.thing.queue.QueueProducer; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.gen.queue.QueueProto.TransportMsg; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Service +@RequiredArgsConstructor +public class CoreQueueProducerProvider implements QueueProducerProvider { + + private final CoreQueueFactory tbQueueProvider; + private QueueProducer> toCore; + + @PostConstruct + public void init() { + this.toCore = tbQueueProvider.createCoreMsgProducer(); + } + + @Override + public QueueProducer> getCoreMsgProducer() { + return toCore; + } +} diff --git a/common/queue/src/main/java/com/thing/queue/provider/InMemoryCoreQueueFactory.java b/common/queue/src/main/java/com/thing/queue/provider/InMemoryCoreQueueFactory.java new file mode 100644 index 0000000..05bb2f9 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/provider/InMemoryCoreQueueFactory.java @@ -0,0 +1,35 @@ +package com.thing.queue.provider; + +import com.thing.queue.QueueConsumer; +import com.thing.queue.QueueProducer; +import com.thing.queue.memory.InMemoryQueueConsumer; +import com.thing.queue.memory.InMemoryQueueProducer; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.queue.setting.CoreQueueSetting; +import com.thing.gen.queue.QueueProto.TransportMsg; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +@ConditionalOnExpression("'${queue.type}'=='in-memory'") +public class InMemoryCoreQueueFactory implements CoreQueueFactory { + + private final CoreQueueSetting coreSetting; + + @Override + public QueueProducer> createCoreMsgProducer() { + return new InMemoryQueueProducer<>(coreSetting.getTopic()); + } + + @Override + public QueueConsumer> createCoreMsgConsumer() { + return new InMemoryQueueConsumer<>(coreSetting.getTopic()); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/provider/QueueProducerProvider.java b/common/queue/src/main/java/com/thing/queue/provider/QueueProducerProvider.java new file mode 100644 index 0000000..20c92a0 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/provider/QueueProducerProvider.java @@ -0,0 +1,13 @@ +package com.thing.queue.provider; + +import com.thing.queue.QueueProducer; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.gen.queue.QueueProto.TransportMsg; + +/** + * @author zhenghh. 2022-08-24 + **/ +public interface QueueProducerProvider { + + QueueProducer> getCoreMsgProducer(); +} diff --git a/common/queue/src/main/java/com/thing/queue/provider/RabbitMqCoreQueueFactory.java b/common/queue/src/main/java/com/thing/queue/provider/RabbitMqCoreQueueFactory.java new file mode 100644 index 0000000..1ab4f11 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/provider/RabbitMqCoreQueueFactory.java @@ -0,0 +1,52 @@ +package com.thing.queue.provider; + +import com.thing.queue.QueueAdmin; +import com.thing.queue.QueueConsumer; +import com.thing.queue.QueueProducer; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.queue.rabbitmq.*; +import com.thing.queue.setting.CoreQueueSetting; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.queue.rabbitmq.*; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.annotation.PreDestroy; + +@Component +@ConditionalOnExpression("'${queue.type}'=='rabbitmq'") +public class RabbitMqCoreQueueFactory implements CoreQueueFactory { + + private final RabbitMqSettings rabbitMqSettings; + private final CoreQueueSetting coreSettings; + + private final QueueAdmin coreAdmin; + + public RabbitMqCoreQueueFactory(RabbitMqSettings rabbitMqSettings, + CoreQueueSetting coreSettings, + RabbitMqQueueArguments queueArguments) { + this.rabbitMqSettings = rabbitMqSettings; + this.coreSettings = coreSettings; + + this.coreAdmin = new RabbitMqAdmin(rabbitMqSettings, queueArguments.getCoreArgs()); + } + + @Override + public QueueProducer> createCoreMsgProducer() { + return new RabbitMqProducerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic()); + } + + @Override + public QueueConsumer> createCoreMsgConsumer() { + return new RabbitMqConsumerTemplate<>(coreAdmin, rabbitMqSettings, coreSettings.getTopic(), + msg -> new ProtoQueueMsg<>(TransportMsg.parseFrom(msg.getData()), msg.getHeaders())); + } + + + @PreDestroy + private void destroy() { + if (coreAdmin != null) { + coreAdmin.destroy(); + } + } +} diff --git a/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqAdmin.java b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqAdmin.java new file mode 100644 index 0000000..ba232fb --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqAdmin.java @@ -0,0 +1,72 @@ +package com.thing.queue.rabbitmq; + +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.thing.queue.QueueAdmin; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +@Slf4j +public class RabbitMqAdmin implements QueueAdmin { + + private final Channel channel; + private final Connection connection; + private final Map arguments; + + public RabbitMqAdmin(RabbitMqSettings rabbitMqSettings, Map arguments) { + this.arguments = arguments; + + try { + connection = rabbitMqSettings.getConnectionFactory().newConnection(); + } catch (IOException | TimeoutException e) { + log.error("Failed to create connection.", e); + throw new RuntimeException("Failed to create connection.", e); + } + + try { + channel = connection.createChannel(); + } catch (IOException e) { + log.error("Failed to create channel.", e); + throw new RuntimeException("Failed to create channel.", e); + } + } + + @Override + public void createTopicIfNotExists(String topic) { + try { + channel.queueDeclare(topic, false, false, false, arguments); + } catch (IOException e) { + log.error("Failed to bind queue: [{}]", topic, e); + } + } + + @Override + public void deleteTopic(String topic) { + try { + channel.queueDelete(topic); + } catch (IOException e) { + log.error("Failed to delete RabbitMq queue [{}].", topic); + } + } + + @Override + public void destroy() { + if (channel != null) { + try { + channel.close(); + } catch (IOException | TimeoutException e) { + log.error("Failed to close channel.", e); + } + } + if (connection != null) { + try { + connection.close(); + } catch (IOException e) { + log.error("Failed to close Connection.", e); + } + } + } +} diff --git a/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqConsumerTemplate.java b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqConsumerTemplate.java new file mode 100644 index 0000000..7f9a680 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqConsumerTemplate.java @@ -0,0 +1,121 @@ +package com.thing.queue.rabbitmq; + +import cn.hutool.core.collection.ConcurrentHashSet; +import com.google.gson.Gson; +import com.google.protobuf.InvalidProtocolBufferException; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.GetResponse; +import com.thing.queue.QueueAdmin; +import com.thing.queue.QueueMsg; +import com.thing.queue.QueueMsgDecoder; +import com.thing.queue.common.AbstractQueueConsumerTemplate; +import com.thing.queue.message.DefaultQueueMsg; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +@Slf4j +public class RabbitMqConsumerTemplate extends AbstractQueueConsumerTemplate { + + private final Gson gson = new Gson(); + private final QueueAdmin admin; + private final QueueMsgDecoder decoder; + private final Channel channel; + private final Connection connection; + + private final Set topicNames = new ConcurrentHashSet<>(); + private volatile Set queues; + + public RabbitMqConsumerTemplate(QueueAdmin admin, RabbitMqSettings rabbitMqSettings, String topic, QueueMsgDecoder decoder) { + super(topic); + this.admin = admin; + this.decoder = decoder; + try { + connection = rabbitMqSettings.getConnectionFactory().newConnection(); + } catch (IOException | TimeoutException e) { + log.error("Failed to create connection.", e); + throw new RuntimeException("Failed to create connection.", e); + } + try { + channel = connection.createChannel(); + } catch (IOException e) { + log.error("Failed to create channel.", e); + throw new RuntimeException("Failed to create channel.", e); + } + stopped = false; + } + + @Override + protected Integer doCount() { + return this.topicNames.stream() + .map(queue -> { + try { + return channel.queueDeclarePassive(queue).getMessageCount(); + } catch (IOException e) { + return 0; + } + }).reduce(Integer::sum).orElse(0); + } + + @Override + protected List doPoll(long durationInMillis) { + List result = queues.stream() + .map(queue -> { + try { + return channel.basicGet(queue, false); + } catch (IOException e) { + log.error("Failed to get messages from queue: [{}]", queue); + throw new RuntimeException("Failed to get messages from queue.", e); + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + if (result.size() > 0) { + return result; + } else { + return Collections.emptyList(); + } + } + + @Override + protected void doSubscribe(List topicNames) { + queues = new HashSet<>(topicNames); + this.topicNames.addAll(queues); + queues.forEach(admin::createTopicIfNotExists); + } + + @Override + protected void doCommit() { + try { + channel.basicAck(0, true); + } catch (IOException e) { + log.error("Failed to ack messages.", e); + } + } + + @Override + protected void doUnsubscribe() { + if (channel != null) { + try { + channel.close(); + } catch (IOException | TimeoutException e) { + log.error("Failed to close the channel."); + } + } + if (connection != null) { + try { + connection.close(); + } catch (IOException e) { + log.error("Failed to close the connection."); + } + } + } + + @Override + public T decode(GetResponse message) throws InvalidProtocolBufferException { + DefaultQueueMsg msg = gson.fromJson(new String(message.getBody()), DefaultQueueMsg.class); + return decoder.decode(msg); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqProducerTemplate.java b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqProducerTemplate.java new file mode 100644 index 0000000..b66d221 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqProducerTemplate.java @@ -0,0 +1,110 @@ +package com.thing.queue.rabbitmq; + +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.google.gson.Gson; +import com.rabbitmq.client.AMQP; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.thing.queue.QueueAdmin; +import com.thing.queue.QueueCallback; +import com.thing.queue.QueueMsg; +import com.thing.queue.QueueProducer; +import com.thing.queue.partition.TopicPartitionInfo; +import com.thing.queue.message.DefaultQueueMsg; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeoutException; + +@Slf4j +public class RabbitMqProducerTemplate implements QueueProducer { + private final String defaultTopic; + private final Gson gson = new Gson(); + private final QueueAdmin admin; + private final RabbitMqSettings rabbitMqSettings; + private final ListeningExecutorService producerExecutor; + private final Channel channel; + private final Connection connection; + + private final Set topics = ConcurrentHashMap.newKeySet(); + + public RabbitMqProducerTemplate(QueueAdmin admin, RabbitMqSettings rabbitMqSettings, String defaultTopic) { + this.admin = admin; + this.defaultTopic = defaultTopic; + this.rabbitMqSettings = rabbitMqSettings; + producerExecutor = MoreExecutors.listeningDecorator(Executors.newCachedThreadPool()); + try { + connection = rabbitMqSettings.getConnectionFactory().newConnection(); + } catch (IOException | TimeoutException e) { + log.error("Failed to create connection.", e); + throw new RuntimeException("Failed to create connection.", e); + } + + try { + channel = connection.createChannel(); + } catch (IOException e) { + log.error("Failed to create channel.", e); + throw new RuntimeException("Failed to create channel.", e); + } + } + + @Override + public void init() { + + } + + @Override + public String getDefaultTopic() { + return defaultTopic; + } + + @Override + public void send(TopicPartitionInfo tpi, T msg, QueueCallback callback) { + createTopicIfNotExist(tpi); + AMQP.BasicProperties properties = new AMQP.BasicProperties(); + try { + channel.basicPublish(rabbitMqSettings.getExchangeName(), tpi.getFullTopicName(), properties, gson.toJson(new DefaultQueueMsg(msg)).getBytes()); + if (callback != null) { + callback.onSuccess(null); + } + } catch (IOException e) { + log.error("Failed publish message: [{}].", msg, e); + if (callback != null) { + callback.onFailure(e); + } + } + } + + @Override + public void stop() { + if (producerExecutor != null) { + producerExecutor.shutdownNow(); + } + if (channel != null) { + try { + channel.close(); + } catch (IOException | TimeoutException e) { + log.error("Failed to close the channel."); + } + } + if (connection != null) { + try { + connection.close(); + } catch (IOException e) { + log.error("Failed to close the connection."); + } + } + } + + private void createTopicIfNotExist(TopicPartitionInfo tpi) { + if (topics.contains(tpi)) { + return; + } + admin.createTopicIfNotExists(tpi.getFullTopicName()); + topics.add(tpi); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqQueueArguments.java b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqQueueArguments.java new file mode 100644 index 0000000..564e05e --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqQueueArguments.java @@ -0,0 +1,67 @@ +package com.thing.queue.rabbitmq; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; + +@Component +@ConditionalOnExpression("'${queue.type}'=='rabbitmq'") +public class RabbitMqQueueArguments { + + @Value("${queue.rabbitmq.queue-properties.core:}") + private String coreProperties; + + @Getter + private Map coreArgs; + + @PostConstruct + private void init() { + coreArgs = getArgs(coreProperties); + } + + private Map getArgs(String properties) { + Map configs = new HashMap<>(); + if (StringUtils.isNotEmpty(properties)) { + for (String property : properties.split(";")) { + int delimiterPosition = property.indexOf(":"); + String key = property.substring(0, delimiterPosition); + String strValue = property.substring(delimiterPosition + 1); + configs.put(key, getObjectValue(strValue)); + } + } + return configs; + } + + private Object getObjectValue(String str) { + if ("true".equalsIgnoreCase(str) || "false".equalsIgnoreCase(str)) { + return Boolean.valueOf(str); + } else if (isNumeric(str)) { + return getNumericValue(str); + } + return str; + } + + private Object getNumericValue(String str) { + if (str.contains(".")) { + return Double.valueOf(str); + } else { + return Long.valueOf(str); + } + } + + private static final Pattern PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?"); + + public boolean isNumeric(String strNum) { + if (strNum == null) { + return false; + } + return PATTERN.matcher(strNum).matches(); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqSettings.java b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqSettings.java new file mode 100644 index 0000000..71fc293 --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/rabbitmq/RabbitMqSettings.java @@ -0,0 +1,50 @@ +package com.thing.queue.rabbitmq; + +import com.rabbitmq.client.ConnectionFactory; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; + +@Slf4j +@ConditionalOnExpression("'${queue.type}'=='rabbitmq'") +@Component +@Data +public class RabbitMqSettings { + @Value("${queue.rabbitmq.exchange_name}") + private String exchangeName; + @Value("${queue.rabbitmq.host}") + private String host; + @Value("${queue.rabbitmq.port}") + private int port; + @Value("${queue.rabbitmq.virtual_host}") + private String virtualHost; + @Value("${queue.rabbitmq.username}") + private String username; + @Value("${queue.rabbitmq.password}") + private String password; + @Value("${queue.rabbitmq.automatic_recovery_enabled}") + private boolean automaticRecoveryEnabled; + @Value("${queue.rabbitmq.connection_timeout}") + private int connectionTimeout; + @Value("${queue.rabbitmq.handshake_timeout}") + private int handshakeTimeout; + + private ConnectionFactory connectionFactory; + + @PostConstruct + private void init() { + connectionFactory = new ConnectionFactory(); + connectionFactory.setHost(host); + connectionFactory.setPort(port); + connectionFactory.setVirtualHost(virtualHost); + connectionFactory.setUsername(username); + connectionFactory.setPassword(password); + connectionFactory.setAutomaticRecoveryEnabled(automaticRecoveryEnabled); + connectionFactory.setConnectionTimeout(connectionTimeout); + connectionFactory.setHandshakeTimeout(handshakeTimeout); + } +} diff --git a/common/queue/src/main/java/com/thing/queue/setting/CoreQueueSetting.java b/common/queue/src/main/java/com/thing/queue/setting/CoreQueueSetting.java new file mode 100644 index 0000000..6fb289d --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/setting/CoreQueueSetting.java @@ -0,0 +1,19 @@ +package com.thing.queue.setting; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Data +@Component +public class CoreQueueSetting { + + @Value("${queue.core.topic}") + private String topic; + + @Value("${queue.core.partitions}") + private int partitions; +} diff --git a/common/queue/src/main/java/com/thing/queue/util/Topics.java b/common/queue/src/main/java/com/thing/queue/util/Topics.java new file mode 100644 index 0000000..f99362d --- /dev/null +++ b/common/queue/src/main/java/com/thing/queue/util/Topics.java @@ -0,0 +1,33 @@ +package com.thing.queue.util; + +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +@Getter +public enum Topics { + + V1_TSKV("v1/tskv"), + V1_TSKV_LATEST("v1/tskv/latest"), + V1_TSKV_HISTORY("v1/tskv/history"), + + /** 物计算日志存储事件 */ + V1_TSKV_CALC_LOG_SAVE("v1/tskv/calc/log/save"), + + /** 过滤日志存储事件 */ + V1_TSKV_FILTER_LOG_SAVE("v1/tskv/filter/log/save"); + + private final String value; + + Topics(String value) { + this.value = value; + } + + public static Topics match(String val) { + return Arrays.stream(Topics.values()) + .filter(item -> Objects.equals(item.value, val)) + .findFirst() + .orElse(null); + } +} diff --git a/common/queue/src/main/proto/queue.proto b/common/queue/src/main/proto/queue.proto new file mode 100644 index 0000000..3ad1a2e --- /dev/null +++ b/common/queue/src/main/proto/queue.proto @@ -0,0 +1,64 @@ +syntax = "proto3"; +package queue; + +option java_package = "com.thing.gen.queue"; +option java_outer_classname = "QueueProto"; + +message TransportMsg { + TelemetryMsg telemetryMsg = 1; + ExtensionMsg transportExtensionMsg = 2; + ControlMsg controlMsg = 3; + string origin = 4; + int64 tenantCode = 5; + int64 companyId = 6; + int64 deptId = 7; +} + +message ControlMsg { + bytes inMsg = 1; + bytes outMsg = 2; + bytes errorMsg = 3; + int64 ts = 4; + int64 configId = 5; + int64 relationId = 6; + bool debug = 7; +} + +message ExtensionMsg { + bytes payload = 1; + int64 ts = 2; + int64 configId = 3; + repeated ExtensionRelation relation = 4; +} + +message ExtensionRelation { + int64 calculationId = 1; + bool debug = 2; + int64 relationId = 3; +} + +message TelemetryMsg { + repeated TelemetryProto telemetryList = 1; +} + +message TelemetryProto { + string thingCode = 1; + repeated TsKvProto tsKvList = 2; +} + +message TsKvProto { + int64 ts = 1; + string key = 2; + string val = 3; +} + +message SessionInfoProto { + int64 sessionIdMSB = 1; + int64 sessionIdLSB = 2; + string thingCode = 3; + string gateway = 4; + string clientId = 5; + string userName = 6; + string clientIp = 7; + int64 startTime = 8; +} \ No newline at end of file diff --git a/common/script/pom.xml b/common/script/pom.xml new file mode 100644 index 0000000..f0da15d --- /dev/null +++ b/common/script/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + com.thing + common + 5.1 + ../pom.xml + + + script + ThingBI Server Common script + jar + com.thing.common + + ${basedir}/../.. + + + + + com.thing.common + cache + + + com.thing.common + util + + + com.thing.common + core + + + org.javadelight + delight-nashorn-sandbox + + + com.googlecode.aviator + aviator + + + org.thingsboard + tbel + + + cn.hutool + hutool-all + + + com.thing.common + orm + + + com.thing.common + tskv + + + + \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/CustomScriptException.java b/common/script/src/main/java/com/thing/CustomScriptException.java new file mode 100644 index 0000000..997040a --- /dev/null +++ b/common/script/src/main/java/com/thing/CustomScriptException.java @@ -0,0 +1,23 @@ +package com.thing; + +import lombok.Getter; + +public class CustomScriptException extends RuntimeException { + private static final long serialVersionUID = -1958193538782818284L; + + public enum ErrorCode {COMPILATION, TIMEOUT, RUNTIME, OTHER} + + @Getter + private final String scriptId; + @Getter + private final ErrorCode errorCode; + @Getter + private final String body; + + public CustomScriptException(String scriptId, ErrorCode errorCode, String body, Exception cause) { + super(cause); + this.scriptId = scriptId; + this.errorCode = errorCode; + this.body = body; + } +} diff --git a/common/script/src/main/java/com/thing/ScriptCreateService.java b/common/script/src/main/java/com/thing/ScriptCreateService.java new file mode 100644 index 0000000..8a3a4bf --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptCreateService.java @@ -0,0 +1,197 @@ +package com.thing; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.api.ScriptEngine; +import com.thing.modules.entity.ScriptInfoEntity; + +/** + * @author zhenghh. 2022-12-27 + **/ +public interface ScriptCreateService { + + /** + * 创建ScriptEngine + * 直接执行script使用 + * + * @param scriptId 数据解析主键 + * @param scriptLanguage 解析语言 + * @param script 消息体 + * @param debug 是否调试 + * @param argNames 入参名称 + * @return ScriptEngine + */ + ScriptEngine createScriptEngine(Long scriptId, ScriptLanguage scriptLanguage, String script, Boolean debug, String... argNames); + + /** + * 创建ScriptEngine + * 直接执行script使用 + * 默认入参为 msg, metadata + * + * @param scriptId 数据解析主键 + * @param scriptLanguage 解析语言 + * @param script 消息体 + * @param debug 是否调试 + * @return ScriptEngine + */ + ScriptEngine createScriptEngine(Long scriptId, ScriptLanguage scriptLanguage, String script, Boolean debug); + + /** + * 过滤 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return Boolean + */ + ListenableFuture executeFilterAsync(Long scriptId, String msg); + + /** + * 过滤 + * + * @param entity 数据解析 + * @param msg 消息 + * @return Boolean + */ + ListenableFuture executeFilterAsync(ScriptInfoEntity entity, String msg); + + /** + * 过滤 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return Boolean + */ + ListenableFuture executeFilterAsync(Long scriptId, String msg, String... argNames); + + /** + * 过滤 + * + * @param entity 数据解析 + * @param msg 消息 + * @param argNames 入参名称 + * @return Boolean + */ + ListenableFuture executeFilterAsync(ScriptInfoEntity entity, String msg, String... argNames); + + + /** + * json转换 + * + * @param scriptId 计算主键 + * @param language 解析语言 + * @param scriptBody 方法体 + * @param debug 是否调试 + * @param msg 消息 + * @param sup 辅助参数 + * @param query 查询参数 + * @param destroy 是否销毁 + * @param argNames 属性名称 + * @return JsonNode + */ + JsonNode executeJson(Long scriptId, ScriptLanguage language, String scriptBody, Boolean debug, String msg, + JsonNode sup, JsonNode query, Boolean destroy, String... argNames); + + /** + * json转换 + * + * @param scriptId 计算主键 + * @param language 解析语言 + * @param scriptBody 方法体 + * @param debug 是否调试 + * @param msg 消息 + * @param sup 辅助参数 + * @param query 查询参数 + * @param destroy 是否销毁 + * @return JsonNode + */ + JsonNode executeJson(Long scriptId, ScriptLanguage language, String scriptBody, Boolean debug, String msg, + JsonNode sup, JsonNode query, Boolean destroy); + + /** + * 转换json + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return JsonNode + */ + ListenableFuture executeJsonAsync(Long scriptId, String msg); + + /** + * 转换json + * + * @param entity 数据解析 + * @param msg 消息 + * @return JsonNode + */ + ListenableFuture executeJsonAsync(ScriptInfoEntity entity, String msg); + + /** + * 转换json + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return JsonNode + */ + ListenableFuture executeJsonAsync(Long scriptId, String msg, String... argNames); + + /** + * 转换json + * + * @param entity 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return JsonNode + */ + ListenableFuture executeJsonAsync(ScriptInfoEntity entity, String msg, String... argNames); + + /** + * 转换字符串 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return String + */ + ListenableFuture executeToStringAsync(Long scriptId, String msg); + + /** + * 转换字符串 + * + * @param entity 数据解析 + * @param msg 消息 + * @return String + */ + ListenableFuture executeToStringAsync(ScriptInfoEntity entity, String msg); + + /** + * 转换字符串 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return String + */ + ListenableFuture executeToStringAsync(Long scriptId, String msg, String... argNames); + + /** + * 转换字符串 + * + * @param entity 数据解析 + * @param msg 消息 + * @param argNames 入参名称 + * @return String + */ + ListenableFuture executeToStringAsync(ScriptInfoEntity entity, String msg, String... argNames); + + /** + * 前置判断 + * + * @param inMsg 入参 + * @param sup 辅助参数 + * @param query 查询参数 + * @return map + */ + ScriptMsg convertScriptMsg(String inMsg, JsonNode sup, JsonNode query); + +} diff --git a/common/script/src/main/java/com/thing/ScriptCreateServiceImpl.java b/common/script/src/main/java/com/thing/ScriptCreateServiceImpl.java new file mode 100644 index 0000000..382f475 --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptCreateServiceImpl.java @@ -0,0 +1,381 @@ +package com.thing; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.api.ScriptEngine; +import com.thing.api.aviator.AviatorScriptEngine; +import com.thing.api.aviator.DefaultAviatorInvokeService; +import com.thing.api.mvel.DefaultTbelInvokeService; +import com.thing.api.mvel.TbelScriptEngine; +import com.thing.api.nashorn.DefaultNashornInvokeService; +import com.thing.api.nashorn.NashornScriptEngine; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.modules.dto.QueryMsg; +import com.thing.modules.entity.ScriptInfoEntity; +import com.thing.modules.mapper.ScriptInfoMapper; +import com.thing.modules.service.ScriptLogService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-12-27 + **/ +@Service +public class ScriptCreateServiceImpl implements ScriptCreateService { + + @Autowired + private DefaultNashornInvokeService nashornInvokeService; + @Autowired + private DefaultTbelInvokeService tbelInvokeService; + @Autowired + private DefaultAviatorInvokeService aviatorInvokeService; + @Autowired + private ScriptLogService scriptLogService; + @Lazy + @Autowired + private ScriptInfoMapper scriptInfoMapper; + @Resource + private TsKvService tsKvService; + + + /** + * 创建ScriptEngine 调试用 + * + * @param scriptId 数据解析主键 + * @param scriptLanguage 解析语言 + * @param script 消息体 + * @param debug 是否调试 + * @param argNames 入参 + * @return ScriptEngine + */ + @Override + public ScriptEngine createScriptEngine(Long scriptId, ScriptLanguage scriptLanguage, String script, Boolean debug, String... argNames) { + switch (scriptLanguage) { + case MVEL: + return new TbelScriptEngine(String.valueOf(scriptId), tbelInvokeService, scriptLogService, debug, script, argNames); + case NASHORN: + return new NashornScriptEngine(String.valueOf(scriptId), nashornInvokeService, scriptLogService, debug, script, argNames); + case AVIATOR: + return new AviatorScriptEngine(String.valueOf(scriptId), aviatorInvokeService, scriptLogService, debug, script, argNames); + default: + throw new UnsupportedOperationException("unsupported type: " + scriptLanguage.name()); + } + } + + /** + * 创建ScriptEngine + * 直接执行script使用 + * 默认入参为 msg, metadata + * + * @param scriptId 数据解析主键 + * @param scriptLanguage 解析语言 + * @param script 消息体 + * @param debug 是否调试 + * @return ScriptEngine + */ + @Override + public ScriptEngine createScriptEngine(Long scriptId, ScriptLanguage scriptLanguage, String script, Boolean debug) { + return createScriptEngine(scriptId, scriptLanguage, script, debug, "msg", "metadata"); + } + + /** + * 过滤 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return Boolean + */ + @Override + public ListenableFuture executeFilterAsync(Long scriptId, String msg) { + return executeFilterAsync(scriptId, msg, "msg", "metadata"); + } + + /** + * 过滤 + * + * @param entity 数据解析 + * @param msg 消息 + * @return Boolean + */ + @Override + public ListenableFuture executeFilterAsync(ScriptInfoEntity entity, String msg) { + return executeFilterAsync(entity, msg, "msg", "metadata"); + } + + /** + * 过滤 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return Boolean + */ + @Override + public ListenableFuture executeFilterAsync(Long scriptId, String msg, String... argNames) { + return executeFilterAsync(getScriptInfo(scriptId), msg, argNames); + } + + /** + * 过滤 + * + * @param entity 数据解析 + * @param msg 消息 + * @param argNames 入参名称 + * @return Boolean + */ + @Override + public ListenableFuture executeFilterAsync(ScriptInfoEntity entity, String msg, String... argNames) { + ScriptMsg scriptMsg = convertScriptMsg(msg, JacksonUtil.toJsonNode(entity.getSupMsg()), JacksonUtil.toJsonNode(entity.getQueryMsg())); + ScriptEngine scriptEngine = createScriptEngine(entity.getId(), ScriptLanguage.valueOf(entity.getScriptType()), entity.getScriptBody(), entity.getDebug(), argNames); + return scriptEngine.executeFilterAsync(scriptMsg); + } + + /** + * json转换 + * + * @param scriptId 计算主键 + * @param language 解析语言 + * @param scriptBody 方法体 + * @param debug 是否调试 + * @param msg 消息 + * @param sup 辅助参数 + * @param query 查询参数 + * @param destroy 是否销毁 + * @param argNames 属性名称 + * @return JsonNode + */ + @Override + public JsonNode executeJson(Long scriptId, ScriptLanguage language, String scriptBody, Boolean debug, String msg, + JsonNode sup, JsonNode query, Boolean destroy, String... argNames) { + if (StringUtils.isBlank(scriptBody)) { + return JacksonUtil.toJsonNode(StringUtils.defaultIfBlank(msg, "{}")); + } + ScriptEngine scriptEngine = null; + try { + ScriptMsg scriptMsg = convertScriptMsg(msg, sup, query); + scriptEngine = createScriptEngine(scriptId, language, scriptBody, debug, argNames); + return scriptEngine.executeJsonAsync(scriptMsg).get(); + } catch (Exception e) { + if (scriptEngine != null) { + scriptEngine.destroy(); + } + throw new SysException("解析失败: " + e.getMessage()); + } finally { + if (destroy && scriptEngine != null) { + scriptEngine.destroy(); + } + } + } + + /** + * json转换 + * + * @param scriptId 计算主键 + * @param language 解析语言 + * @param scriptBody 方法体 + * @param debug 是否调试 + * @param msg 消息 + * @param sup 辅助参数 + * @param query 查询参数 + * @param destroy 是否销毁 + * @return JsonNode + */ + @Override + public JsonNode executeJson(Long scriptId, ScriptLanguage language, String scriptBody, Boolean debug, + String msg, JsonNode sup, JsonNode query, Boolean destroy) { + return executeJson(scriptId, language, scriptBody, debug, msg, sup, query, destroy, "msg", "metadata"); + } + + /** + * 转换json + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(Long scriptId, String msg) { + return executeJsonAsync(scriptId, msg, "msg", "metadata"); + } + + /** + * 转换json + * + * @param entity 数据解析 + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(ScriptInfoEntity entity, String msg) { + return executeJsonAsync(entity, msg, "msg", "metadata"); + } + + /** + * 转换json + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(Long scriptId, String msg, String... argNames) { + return executeJsonAsync(getScriptInfo(scriptId), msg, argNames); + } + + /** + * 转换json + * + * @param entity 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(ScriptInfoEntity entity, String msg, String... argNames) { + ScriptMsg scriptMsg = convertScriptMsg(msg, JacksonUtil.toJsonNode(entity.getSupMsg()), JacksonUtil.toJsonNode(entity.getQueryMsg())); + ScriptEngine scriptEngine = createScriptEngine(entity.getId(), ScriptLanguage.valueOf(entity.getScriptType()), entity.getScriptBody(), entity.getDebug(), argNames); + return scriptEngine.executeJsonAsync(scriptMsg); + } + + /** + * 转换字符串 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @return String + */ + @Override + public ListenableFuture executeToStringAsync(Long scriptId, String msg) { + return executeToStringAsync(scriptId, msg, "msg", "metadata"); + } + + /** + * 转换字符串 + * + * @param entity 数据解析 + * @param msg 消息 + * @return String + */ + @Override + public ListenableFuture executeToStringAsync(ScriptInfoEntity entity, String msg) { + return executeToStringAsync(entity, msg, "msg", "metadata"); + } + + /** + * 转换字符串 + * + * @param scriptId 数据解析主键 + * @param msg 消息 + * @param argNames 入参名称 + * @return String + */ + @Override + public ListenableFuture executeToStringAsync(Long scriptId, String msg, String... argNames) { + return executeToStringAsync(getScriptInfo(scriptId), msg, argNames); + } + + /** + * 转换字符串 + * + * @param entity 数据解析 + * @param msg 消息 + * @param argNames 入参名称 + * @return String + */ + @Override + public ListenableFuture executeToStringAsync(ScriptInfoEntity entity, String msg, String... argNames) { + ScriptMsg scriptMsg = convertScriptMsg(msg, JacksonUtil.toJsonNode(entity.getSupMsg()), JacksonUtil.toJsonNode(entity.getQueryMsg())); + ScriptEngine scriptEngine = createScriptEngine(entity.getId(), ScriptLanguage.valueOf(entity.getScriptType()), entity.getScriptBody(), entity.getDebug(), argNames); + return scriptEngine.executeToStringAsync(scriptMsg); + } + + /** + * 前置判断 + * + * @param inMsg 入参 + * @param sup 辅助参数 + * @param query 查询参数 + * @return map + */ + @Override + public ScriptMsg convertScriptMsg(String inMsg, JsonNode sup, JsonNode query) { + String msg = StringUtils.defaultIfBlank(inMsg, "{}"); + Map metaData = Maps.newHashMap(); + JsonNode supMsg = sup == null ? JacksonUtil.newObjectNode() : sup; + metaData.put("sup", supMsg); + List queryList = query == null || query.isEmpty() ? Lists.newArrayList() : JacksonUtil.convertValue(query, new TypeReference<>() { + }); + if (CollectionUtil.isEmpty(queryList)) { + metaData.put("query", JacksonUtil.newObjectNode()); + return new ScriptMsg(msg, metaData); + } + queryList.forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + if (queryList.stream().anyMatch(item -> !item.isLast()) && !supMsg.has("ts")) { + throw new SysException("勾选同时间后,辅助参数必须包含ts"); + } + List list = Lists.newArrayList(); + //最新值 + Map> lastMap = queryList.stream() + .filter(QueryMsg::isLast) + .collect(Collectors.groupingBy(QueryMsg::getCode, Collectors.mapping(QueryMsg::getAttr, Collectors.toCollection(ArrayList::new)))); + + if (CollectionUtil.isNotEmpty(lastMap)) { + list.addAll(tsKvService.findLatestByMultiMap(lastMap, Boolean.FALSE)); + } + //区间值 + Map> intervalMap = queryList.stream().filter(item -> !item.isLast()) + .collect(Collectors.groupingBy(QueryMsg::getCode, Collectors.mapping(QueryMsg::getAttr, Collectors.toCollection(ArrayList::new)))); + if (CollectionUtil.isNotEmpty(intervalMap)) { + long ts = supMsg.get("ts").asLong(); + list.addAll(tsKvService.findLatestByMultiMap(intervalMap,ts - 1, ts + 1, Boolean.FALSE)); + } + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + for (QueryMsg queryMsg : queryList) { + Optional first = list.parallelStream() + .filter(item -> StringUtils.equals(item.getThingCode(), queryMsg.getCode()) && StringUtils.equals(item.getAttrKey(), queryMsg.getAttr())) + .findFirst(); + if (first.isPresent()) { + jsonNodes.put(queryMsg.getLabel(), first.get().getVal()); + } else if (StringUtils.isNotBlank(queryMsg.getDefVal())) { + jsonNodes.put(queryMsg.getLabel(), queryMsg.getDefVal()); + } + } + if (jsonNodes.isEmpty() || jsonNodes.size() != queryList.size()) { + throw new SysException("未查询到数据或查询数据缺失"); + } + metaData.put("query", jsonNodes); + return new ScriptMsg(msg, metaData); + } + + /** + * 数据解析 + * + * @param scriptId 数据解析主键 + * @return ScriptInfoEntity + */ + private ScriptInfoEntity getScriptInfo(Long scriptId) { + //todo 效率太低的话,调整一下缓存,从缓存获取 + ScriptInfoEntity entity = scriptInfoMapper.selectOneById(scriptId); + if (Objects.isNull(entity)) { + throw new SysException("未找到数据解析配置"); + } + return entity; + } +} diff --git a/common/script/src/main/java/com/thing/ScriptExecutionTask.java b/common/script/src/main/java/com/thing/ScriptExecutionTask.java new file mode 100644 index 0000000..13f6d4d --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptExecutionTask.java @@ -0,0 +1,22 @@ +package com.thing; + +import com.google.common.util.concurrent.ListenableFuture; +import lombok.Getter; + +/** + * @author zhenghh. 2022-12-21 + **/ +public abstract class ScriptExecutionTask { + + @Getter + private final ListenableFuture resultFuture; + + protected ScriptExecutionTask(ListenableFuture resultFuture) { + this.resultFuture = resultFuture; + } + + /** + * 停止 + */ + public abstract void stop(); +} diff --git a/common/script/src/main/java/com/thing/ScriptLanguage.java b/common/script/src/main/java/com/thing/ScriptLanguage.java new file mode 100644 index 0000000..0261edc --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptLanguage.java @@ -0,0 +1,52 @@ +package com.thing; + + +import com.thing.common.core.exception.SysException; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.Arrays; + +/** + * 编译语言 + * + * @author zhenghh. 2022-12-20 + **/ +public enum ScriptLanguage implements Serializable { + /** + * 编译语言 + */ + MVEL("MVEL"), + NASHORN("Java Script"), + AVIATOR("Aviator Script"); + + @Getter + private final String alias; + + ScriptLanguage(String alias) { + this.alias = alias; + } + + /** + * 是否包含 + * + * @param language 语言 + * @return boolean + */ + public static Boolean contains(String language) { + return Arrays.stream(ScriptLanguage.values()).anyMatch(item -> StringUtils.equals(item.name(), language)); + } + + /** + * 获取语言枚举 + * + * @param language 语言 + * @return ScriptLanguage + */ + public static ScriptLanguage get(String language) { + return Arrays.stream(ScriptLanguage.values()) + .filter(item -> StringUtils.equals(item.name(), language)) + .findFirst().orElseThrow(() -> new SysException("unsupported script language: " + language)); + } +} diff --git a/common/script/src/main/java/com/thing/ScriptLog.java b/common/script/src/main/java/com/thing/ScriptLog.java new file mode 100644 index 0000000..c43370e --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptLog.java @@ -0,0 +1,35 @@ +package com.thing; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2022-12-21 + **/ +@Data +@NoArgsConstructor +public class ScriptLog { + + @JsonIgnore + private String scriptId; + + private JsonNode inMsg; + + private JsonNode outMsg; + + private String errMsg; + + public ScriptLog(String scriptId, JsonNode inMsg, JsonNode outMsg) { + this.scriptId = scriptId; + this.inMsg = inMsg; + this.outMsg = outMsg; + } + + public ScriptLog(String scriptId, String errMsg) { + this.scriptId = scriptId; + this.errMsg = errMsg; + } + +} diff --git a/common/script/src/main/java/com/thing/ScriptMsg.java b/common/script/src/main/java/com/thing/ScriptMsg.java new file mode 100644 index 0000000..1a35d54 --- /dev/null +++ b/common/script/src/main/java/com/thing/ScriptMsg.java @@ -0,0 +1,42 @@ +package com.thing; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Maps; +import com.thing.common.core.utils.JacksonUtil; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author zhenghh. 2022-12-21 + **/ +@Data +@NoArgsConstructor +public class ScriptMsg implements Serializable { + + private static final long serialVersionUID = -7817236064385848184L; + + private String msg; + private Map metaData; + + public ScriptMsg(String msg, Map metaData) { + this.msg = StringUtils.isBlank(msg) ? "{}" : msg; + this.metaData = metaData; + } + + public ScriptMsg(String msg, JsonNode metadataJson) { + this.msg = StringUtils.isBlank(msg) ? "{}" : msg; + this.metaData = JacksonUtil.convertValue(metadataJson, new TypeReference<>() {}); + } + + public JsonNode getMsgStr() { + Map map = Maps.newHashMapWithExpectedSize(2); + map.put("msg", JacksonUtil.toJsonNode(this.msg)); + map.put("metaData", JacksonUtil.convertValue(this.metaData, JsonNode.class)); + return JacksonUtil.convertValue(map, JsonNode.class); + } +} diff --git a/common/script/src/main/java/com/thing/api/AbstractScriptEngine.java b/common/script/src/main/java/com/thing/api/AbstractScriptEngine.java new file mode 100644 index 0000000..6dbb65c --- /dev/null +++ b/common/script/src/main/java/com/thing/api/AbstractScriptEngine.java @@ -0,0 +1,151 @@ +package com.thing.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import com.thing.ScriptLog; +import com.thing.ScriptMsg; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; + +import javax.script.ScriptException; +import java.util.concurrent.ExecutionException; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Slf4j +public abstract class AbstractScriptEngine implements ScriptEngine { + + private final T scriptInvokeService; + + private final String scriptId; + private final Boolean debug; + private final ScriptLogService scriptLogService; + + public AbstractScriptEngine(String scriptId, T scriptInvokeService, ScriptLogService scriptLogService, + Boolean debug, String script, String... argNames) { + this.scriptInvokeService = scriptInvokeService; + this.scriptId = scriptId; + this.debug = BooleanUtils.toBoolean(debug); + this.scriptLogService = scriptLogService; + eval(scriptId, script, argNames); + } + + private void eval(String scriptId, String script, String... argNames) { + try { + this.scriptInvokeService.eval(scriptId, script, argNames).get(); + } catch (Exception e) { + Throwable t = e; + if (e instanceof ExecutionException) { + t = e.getCause(); + } + scriptLogService.saveLog(new ScriptLog(scriptId, "Can't compile script: " + t.getMessage())); + throw new IllegalArgumentException("Can't compile script: " + t.getMessage(), t); + } + } + + /** + * 过滤 + * + * @param msg 消息 + * @return Boolean + */ + @Override + public ListenableFuture executeFilterAsync(ScriptMsg msg) { + return Futures.transformAsync(executeScriptAsync(msg), this::executeFilterTransform, MoreExecutors.directExecutor()); + } + + /** + * 转换字符串 + * + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeToStringAsync(ScriptMsg msg) { + return Futures.transformAsync(executeScriptAsync(msg), this::executeToStringTransform, MoreExecutors.directExecutor()); + } + + /** + * 销毁 + */ + @Override + public void destroy() { + scriptInvokeService.release(this.scriptId); + } + + /** + * 转换string + * + * @param result 结果 + * @return 结果 + */ + protected abstract ListenableFuture executeToStringTransform(R result); + + /** + * 转换Boolean + * + * @param result 结果 + * @return 结果 + */ + protected abstract ListenableFuture executeFilterTransform(R result); + + /** + * 转换入参 + * + * @param msg 入参 + * @return Object[] + */ + protected abstract Object[] prepareArgs(ScriptMsg msg); + + /** + * 转换 + * + * @param result 结果 + * @return 结果 + */ + protected abstract R convertResult(Object result); + + protected ListenableFuture executeScriptAsync(ScriptMsg msg) { + log.trace("execute script async, msg {}", msg); + Object[] inArgs = prepareArgs(msg); + return executeScriptAsync(msg, inArgs[0], inArgs[1]); + } + + ListenableFuture executeScriptAsync(ScriptMsg msg, Object... args) { + return Futures.transformAsync(scriptInvokeService.invokeScript(this.scriptId, args), + o -> { + ScriptLog entity = new ScriptLog(this.scriptId, msg.getMsgStr(), null); + try { + R r = convertResult(o); + if (debug) { + saveLog(entity, r); + } + return Futures.immediateFuture(r); + } catch (Exception e) { + entity.setErrMsg(e.getMessage()); + scriptLogService.saveLog(entity); + if (e.getCause() instanceof ScriptException) { + return Futures.immediateFailedFuture(e.getCause()); + } else if (e.getCause() instanceof RuntimeException) { + return Futures.immediateFailedFuture(new ScriptException(e.getCause().getMessage())); + } else { + return Futures.immediateFailedFuture(new ScriptException(e)); + } + } + }, MoreExecutors.directExecutor()); + } + + private void saveLog(ScriptLog entity, R r) { + try { + entity.setOutMsg(JacksonUtil.convertValue(r, JsonNode.class)); + } catch (Exception e) { + entity.setErrMsg(e.getMessage()); + } + scriptLogService.saveLog(entity); + } +} diff --git a/common/script/src/main/java/com/thing/api/AbstractScriptInvokeService.java b/common/script/src/main/java/com/thing/api/AbstractScriptInvokeService.java new file mode 100644 index 0000000..7e28116 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/AbstractScriptInvokeService.java @@ -0,0 +1,166 @@ +package com.thing.api; + +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; +import com.thing.CustomScriptException; +import com.thing.ScriptExecutionTask; +import com.thing.common.util.thread.ThingThreadFactory; +import lombok.extern.slf4j.Slf4j; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * @author zhenghh. 2022-12-21 + **/ +@Slf4j +public abstract class AbstractScriptInvokeService implements ScriptInvokeService { + + protected ScheduledExecutorService timeoutExecutorService; + + protected long getMaxEvalRequestsTimeout() { + return getMaxInvokeRequestsTimeout(); + } + + /** + * 调用超时时间 + * + * @return long + */ + protected abstract long getMaxInvokeRequestsTimeout(); + + /** + * 是否存在 + * + * @param scriptId 缓存主键 + * @return boolean + */ + protected abstract boolean isScriptPresent(String scriptId); + + /** + * 编译 + * + * @param scriptBody 方法体 + * @param scriptId 计算主键 + * @param argNames 参数集合 + * @return 计算主键 + */ + protected abstract ListenableFuture doEvalScript(String scriptBody, String scriptId, String[] argNames); + + /** + * 执行 + * + * @param scriptId 计算主键 + * @param args 入参 + * @return 结果 + */ + protected abstract ScriptExecutionTask doInvokeFunction(String scriptId, Object[] args); + + /** + * 销毁 + * + * @param scriptId 缓存主键 + * @throws Exception e + */ + protected abstract void doRelease(String scriptId) throws Exception; + + public void init() { + if (getMaxEvalRequestsTimeout() > 0) { + timeoutExecutorService = Executors.newSingleThreadScheduledExecutor(ThingThreadFactory.forName(getLanguage() + "script-timeout")); + } + } + + public void stop() { + if (timeoutExecutorService != null) { + timeoutExecutorService.shutdownNow(); + } + } + + @Override + public ListenableFuture eval(String scriptId, String scriptBody, String... argNames) { + return withTimeout(scriptId, null, doEvalScript(scriptBody, scriptId, argNames), getMaxEvalRequestsTimeout()); + } + + @Override + public ListenableFuture invokeScript(String scriptId, Object... args) { + if (!isScriptPresent(scriptId)) { + return error("No compiled script found for scriptId: [" + scriptId + "]!"); + } + log.trace("InvokeScript scriptId {} with timeout {}ms", scriptId, getMaxInvokeRequestsTimeout()); + ScriptExecutionTask task = doInvokeFunction(scriptId, args); + ListenableFuture resultFuture = Futures.transformAsync(task.getResultFuture(), Futures::immediateFuture, MoreExecutors.directExecutor()); + + return withTimeout(scriptId, task, resultFuture, getMaxInvokeRequestsTimeout()); + + } + + @Override + public ListenableFuture release(String scriptId) { + if (isScriptPresent(scriptId)) { + try { + doRelease(scriptId); + } catch (Exception e) { + return Futures.immediateFailedFuture(e); + } + } + return Futures.immediateFuture(null); + } + + private ListenableFuture withTimeout(String scriptId, ScriptExecutionTask task, ListenableFuture future, long timeout) { + if (timeout > 0) { + future = Futures.withTimeout(future, timeout, TimeUnit.MILLISECONDS, timeoutExecutorService); + } + return Futures.catchingAsync(future, Exception.class, + input -> Futures.immediateFailedFuture(handleScriptException(scriptId, task, input)), + MoreExecutors.directExecutor()); + } + + private Throwable handleScriptException(String scriptId, ScriptExecutionTask task, Throwable t) { + boolean timeout = t instanceof TimeoutException || (t.getCause() != null && t.getCause() instanceof TimeoutException); + if (timeout && task != null) { + task.stop(); + } + if (t instanceof CustomScriptException) { + CustomScriptException customScriptException = (CustomScriptException) t; + Throwable cause = customScriptException.getCause(); + switch (customScriptException.getErrorCode()) { + case COMPILATION: + log.debug("[{}] Failed to compile script: {}", scriptId, customScriptException.getBody(), cause); + break; + case TIMEOUT: + log.debug("[{}] Timeout to execute script: {}", scriptId, customScriptException.getBody(), cause); + break; + case OTHER: + case RUNTIME: + default: + log.debug("[{}] Failed to execute script: {}", scriptId, customScriptException.getBody(), cause); + break; + } + } + if (timeout) { + return new TimeoutException("Script timeout!"); + } else { + return t; + } + } + + private ListenableFuture error(String message) { + return Futures.immediateFailedFuture(new RuntimeException(message)); + } + + @SuppressWarnings("UnstableApiUsage") + protected String hash(String scriptBody, String[] argNames) { + Hasher hasher = Hashing.murmur3_128().newHasher(); + hasher.putUnencodedChars(scriptBody); + for (String argName : argNames) { + hasher.putString(argName, StandardCharsets.UTF_8); + } + return hasher.hash().toString(); + } +} diff --git a/common/script/src/main/java/com/thing/api/FormulaInvokeService.java b/common/script/src/main/java/com/thing/api/FormulaInvokeService.java new file mode 100644 index 0000000..5f20b02 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/FormulaInvokeService.java @@ -0,0 +1,40 @@ +package com.thing.api; + +import com.googlecode.aviator.Expression; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * @author zhenghh. 2022-06-09 + **/ +public interface FormulaInvokeService { + + /** + * 编译公式 + * + * @param formula 公式 + * @return Expression + */ + Expression evalFormula(String formula); + + /** + * 执行公式 + * + * @param formula 公式 + * @param env 参数 + * @return 结果 + */ + Object invokeFormula(String formula, Map env); + + + /** + * 执行公式 + * + * @param formula 公式 + * @param env 参数 + * @param newScale 保留小数位 + * @return 结果 + */ + BigDecimal invokeFormula(String formula, Map env, int newScale); +} diff --git a/common/script/src/main/java/com/thing/api/FunctionScriptFactory.java b/common/script/src/main/java/com/thing/api/FunctionScriptFactory.java new file mode 100644 index 0000000..45bdee0 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/FunctionScriptFactory.java @@ -0,0 +1,34 @@ +package com.thing.api; + +/** + * @author zhenghh. 2022-12-21 + **/ +public class FunctionScriptFactory { + + public static final String MSG = "msg"; + public static final String METADATA = "metadata"; + public static final String FUNCTION_NAME = "scriptFunc"; + + private static final String JS_WRAPPER_PREFIX_TEMPLATE = "function %s(msgStr, metadataStr) { " + + " var msg = JSON.parse(msgStr); " + + " var metadata = JSON.parse(metadataStr); " + + " return JSON.stringify(%s(msg, metadata));" + + " function %s(%s, %s) {"; + private static final String JS_WRAPPER_SUFFIX = "\n}" + + "\n}"; + + + public static String generateScript(String functionName, String scriptBody, String... argNames) { + String msgArg; + String metadataArg; + if (argNames != null && argNames.length == 2) { + msgArg = argNames[0]; + metadataArg = argNames[1]; + } else { + msgArg = MSG; + metadataArg = METADATA; + } + String jsWrapperPrefix = String.format(JS_WRAPPER_PREFIX_TEMPLATE, functionName, FUNCTION_NAME, FUNCTION_NAME, msgArg, metadataArg); + return jsWrapperPrefix + scriptBody + JS_WRAPPER_SUFFIX; + } +} diff --git a/common/script/src/main/java/com/thing/api/JavaInvokeService.java b/common/script/src/main/java/com/thing/api/JavaInvokeService.java new file mode 100644 index 0000000..3b49ebe --- /dev/null +++ b/common/script/src/main/java/com/thing/api/JavaInvokeService.java @@ -0,0 +1,38 @@ +package com.thing.api; + +import com.google.common.collect.Lists; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; + +import java.util.List; + +/** + * @author zhenghh. 2023-02-07 + **/ +public class JavaInvokeService { + + /** + * Nashorn 使用 + * 配置 sandbox.allow(JavaInvokeService.class); + * js写法如下 + * var javaInvokeService = Java.type("com.thing.api.JavaInvokeService"); + * var result = javaInvokeService.getLastTimeseries('D1', 'EF_SC_HMI'); + * + * Aviator 使用 + * 配置 aviatorEvaluatorInstance.addStaticFunctions("JavaInvokeService", JavaInvokeService.class); + * js写法如下 + * let result = JavaInvokeService.getLastTimeseries('D1', 'EF_SC_HMI'); + * + * @param thingCode 物编码 + * @param thingAttr 物属性 + * @return 遥测值 + */ + public static String getLastTimeseries(String thingCode, String thingAttr) { + TsKvService tsKvService = SpringContextUtils.getBean(TsKvService.class); + List lastTimeseries = tsKvService.findLatestByCodeAndAttrs(thingCode, Lists.newArrayList(thingAttr), false); + return JacksonUtil.toString(lastTimeseries); + } + +} diff --git a/common/script/src/main/java/com/thing/api/ScriptEngine.java b/common/script/src/main/java/com/thing/api/ScriptEngine.java new file mode 100644 index 0000000..4c74d88 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/ScriptEngine.java @@ -0,0 +1,40 @@ +package com.thing.api; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptMsg; + +/** + * @author zhenghh. 2022-12-21 + **/ +public interface ScriptEngine { + + /** + * 过滤 + * + * @param msg 消息 + * @return Boolean + */ + ListenableFuture executeFilterAsync(ScriptMsg msg); + + /** + * 转换json + * + * @param msg 消息 + * @return JsonNode + */ + ListenableFuture executeJsonAsync(ScriptMsg msg); + + /** + * 转换字符串 + * + * @param msg 消息 + * @return String + */ + ListenableFuture executeToStringAsync(ScriptMsg msg); + + /** + * 销毁 + */ + void destroy(); +} diff --git a/common/script/src/main/java/com/thing/api/ScriptInvokeService.java b/common/script/src/main/java/com/thing/api/ScriptInvokeService.java new file mode 100644 index 0000000..32cb30c --- /dev/null +++ b/common/script/src/main/java/com/thing/api/ScriptInvokeService.java @@ -0,0 +1,44 @@ +package com.thing.api; + +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptLanguage; + +/** + * @author zhenghh. 2022-12-20 + **/ +public interface ScriptInvokeService { + + /** + * 编译 + * + * @param scriptId 计算主键 + * @param scriptBody 消息体 + * @param argNames 参数名称 + * @return scriptId + */ + ListenableFuture eval(String scriptId, String scriptBody, String... argNames); + + /** + * 执行 + * + * @param scriptId uuid + * @param args 入参 + * @return 计算结果 + */ + ListenableFuture invokeScript(String scriptId, Object... args); + + /** + * 销毁 + * + * @param scriptId uuid + * @return Void + */ + ListenableFuture release(String scriptId); + + /** + * 编译语言 + * + * @return ScriptLanguage + */ + ScriptLanguage getLanguage(); +} diff --git a/common/script/src/main/java/com/thing/api/aviator/AviatorExecutionTask.java b/common/script/src/main/java/com/thing/api/aviator/AviatorExecutionTask.java new file mode 100644 index 0000000..eadc7b3 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/aviator/AviatorExecutionTask.java @@ -0,0 +1,22 @@ +package com.thing.api.aviator; + +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptExecutionTask; + +/** + * @author zhenghh. 2022-12-21 + **/ +public class AviatorExecutionTask extends ScriptExecutionTask { + + protected AviatorExecutionTask(ListenableFuture resultFuture) { + super(resultFuture); + } + + /** + * 停止 + */ + @Override + public void stop() { + + } +} diff --git a/common/script/src/main/java/com/thing/api/aviator/AviatorInvokeService.java b/common/script/src/main/java/com/thing/api/aviator/AviatorInvokeService.java new file mode 100644 index 0000000..5e22f7b --- /dev/null +++ b/common/script/src/main/java/com/thing/api/aviator/AviatorInvokeService.java @@ -0,0 +1,21 @@ +package com.thing.api.aviator; + + +import com.thing.ScriptLanguage; +import com.thing.api.ScriptInvokeService; + +/** + * @author zhenghh. 2022-12-23 + **/ +public interface AviatorInvokeService extends ScriptInvokeService { + + /** + * 编译语言 + * + * @return ScriptLanguage + */ + @Override + default ScriptLanguage getLanguage() { + return ScriptLanguage.AVIATOR; + } +} diff --git a/common/script/src/main/java/com/thing/api/aviator/AviatorScript.java b/common/script/src/main/java/com/thing/api/aviator/AviatorScript.java new file mode 100644 index 0000000..e8dd212 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/aviator/AviatorScript.java @@ -0,0 +1,27 @@ +package com.thing.api.aviator; + +import com.google.common.collect.Maps; +import lombok.Data; + +import java.util.Map; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Data +public class AviatorScript { + + private final String scriptId; + private final String[] argNames; + + public Map createVars(Object[] args) { + if (args == null || args.length != argNames.length) { + throw new IllegalArgumentException("Invalid number of argument values"); + } + Map result = Maps.newHashMapWithExpectedSize(argNames.length); + for (int i = 0; i < argNames.length; i++) { + result.put(argNames[i], args[i]); + } + return result; + } +} diff --git a/common/script/src/main/java/com/thing/api/aviator/AviatorScriptEngine.java b/common/script/src/main/java/com/thing/api/aviator/AviatorScriptEngine.java new file mode 100644 index 0000000..9cfb56f --- /dev/null +++ b/common/script/src/main/java/com/thing/api/aviator/AviatorScriptEngine.java @@ -0,0 +1,124 @@ +package com.thing.api.aviator; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Maps; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +import com.thing.ScriptMsg; +import com.thing.api.AbstractScriptEngine; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.util.CollectionUtils; + +import javax.script.ScriptException; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Map; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Slf4j +public class AviatorScriptEngine extends AbstractScriptEngine implements Serializable { + + @Serial private static final long serialVersionUID = 2046783551920029583L; + + public AviatorScriptEngine(String scriptId, AviatorInvokeService scriptInvokeService, ScriptLogService scriptLogService, + Boolean debug, String script, String... argNames) { + super(scriptId, scriptInvokeService, scriptLogService, debug, script, argNames); + } + + /** + * 转换string + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeToStringTransform(Object result) { + if (result instanceof String) { + return Futures.immediateFuture((String) result); + } else { + return Futures.immediateFuture(JacksonUtil.toString(result)); + } + } + + /** + * 转换Boolean + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeFilterTransform(Object result) { + if (result instanceof Boolean) { + return Futures.immediateFuture((Boolean) result); + } + return wrongResultType(result); + } + + /** + * 转换入参 + * + * @param msg 入参 + * @return Object[] + */ + @Override + protected Object[] prepareArgs(ScriptMsg msg) { + Object[] args = new Object[2]; + args[0] = putValue(JacksonUtil.fromString(msg.getMsg(), Map.class)); + args[1] = putValue(msg.getMetaData()); + return args; + } + + public Map putValue(Map map) { + if (CollectionUtils.isEmpty(map)) { + return Maps.newHashMap(); + } + Map result = Maps.newHashMapWithExpectedSize(map.size()); + map.forEach((key, value) -> { + if (key != null && value != null) { + result.put(key, value instanceof String ? value : + (NumberUtils.isCreatable(value.toString()) ? new BigDecimal(value.toString()) : value)); + } + }); + return result; + } + + /** + * 转换 + * + * @param result 结果 + * @return 结果 + */ + @Override + protected Object convertResult(Object result) { + return result; + } + + /** + * 转换json + * + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(ScriptMsg msg) { + return Futures.transform(executeScriptAsync(msg), JacksonUtil::valueToTree, MoreExecutors.directExecutor()); + } + + private static ListenableFuture wrongResultType(Object result) { + String className = toClassName(result); + log.warn("Wrong result type: {}", className); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + className)); + } + + private static String toClassName(Object result) { + return result != null ? result.getClass().getSimpleName() : "null"; + } +} diff --git a/common/script/src/main/java/com/thing/api/aviator/DefaultAviatorInvokeService.java b/common/script/src/main/java/com/thing/api/aviator/DefaultAviatorInvokeService.java new file mode 100644 index 0000000..67cb05b --- /dev/null +++ b/common/script/src/main/java/com/thing/api/aviator/DefaultAviatorInvokeService.java @@ -0,0 +1,189 @@ +package com.thing.api.aviator; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.hash.Hasher; +import com.google.common.hash.Hashing; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.AviatorEvaluatorInstance; +import com.googlecode.aviator.Expression; +import com.googlecode.aviator.Options; +import com.thing.CustomScriptException; +import com.thing.ScriptExecutionTask; +import com.thing.api.AbstractScriptInvokeService; +import com.thing.api.FormulaInvokeService; +import com.thing.api.JavaInvokeService; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Slf4j +@Service +public class DefaultAviatorInvokeService extends AbstractScriptInvokeService implements AviatorInvokeService, FormulaInvokeService { + + protected final Map scriptInfoMap = new ConcurrentHashMap<>(); + private AviatorEvaluatorInstance aviatorEvaluatorInstance; + private ListeningExecutorService jsExecutor; + private final ReentrantLock evalLock = new ReentrantLock(); + + @Getter + @Value("${aviator.max_requests_timeout:0}") + private long maxInvokeRequestsTimeout; + + @Value("${aviator.js_thread_pool_size:50}") + private int jsExecutorThreadPoolSize; + + @Value("${aviator.put_capturing_groups_into_env:false}") + private boolean putCapturingGroupsIntoEnv; + + @Value("${aviator.max_loop_count:1000}") + private int maxLoopCount; + + @Override + @PostConstruct + public void init() { + super.init(); + jsExecutor = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(jsExecutorThreadPoolSize)); + aviatorEvaluatorInstance = AviatorEvaluator.getInstance(); + aviatorEvaluatorInstance.setOption(Options.PUT_CAPTURING_GROUPS_INTO_ENV, putCapturingGroupsIntoEnv); + aviatorEvaluatorInstance.setOption(Options.MAX_LOOP_COUNT, maxLoopCount); + try { + aviatorEvaluatorInstance.addStaticFunctions("JavaInvokeService", JavaInvokeService.class); + } catch (IllegalAccessException | NoSuchMethodException e) { + e.printStackTrace(); + } + } + + @Override + @PreDestroy + public void stop() { + super.stop(); + } + + @Override + protected boolean isScriptPresent(String scriptId) { + return scriptInfoMap.containsKey(scriptId); + } + + @Override + protected ListenableFuture doEvalScript(String scriptBody, String scriptId, String[] argNames) { + AviatorScript script = new AviatorScript(scriptId, argNames); + return jsExecutor.submit(() -> { + try { + evalLock.lock(); + try { + aviatorEvaluatorInstance.compile(scriptId, scriptBody, true); + } finally { + evalLock.unlock(); + } + scriptInfoMap.put(scriptId, script); + return scriptId; + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.COMPILATION, scriptBody, e); + } + }); + } + + @Override + protected ScriptExecutionTask doInvokeFunction(String scriptId, Object[] args) { + return new AviatorExecutionTask(jsExecutor.submit(() -> { + try { + Expression expression = aviatorEvaluatorInstance.getCachedExpressionByKey(scriptId); + return expression.execute(scriptInfoMap.get(scriptId).createVars(args)); + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.OTHER, null, e); + } + })); + } + + @Override + protected void doRelease(String scriptId) throws Exception { + scriptInfoMap.remove(scriptId); + aviatorEvaluatorInstance.invalidateCacheByKey(scriptId); + } + + /** + * 编译公式 + * + * @param formula 公式 + * @return Expression + */ + @Override + public Expression evalFormula(String formula) { + return doEval(hash(formula), formula); + } + + /** + * 执行公式 + * + * @param formula 公式 + * @param env 参数 + * @return 结果 + */ + @Override + public Object invokeFormula(String formula, Map env) { + String scriptId = hash(formula); + Expression expression = aviatorEvaluatorInstance.getCachedExpressionByKey(scriptId); + if (expression == null) { + expression = doEval(scriptId, formula); + } + + return doInvokeFormula(expression, env); + } + + /** + * 执行公式 + * + * @param formula 公式 + * @param env 参数 + * @param newScale 保留小数位 + * @return 结果 + */ + @Override + public BigDecimal invokeFormula(String formula, Map env, int newScale) { + Object execute = invokeFormula(formula, env); + if (ObjectUtil.isNotNull(execute) && NumberUtils.isNumber(execute.toString())) { + if (newScale == -1) { + return new BigDecimal(execute.toString()).stripTrailingZeros(); + } + return new BigDecimal(execute.toString()).setScale(newScale, RoundingMode.HALF_UP).stripTrailingZeros(); + } + return BigDecimal.ZERO; + } + + private Expression doEval(String scriptId, String formula) { + return aviatorEvaluatorInstance.compile(scriptId, formula, true); + } + + protected Object doInvokeFormula(Expression expression, Map env) { + try { + return expression.execute(env); + } catch (Exception e) { + log.error("计算失败:{}", e.getMessage()); + } + return null; + } + + @SuppressWarnings("UnstableApiUsage") + private String hash(String formula) { + Hasher hasher = Hashing.murmur3_128().newHasher(); + hasher.putUnencodedChars(formula); + return hasher.hash().toString(); + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/DefaultTbelInvokeService.java b/common/script/src/main/java/com/thing/api/mvel/DefaultTbelInvokeService.java new file mode 100644 index 0000000..590a72c --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/DefaultTbelInvokeService.java @@ -0,0 +1,156 @@ +package com.thing.api.mvel; + + + +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +import com.thing.CustomScriptException; +import com.thing.api.AbstractScriptInvokeService; +import com.thing.common.util.thread.ThingExecutors; +import lombok.Getter; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.mvel2.*; +import org.mvel2.optimizers.OptimizerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.io.Serializable; +import java.util.Calendar; +import java.util.Collections; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +@Slf4j +@Service +public class DefaultTbelInvokeService extends AbstractScriptInvokeService implements TbelInvokeService { + + protected final Map scriptIdToHash = new ConcurrentHashMap<>(); + protected final Map scriptMap = new ConcurrentHashMap<>(); + protected Cache compiledScriptsCache; + + private SandboxedParserConfiguration parserConfig; + + @Getter + @Value("${tbel.max_requests_timeout:0}") + private long maxInvokeRequestsTimeout; + + @Value("${tbel.thread_pool_size:50}") + private int threadPoolSize; + + @Value("${tbel.max_memory_limit_mb:8}") + private long maxMemoryLimitMb; + + @Value("${tbel.compiled_scripts_cache_size:1000}") + private int compiledScriptsCacheSize; + + private ListeningExecutorService executor; + + private final Lock lock = new ReentrantLock(); + + @SneakyThrows + @Override + @PostConstruct + public void init() { + super.init(); + OptimizerFactory.setDefaultOptimizer(OptimizerFactory.SAFE_REFLECTIVE); + parserConfig = ParserContext.enableSandboxedMode(); + parserConfig.addImport("JSON", TbJson.class); + parserConfig.registerDataType("Date", TbDate.class, date -> 8L); + parserConfig.registerDataType("Random", Random.class, date -> 8L); + parserConfig.registerDataType("Calendar", Calendar.class, date -> 8L); + TbUtils.register(parserConfig); + executor = MoreExecutors.listeningDecorator(ThingExecutors.newWorkStealingPool(threadPoolSize, "tbel-executor")); + try { + // Special command to warm up TBEL engine + Serializable script = compileScript("var warmUp = {}; warmUp"); + MVEL.executeTbExpression(script, new ExecutionContext(parserConfig), Collections.emptyMap()); + } catch (Exception e) { + // do nothing + } + compiledScriptsCache = Caffeine.newBuilder() + .maximumSize(compiledScriptsCacheSize) + .build(); + } + + @PreDestroy + public void destroy() { + if (executor != null) { + executor.shutdownNow(); + } + } + + @Override + protected boolean isScriptPresent(String scriptId) { + return scriptIdToHash.containsKey(scriptId); + } + + @Override + protected ListenableFuture doEvalScript(String scriptBody, String scriptId, String[] argNames) { + return executor.submit(() -> { + try { + String scriptHash = hash(scriptBody, argNames); + compiledScriptsCache.get(scriptHash, k -> compileScript(scriptBody)); + lock.lock(); + try { + scriptIdToHash.put(scriptId, scriptHash); + scriptMap.computeIfAbsent(scriptHash, k -> new TbelScript(scriptBody, argNames)); + } finally { + lock.unlock(); + } + return scriptId; + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.COMPILATION, scriptBody, e); + } + }); + } + + @Override + protected TbelExecutionTask doInvokeFunction(String scriptId, Object[] args) { + ExecutionContext executionContext = new ExecutionContext(this.parserConfig, maxMemoryLimitMb * 1024 * 1024); + return new TbelExecutionTask(executionContext, executor.submit(() -> { + String scriptHash = scriptIdToHash.get(scriptId); + if (scriptHash == null) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.OTHER, null, new RuntimeException("Script not found!")); + } + TbelScript script = scriptMap.get(scriptHash); + Serializable compiledScript = compiledScriptsCache.get(scriptHash, k -> compileScript(script.getScriptBody())); + try { + return MVEL.executeTbExpression(compiledScript, executionContext, script.createVars(args)); + } catch (ScriptMemoryOverflowException e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.OTHER, script.getScriptBody(), new RuntimeException("Script memory overflow!")); + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.RUNTIME, script.getScriptBody(), e); + } + })); + } + + @Override + protected void doRelease(String scriptId) { + String scriptHash = scriptIdToHash.remove(scriptId); + if (scriptHash != null) { + lock.lock(); + try { + if (!scriptIdToHash.containsValue(scriptHash)) { + scriptMap.remove(scriptHash); + compiledScriptsCache.invalidate(scriptHash); + } + } finally { + lock.unlock(); + } + } + } + + private Serializable compileScript(String scriptBody) { + return MVEL.compileExpression(scriptBody, new ParserContext()); + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbDate.java b/common/script/src/main/java/com/thing/api/mvel/TbDate.java new file mode 100644 index 0000000..594f7b8 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbDate.java @@ -0,0 +1,106 @@ +package com.thing.api.mvel; + +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.*; +import java.time.format.DateTimeFormatter; +import java.time.temporal.ChronoUnit; +import java.time.temporal.TemporalAccessor; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +public class TbDate extends Date { + + private static final DateTimeFormatter isoDateFormatter = DateTimeFormatter.ofPattern( + "yyyy-MM-dd[[ ]['T']HH:mm[:ss[.SSS]][ ][XXX][Z][z][VV][O]]").withZone(ZoneId.systemDefault()); + + private static final DateFormat isoDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); + + public TbDate() { + super(); + } + + public TbDate(String s) { + super(parse(s)); + } + + public TbDate(long date) { + super(date); + } + + public TbDate(int year, int month, int date) { + this(year, month, date, 0, 0, 0); + } + + public TbDate(int year, int month, int date, int hrs, int min) { + this(year, month, date, hrs, min, 0); + } + + public TbDate(int year, int month, int date, + int hrs, int min, int second) { + super(new GregorianCalendar(year, month, date, hrs, min, second).getTimeInMillis()); + } + + public String toDateString() { + DateFormat formatter = DateFormat.getDateInstance(); + return formatter.format(this); + } + + public String toTimeString() { + DateFormat formatter = DateFormat.getTimeInstance(DateFormat.LONG); + return formatter.format(this); + } + + public String toISOString() { + return isoDateFormat.format(this); + } + + public String toLocaleString(String locale) { + DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale)); + return formatter.format(this); + } + + public String toLocaleString(String locale, String tz) { + DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM, Locale.forLanguageTag(locale)); + formatter.setTimeZone(TimeZone.getTimeZone(tz)); + return formatter.format(this); + } + + public static long now() { + return System.currentTimeMillis(); + } + + public static long parse(String value, String format) { + try { + DateFormat dateFormat = new SimpleDateFormat(format); + return dateFormat.parse(value).getTime(); + } catch (Exception e) { + return -1; + } + } + + public static long parse(String value) { + try { + TemporalAccessor accessor = isoDateFormatter.parseBest(value, + ZonedDateTime::from, + LocalDateTime::from, + LocalDate::from); + Instant instant = Instant.from(accessor); + return Instant.EPOCH.until(instant, ChronoUnit.MILLIS); + } catch (Exception e) { + try { + return Date.parse(value); + } catch (IllegalArgumentException e2) { + return -1; + } + } + } + + public static long UTC(int year, int month, int date, + int hrs, int min, int sec) { + return Date.UTC(year - 1900, month, date, hrs, min, sec); + } + +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbJson.java b/common/script/src/main/java/com/thing/api/mvel/TbJson.java new file mode 100644 index 0000000..133abce --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbJson.java @@ -0,0 +1,46 @@ +package com.thing.api.mvel; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.utils.JacksonUtil; +import org.mvel2.ExecutionContext; +import org.mvel2.util.ArgsRepackUtil; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class TbJson { + + public static String stringify(Object value) { + return value != null ? JacksonUtil.toString(value) : "null"; + } + + public static Object parse(ExecutionContext ctx, String value) throws IOException { + if (value != null) { + JsonNode node = JacksonUtil.toJsonNode(value); + if (node.isObject()) { + return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, Map.class)); + } else if (node.isArray()) { + return ArgsRepackUtil.repack(ctx, JacksonUtil.convertValue(node, List.class)); + } else if (node.isDouble()) { + return node.doubleValue(); + } else if (node.isLong()) { + return node.longValue(); + } else if (node.isInt()) { + return node.intValue(); + } else if (node.isBoolean()) { + return node.booleanValue(); + } else if (node.isTextual()) { + return node.asText(); + } else if (node.isBinary()) { + return node.binaryValue(); + } else if (node.isNull()) { + return null; + } else { + return node.asText(); + } + } else { + return null; + } + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbUtils.java b/common/script/src/main/java/com/thing/api/mvel/TbUtils.java new file mode 100644 index 0000000..23be653 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbUtils.java @@ -0,0 +1,304 @@ +package com.thing.api.mvel; + +import org.mvel2.ExecutionContext; +import org.mvel2.ParserConfiguration; +import org.mvel2.execution.ExecutionArrayList; +import org.mvel2.util.MethodStub; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.List; + +public class TbUtils { + + private static final byte[] HEX_ARRAY = "0123456789ABCDEF".getBytes(StandardCharsets.US_ASCII); + + public static void register(ParserConfiguration parserConfig) throws Exception { + parserConfig.addImport("btoa", new MethodStub(TbUtils.class.getMethod("btoa", + String.class))); + parserConfig.addImport("atob", new MethodStub(TbUtils.class.getMethod("atob", + String.class))); + parserConfig.addImport("bytesToString", new MethodStub(TbUtils.class.getMethod("bytesToString", + List.class))); + parserConfig.addImport("bytesToString", new MethodStub(TbUtils.class.getMethod("bytesToString", + List.class, String.class))); + parserConfig.addImport("decodeToString", new MethodStub(TbUtils.class.getMethod("bytesToString", + List.class))); + parserConfig.addImport("decodeToJson", new MethodStub(TbUtils.class.getMethod("decodeToJson", + ExecutionContext.class, List.class))); + parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", + ExecutionContext.class, String.class))); + parserConfig.addImport("stringToBytes", new MethodStub(TbUtils.class.getMethod("stringToBytes", + ExecutionContext.class, String.class, String.class))); + parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt", + String.class))); + parserConfig.addImport("parseInt", new MethodStub(TbUtils.class.getMethod("parseInt", + String.class, int.class))); + parserConfig.addImport("parseFloat", new MethodStub(TbUtils.class.getMethod("parseFloat", + String.class))); + parserConfig.addImport("parseDouble", new MethodStub(TbUtils.class.getMethod("parseDouble", + String.class))); + parserConfig.addImport("parseLittleEndianHexToInt", new MethodStub(TbUtils.class.getMethod("parseLittleEndianHexToInt", + String.class))); + parserConfig.addImport("parseBigEndianHexToInt", new MethodStub(TbUtils.class.getMethod("parseBigEndianHexToInt", + String.class))); + parserConfig.addImport("parseHexToInt", new MethodStub(TbUtils.class.getMethod("parseHexToInt", + String.class))); + parserConfig.addImport("parseHexToInt", new MethodStub(TbUtils.class.getMethod("parseHexToInt", + String.class, boolean.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + List.class, int.class, int.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + List.class, int.class, int.class, boolean.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + byte[].class, int.class, int.class))); + parserConfig.addImport("parseBytesToInt", new MethodStub(TbUtils.class.getMethod("parseBytesToInt", + byte[].class, int.class, int.class, boolean.class))); + parserConfig.addImport("toFixed", new MethodStub(TbUtils.class.getMethod("toFixed", + double.class, int.class))); + parserConfig.addImport("hexToBytes", new MethodStub(TbUtils.class.getMethod("hexToBytes", + ExecutionContext.class, String.class))); + parserConfig.addImport("base64ToHex", new MethodStub(TbUtils.class.getMethod("base64ToHex", + String.class))); + parserConfig.addImport("base64ToBytes", new MethodStub(TbUtils.class.getMethod("base64ToBytes", + String.class))); + parserConfig.addImport("bytesToBase64", new MethodStub(TbUtils.class.getMethod("bytesToBase64", + byte[].class))); + parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex", + byte[].class))); + parserConfig.addImport("bytesToHex", new MethodStub(TbUtils.class.getMethod("bytesToHex", + ExecutionArrayList.class))); + } + + public static String btoa(String input) { + return new String(Base64.getEncoder().encode(input.getBytes())); + } + + public static String atob(String encoded) { + return new String(Base64.getDecoder().decode(encoded)); + } + + public static Object decodeToJson(ExecutionContext ctx, List bytesList) throws IOException { + return TbJson.parse(ctx, bytesToString(bytesList)); + } + + public static String bytesToString(List bytesList) { + byte[] bytes = bytesFromList(bytesList); + return new String(bytes); + } + + public static String bytesToString(List bytesList, String charsetName) throws UnsupportedEncodingException { + byte[] bytes = bytesFromList(bytesList); + return new String(bytes, charsetName); + } + + public static List stringToBytes(ExecutionContext ctx, String str) { + byte[] bytes = str.getBytes(); + return bytesToList(ctx, bytes); + } + + public static List stringToBytes(ExecutionContext ctx, String str, String charsetName) throws UnsupportedEncodingException { + byte[] bytes = str.getBytes(charsetName); + return bytesToList(ctx, bytes); + } + + private static byte[] bytesFromList(List bytesList) { + byte[] bytes = new byte[bytesList.size()]; + for (int i = 0; i < bytesList.size(); i++) { + bytes[i] = bytesList.get(i); + } + return bytes; + } + + private static List bytesToList(ExecutionContext ctx, byte[] bytes) { + List list = new ExecutionArrayList<>(ctx); + for (byte aByte : bytes) { + list.add(aByte); + } + return list; + } + + public static Integer parseInt(String value) { + if (value != null) { + try { + int radix = 10; + if (isHexadecimal(value)) { + radix = 16; + } + return Integer.parseInt(prepareNumberString(value), radix); + } catch (NumberFormatException e) { + Float f = parseFloat(value); + if (f != null) { + return f.intValue(); + } + } + } + return null; + } + + public static Integer parseInt(String value, int radix) { + if (value != null) { + try { + return Integer.parseInt(prepareNumberString(value), radix); + } catch (NumberFormatException e) { + Float f = parseFloat(value); + if (f != null) { + return f.intValue(); + } + } + } + return null; + } + + public static Float parseFloat(String value) { + if (value != null) { + try { + return Float.parseFloat(prepareNumberString(value)); + } catch (NumberFormatException e) { + } + } + return null; + } + + public static Double parseDouble(String value) { + if (value != null) { + try { + return Double.parseDouble(prepareNumberString(value)); + } catch (NumberFormatException e) { + } + } + return null; + } + + public static int parseLittleEndianHexToInt(String hex) { + return parseHexToInt(hex, false); + } + + public static int parseBigEndianHexToInt(String hex) { + return parseHexToInt(hex, true); + } + + public static int parseHexToInt(String hex) { + return parseHexToInt(hex, true); + } + + public static int parseHexToInt(String hex, boolean bigEndian) { + int length = hex.length(); + if (length > 8) { + throw new IllegalArgumentException("Hex string is too large. Maximum 8 symbols allowed."); + } + if (length % 2 > 0) { + throw new IllegalArgumentException("Hex string must be even-length."); + } + byte[] data = new byte[length / 2]; + for (int i = 0; i < length; i += 2) { + data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); + } + return parseBytesToInt(data, 0, data.length, bigEndian); + } + + public static ExecutionArrayList hexToBytes(ExecutionContext ctx, String hex) { + int len = hex.length(); + if (len % 2 > 0) { + throw new IllegalArgumentException("Hex string must be even-length."); + } + ExecutionArrayList data = new ExecutionArrayList<>(ctx); + for (int i = 0; i < len; i += 2) { + data.add((byte)((Character.digit(hex.charAt(i), 16) << 4) + + Character.digit(hex.charAt(i + 1), 16))); + } + return data; + } + + public static String base64ToHex(String base64) { + return bytesToHex(Base64.getDecoder().decode(base64)); + } + + public static String bytesToBase64(byte[] bytes) { + return Base64.getEncoder().encodeToString(bytes); + } + + public static byte[] base64ToBytes(String input) { + return Base64.getDecoder().decode(input); + } + + public static int parseBytesToInt(List data, int offset, int length) { + return parseBytesToInt(data, offset, length, true); + } + + public static int parseBytesToInt(List data, int offset, int length, boolean bigEndian) { + final byte[] bytes = new byte[data.size()]; + for (int i = 0; i < bytes.length; i++) { + bytes[i] = data.get(i); + } + return parseBytesToInt(bytes, offset, length, bigEndian); + } + + public static int parseBytesToInt(byte[] data, int offset, int length) { + return parseBytesToInt(data, offset, length, true); + } + + public static int parseBytesToInt(byte[] data, int offset, int length, boolean bigEndian) { + if (offset > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " is out of bounds for array with length: " + data.length + "!"); + } + if (length > 4) { + throw new IllegalArgumentException("Length: " + length + " is too large. Maximum 4 bytes is allowed!"); + } + if (offset + length > data.length) { + throw new IllegalArgumentException("Offset: " + offset + " and Length: " + length + " is out of bounds for array with length: " + data.length + "!"); + } + var bb = ByteBuffer.allocate(4); + if (!bigEndian) { + bb.order(ByteOrder.LITTLE_ENDIAN); + } + bb.position(bigEndian ? 4 - length : 0); + bb.put(data, offset, length); + bb.position(0); + return bb.getInt(); + } + + public static String bytesToHex(ExecutionArrayList bytesList) { + byte[] bytes = new byte[bytesList.size()]; + for (int i = 0; i < bytesList.size(); i++) { + bytes[i] = Byte.parseByte(bytesList.get(i).toString()); + } + return bytesToHex(bytes); + } + + public static String bytesToHex(byte[] bytes) { + byte[] hexChars = new byte[bytes.length * 2]; + for (int j = 0; j < bytes.length; j++) { + int v = bytes[j] & 0xFF; + hexChars[j * 2] = HEX_ARRAY[v >>> 4]; + hexChars[j * 2 + 1] = HEX_ARRAY[v & 0x0F]; + } + return new String(hexChars, StandardCharsets.UTF_8); + } + + public static double toFixed(double value, int precision) { + return BigDecimal.valueOf(value).setScale(precision, RoundingMode.HALF_UP).doubleValue(); + } + + private static boolean isHexadecimal(String value) { + return value != null && (value.contains("0x") || value.contains("0X")); + } + + private static String prepareNumberString(String value) { + if (value != null) { + value = value.trim(); + if (isHexadecimal(value)) { + value = value.replace("0x", ""); + value = value.replace("0X", ""); + } + value = value.replace(",", "."); + } + return value; + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbelExecutionTask.java b/common/script/src/main/java/com/thing/api/mvel/TbelExecutionTask.java new file mode 100644 index 0000000..af6230e --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbelExecutionTask.java @@ -0,0 +1,21 @@ +package com.thing.api.mvel; + +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptExecutionTask; +import org.mvel2.ExecutionContext; + + +public class TbelExecutionTask extends ScriptExecutionTask { + + private final ExecutionContext context; + + public TbelExecutionTask(ExecutionContext context, ListenableFuture resultFuture) { + super(resultFuture); + this.context = context; + } + + @Override + public void stop() { + context.stop(); + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbelInvokeService.java b/common/script/src/main/java/com/thing/api/mvel/TbelInvokeService.java new file mode 100644 index 0000000..a22f13e --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbelInvokeService.java @@ -0,0 +1,21 @@ +package com.thing.api.mvel; + + +import com.thing.ScriptLanguage; +import com.thing.api.ScriptInvokeService; + +/** + * @author zhenghh. 2022-12-23 + **/ +public interface TbelInvokeService extends ScriptInvokeService { + + /** + * 编译语言 + * + * @return ScriptLanguage + */ + @Override + default ScriptLanguage getLanguage() { + return ScriptLanguage.MVEL; + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbelScript.java b/common/script/src/main/java/com/thing/api/mvel/TbelScript.java new file mode 100644 index 0000000..2e2c534 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbelScript.java @@ -0,0 +1,24 @@ +package com.thing.api.mvel; + +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +@Data +public class TbelScript { + + private final String scriptBody; + private final String[] argNames; + + public Map createVars(Object[] args) { + if (args == null || args.length != argNames.length) { + throw new IllegalArgumentException("Invalid number of argument values"); + } + var result = new HashMap<>(); + for (int i = 0; i < argNames.length; i++) { + result.put(argNames[i], args[i]); + } + return result; + } +} diff --git a/common/script/src/main/java/com/thing/api/mvel/TbelScriptEngine.java b/common/script/src/main/java/com/thing/api/mvel/TbelScriptEngine.java new file mode 100644 index 0000000..645cc95 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/mvel/TbelScriptEngine.java @@ -0,0 +1,111 @@ +package com.thing.api.mvel; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +import com.thing.ScriptMsg; +import com.thing.api.AbstractScriptEngine; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; + +import javax.script.ScriptException; +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +public class TbelScriptEngine extends AbstractScriptEngine implements Serializable { + @Serial private static final long serialVersionUID = 4614024318120950765L; + + public TbelScriptEngine(String scriptId, TbelInvokeService scriptInvokeService, ScriptLogService scriptLogService, + Boolean debug, String script, String... argNames) { + super(scriptId, scriptInvokeService, scriptLogService, debug, script, argNames); + } + + /** + * 转换string + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeToStringTransform(Object result) { + if (result instanceof String) { + return Futures.immediateFuture((String) result); + } else { + return Futures.immediateFuture(JacksonUtil.toString(result)); + } + } + + /** + * 转换Boolean + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeFilterTransform(Object result) { + if (result instanceof Boolean) { + return Futures.immediateFuture((Boolean) result); + } + return wrongResultType(result); + } + + /** + * 转换入参 + * + * @param msg 入参 + * @return Object[] + */ + @Override + protected Object[] prepareArgs(ScriptMsg msg) { + Object[] args = new Object[2]; + if (msg.getMsg() != null) { + args[0] = JacksonUtil.fromString(msg.getMsg(), Map.class); + } else { + args[0] = new HashMap<>(); + } + if (msg.getMetaData() != null) { + args[1] = JacksonUtil.convertValue(msg.getMetaData(), Map.class); + } else { + args[1] = new HashMap<>(); + } + return args; + } + + /** + * 转换 + * + * @param result 结果 + * @return 结果 + */ + @Override + protected Object convertResult(Object result) { + return result; + } + + /** + * 转换json + * + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(ScriptMsg msg) { + return Futures.transform(executeScriptAsync(msg), JacksonUtil::valueToTree, MoreExecutors.directExecutor()); + } + + private static ListenableFuture wrongResultType(Object result) { + String className = toClassName(result); + log.warn("Wrong result type: {}", className); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + className)); + } + + private static String toClassName(Object result) { + return result != null ? result.getClass().getSimpleName() : "null"; + } +} diff --git a/common/script/src/main/java/com/thing/api/nashorn/DefaultNashornInvokeService.java b/common/script/src/main/java/com/thing/api/nashorn/DefaultNashornInvokeService.java new file mode 100644 index 0000000..0f68763 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/nashorn/DefaultNashornInvokeService.java @@ -0,0 +1,129 @@ +package com.thing.api.nashorn; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; +import com.thing.CustomScriptException; +import com.thing.ScriptExecutionTask; +import com.thing.api.AbstractScriptInvokeService; +import com.thing.api.FunctionScriptFactory; +import com.thing.api.JavaInvokeService; +import com.thing.common.util.thread.ThingExecutors; + +import delight.nashornsandbox.NashornSandbox; +import delight.nashornsandbox.NashornSandboxes; + +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.ReentrantLock; + +import javax.script.ScriptException; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Slf4j +@Service +public class DefaultNashornInvokeService extends AbstractScriptInvokeService implements NashornInvokeService { + + protected final Map scriptInfoMap = new ConcurrentHashMap<>(); + + private NashornSandbox sandbox; + private ExecutorService monitorExecutorService; + private ListeningExecutorService jsExecutor; + + private final ReentrantLock evalLock = new ReentrantLock(); + + @Value("${nashorn.monitor_thread_pool_size}") + private int monitorThreadPoolSize; + + @Value("${nashorn.max_cpu_time}") + private long maxCpuTime; + + @Getter + @Value("${nashorn.max_requests_timeout:0}") + private long maxInvokeRequestsTimeout; + + @Value("${nashorn.js_thread_pool_size:50}") + private int jsExecutorThreadPoolSize; + + @Override + @PostConstruct + public void init() { + super.init(); + jsExecutor = MoreExecutors.listeningDecorator(Executors.newWorkStealingPool(jsExecutorThreadPoolSize)); + sandbox = NashornSandboxes.create(); + monitorExecutorService = ThingExecutors.newWorkStealingPool(monitorThreadPoolSize, "nashorn-js-monitor"); + sandbox.setExecutor(monitorExecutorService); + sandbox.setMaxCPUTime(maxCpuTime); + sandbox.allowNoBraces(false); + sandbox.allowLoadFunctions(true); + sandbox.setMaxPreparedStatements(30); + sandbox.allow(JavaInvokeService.class); + } + + @Override + @PreDestroy + public void stop() { + super.stop(); + if (monitorExecutorService != null) { + monitorExecutorService.shutdownNow(); + } + } + + @Override + protected boolean isScriptPresent(String scriptId) { + return scriptInfoMap.containsKey(scriptId); + } + + @Override + protected ListenableFuture doEvalScript(String scriptBody, String scriptId, String[] argNames) { + String functionName = "invokeInternal_" + scriptId.replace("-", "_"); + String jsScript = FunctionScriptFactory.generateScript(functionName, scriptBody, argNames); + NashornScript script = new NashornScript(scriptId, functionName); + return jsExecutor.submit(() -> { + try { + evalLock.lock(); + try { + sandbox.eval(jsScript); + } finally { + evalLock.unlock(); + } + scriptInfoMap.put(scriptId, script); + return scriptId; + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.COMPILATION, scriptBody, e); + } + }); + } + + @Override + protected ScriptExecutionTask doInvokeFunction(String scriptId, Object[] args) { + return new NashornExecutionTask(jsExecutor.submit(() -> { + try { + return sandbox.getSandboxedInvocable().invokeFunction(scriptInfoMap.get(scriptId).getFunctionName(), args); + } catch (ScriptException e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.RUNTIME, null, e); + } catch (Exception e) { + throw new CustomScriptException(scriptId, CustomScriptException.ErrorCode.OTHER, null, e); + } + })); + } + + @Override + protected void doRelease(String scriptId) throws Exception { + NashornScript script = scriptInfoMap.remove(scriptId); + sandbox.eval(script.getFunctionName() + " = undefined;"); + } +} diff --git a/common/script/src/main/java/com/thing/api/nashorn/NashornExecutionTask.java b/common/script/src/main/java/com/thing/api/nashorn/NashornExecutionTask.java new file mode 100644 index 0000000..2e1b889 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/nashorn/NashornExecutionTask.java @@ -0,0 +1,22 @@ +package com.thing.api.nashorn; + +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptExecutionTask; + +/** + * @author zhenghh. 2022-12-23 + **/ +public class NashornExecutionTask extends ScriptExecutionTask { + + protected NashornExecutionTask(ListenableFuture resultFuture) { + super(resultFuture); + } + + /** + * 停止 + */ + @Override + public void stop() { + + } +} diff --git a/common/script/src/main/java/com/thing/api/nashorn/NashornInvokeService.java b/common/script/src/main/java/com/thing/api/nashorn/NashornInvokeService.java new file mode 100644 index 0000000..0d582ed --- /dev/null +++ b/common/script/src/main/java/com/thing/api/nashorn/NashornInvokeService.java @@ -0,0 +1,20 @@ +package com.thing.api.nashorn; + +import com.thing.ScriptLanguage; +import com.thing.api.ScriptInvokeService; + +/** + * @author zhenghh. 2022-12-23 + **/ +public interface NashornInvokeService extends ScriptInvokeService { + + /** + * 编译语言 + * + * @return ScriptLanguage + */ + @Override + default ScriptLanguage getLanguage() { + return ScriptLanguage.NASHORN; + } +} diff --git a/common/script/src/main/java/com/thing/api/nashorn/NashornScript.java b/common/script/src/main/java/com/thing/api/nashorn/NashornScript.java new file mode 100644 index 0000000..3c2cbad --- /dev/null +++ b/common/script/src/main/java/com/thing/api/nashorn/NashornScript.java @@ -0,0 +1,13 @@ +package com.thing.api.nashorn; + +import lombok.Data; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Data +public class NashornScript { + + private final String scriptId; + private final String functionName; +} diff --git a/common/script/src/main/java/com/thing/api/nashorn/NashornScriptEngine.java b/common/script/src/main/java/com/thing/api/nashorn/NashornScriptEngine.java new file mode 100644 index 0000000..423a5b6 --- /dev/null +++ b/common/script/src/main/java/com/thing/api/nashorn/NashornScriptEngine.java @@ -0,0 +1,100 @@ +package com.thing.api.nashorn; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.thing.ScriptMsg; +import com.thing.api.AbstractScriptEngine; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; + +import javax.script.ScriptException; +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +/** + * @author zhenghh. 2022-12-23 + **/ +@Slf4j +public class NashornScriptEngine extends AbstractScriptEngine implements Serializable { + @Serial + private static final long serialVersionUID = -6530922207401963703L; + + public NashornScriptEngine(String scriptId, NashornInvokeService scriptInvokeService, ScriptLogService scriptLogService, + Boolean debug, String script, String... argNames) { + super(scriptId, scriptInvokeService, scriptLogService, debug, script, argNames); + } + + /** + * 转换string + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeToStringTransform(JsonNode result) { + if (result.isTextual() || result.isNumber()) { + return Futures.immediateFuture(result.asText()); + } + if(result.isBoolean()) { + return Futures.immediateFuture(String.valueOf(result.asBoolean())); + } + if(result.isObject()) { + return Futures.immediateFuture(JacksonUtil.toString(result)); + } + return Futures.immediateFuture(result.toString()); + } + + /** + * 转换Boolean + * + * @param result 结果 + * @return 结果 + */ + @Override + protected ListenableFuture executeFilterTransform(JsonNode result) { + if (result.isBoolean()) { + return Futures.immediateFuture(result.asBoolean()); + } + log.warn("Wrong result type: {}", result.getNodeType()); + return Futures.immediateFailedFuture(new ScriptException("Wrong result type: " + result.getNodeType())); + } + + /** + * 转换入参 + * + * @param msg 入参 + * @return Object[] + */ + @Override + protected Object[] prepareArgs(ScriptMsg msg) { + String[] args = new String[2]; + args[0] = Objects.isNull(msg.getMsg()) ? "" : msg.getMsg(); + args[1] = JacksonUtil.toString(msg.getMetaData()); + return args; + } + + /** + * 转换 + * + * @param result 结果 + * @return 结果 + */ + @Override + protected JsonNode convertResult(Object result) { + return JacksonUtil.toJsonNode(result != null ? result.toString() : null); + } + + /** + * 转换json + * + * @param msg 消息 + * @return JsonNode + */ + @Override + public ListenableFuture executeJsonAsync(ScriptMsg msg) { + return executeScriptAsync(msg); + } +} diff --git a/common/script/src/main/java/com/thing/modules/cache/bean/ScriptCache.java b/common/script/src/main/java/com/thing/modules/cache/bean/ScriptCache.java new file mode 100644 index 0000000..b7c8df1 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/cache/bean/ScriptCache.java @@ -0,0 +1,34 @@ +package com.thing.modules.cache.bean; + +import com.thing.ScriptLanguage; +import com.thing.api.ScriptEngine; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author zhenghh. 2022-12-27 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ScriptCache implements Serializable { + + @Serial + private static final long serialVersionUID = 8015194403355785494L; + + private String scriptId; + + private Long scriptEId; + + private String scriptType; + + private String scriptBody; + + private Boolean debug; + +// private ScriptEngine scriptEngine; +} diff --git a/common/script/src/main/java/com/thing/modules/cache/runner/ScriptCacheInitializer.java b/common/script/src/main/java/com/thing/modules/cache/runner/ScriptCacheInitializer.java new file mode 100644 index 0000000..8f0966c --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/cache/runner/ScriptCacheInitializer.java @@ -0,0 +1,58 @@ +package com.thing.modules.cache.runner; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; +import com.thing.modules.cache.bean.ScriptCache; +import com.thing.modules.cache.service.ScriptCacheService; +import com.thing.modules.dto.ScriptInfoDTO; +import com.thing.modules.service.ScriptInfoService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +/** + * @author zhenghh. 2022-12-27 + **/ + +@Slf4j +@Order(1) +@Component +@RequiredArgsConstructor +public class ScriptCacheInitializer extends AbstractCacheService { + + private final ScriptCacheService scriptCacheService; + private final ScriptInfoService scriptInfoService; + + @Override + public String getCacheName() { + return CacheNameEnum.SCRIPT_INFO; + } + + @Override + public void initCache() { + List list = scriptInfoService.allList(); + if (CollectionUtil.isEmpty(list)) { + return; + } + list.stream() + .map( + item -> { + try { + return new ScriptCache(String.valueOf(item.getId()),item.getId(), + item.getScriptType(), + item.getScriptBody(), + item.getDebug()); + } catch (Exception e) { + log.error("{} 解析失败: {}", item.getId(), e.getMessage()); + } + return null; + }) + .filter(Objects::nonNull) + .forEach(scriptCacheService::register); + } +} diff --git a/common/script/src/main/java/com/thing/modules/cache/service/ScriptCacheService.java b/common/script/src/main/java/com/thing/modules/cache/service/ScriptCacheService.java new file mode 100644 index 0000000..e263667 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/cache/service/ScriptCacheService.java @@ -0,0 +1,33 @@ +package com.thing.modules.cache.service; + +import com.thing.modules.cache.bean.ScriptCache; + +/** + * @author zhenghh. 2022-12-27 + **/ +public interface ScriptCacheService { + + /** + * 注册 + * + * @param cache 缓存 + * @return ScriptCache + */ + ScriptCache register(ScriptCache cache); + + /** + * 移除计算缓存 + * + * @param key 缓存key + */ + void unRegister(String key); + + /** + * 获取 + * + * @param key 缓存key + * @return ScriptCache + */ + ScriptCache get(String key); + +} diff --git a/common/script/src/main/java/com/thing/modules/cache/service/impl/ScriptCacheServiceImpl.java b/common/script/src/main/java/com/thing/modules/cache/service/impl/ScriptCacheServiceImpl.java new file mode 100644 index 0000000..4b41f29 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/cache/service/impl/ScriptCacheServiceImpl.java @@ -0,0 +1,51 @@ +package com.thing.modules.cache.service.impl; + +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.modules.cache.bean.ScriptCache; +import com.thing.modules.cache.service.ScriptCacheService; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +/** + * @author zhenghh. 2022-12-27 + **/ +@Service +public class ScriptCacheServiceImpl implements ScriptCacheService { + + /** + * 注册 + * + * @param cache 缓存 + * @return ScriptCache + */ + @Override + @CachePut(value = CacheNameEnum.SCRIPT_INFO, key = "#cache.scriptId") + public ScriptCache register(ScriptCache cache) { + return cache; + } + + /** + * 移除计算缓存 + * + * @param key 缓存key + */ + @Override + @CacheEvict(value = CacheNameEnum.SCRIPT_INFO, key = "#key") + public void unRegister(String key) { + + } + + /** + * 获取 + * + * @param key 缓存key + * @return ScriptCache + */ + @Override + @Cacheable(value = CacheNameEnum.SCRIPT_INFO, key = "#key") + public ScriptCache get(String key) { + return null; + } +} diff --git a/common/script/src/main/java/com/thing/modules/controller/ScriptInfoController.java b/common/script/src/main/java/com/thing/modules/controller/ScriptInfoController.java new file mode 100644 index 0000000..7b01b11 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/controller/ScriptInfoController.java @@ -0,0 +1,172 @@ +package com.thing.modules.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.ScriptLanguage; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.modules.dto.ScriptInfoDTO; +import com.thing.modules.dto.ScriptTypeDTO; +import com.thing.modules.entity.ScriptInfoEntity; +import com.thing.modules.excel.ScriptInfoExcel; +import com.thing.modules.service.ScriptInfoService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 数据解析 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@RestController +@RequestMapping("script/info") +@Tag(name = "数据解析") +public class ScriptInfoController { + @Autowired + private ScriptInfoService scriptInfoService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="名称"), + @Parameter(name = "type",description ="类型") + }) + public Result> page( @RequestParam Map params) { + PageData page = scriptInfoService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list() { + List data = scriptInfoService.allList(); + return new Result>().ok(data); + } + + @GetMapping("type") + @Operation(summary="类型") + public Result> type() { + List data = scriptInfoService.type(); + + return new Result>().ok(data); + } + + @PostMapping("debug") + @Operation(summary="调试") + public Result debug(@RequestBody JsonNode inputParams) { + JsonNode data = scriptInfoService.debug(inputParams); + + return new Result().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + ScriptInfoEntity data = scriptInfoService.getById(id); + return new Result().ok(data); + } + + @GetMapping("getExtendData/{id}") + @Operation(summary="信息") + public Result getExtendData(@PathVariable("id") Long id) { + JsonNode data = scriptInfoService.getExtendData(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody ScriptInfoDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + scriptInfoService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody ScriptInfoDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + scriptInfoService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + scriptInfoService.deleteDto(ids); + + return new Result(); + } + + @GetMapping("debug/{id}") + @Operation(summary="开启/关闭调试模式") + @LogOperation("开启/关闭调试模式") + public Result debugMode(@PathVariable Long id) { + scriptInfoService.debugMode(id); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + if (ids != null && ids.length > 0) { + params.put("ids", Arrays.asList(ids)); + } + List list = scriptInfoService.list(params); + + List collect = list.stream() + .map(item -> new ScriptInfoExcel(item.getName(), ScriptLanguage.get(item.getScriptType()).getAlias(), item.getScriptBody(), + item.getDebug() ? "是" : "否", item.getRemark())) + .collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "数据解析", null, ScriptInfoExcel.class, "数据解析.xls", response); + } + + @PostMapping("import") + @Operation(summary="导入excel") + @Parameter(name = "file",description ="文件") + public Result importExcel(@RequestParam("file") MultipartFile file) { + scriptInfoService.importExcel(file); + return new Result(); + } + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/controller/ScriptLogController.java b/common/script/src/main/java/com/thing/modules/controller/ScriptLogController.java new file mode 100644 index 0000000..8534cb1 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/controller/ScriptLogController.java @@ -0,0 +1,92 @@ +package com.thing.modules.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.modules.dto.ScriptLogDTO; +import com.thing.modules.excel.ScriptLogExcel; +import com.thing.modules.service.ScriptLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.thing.common.core.utils.DateTimeUtils.DATE_TIME_PATTERN; +import static com.thing.common.core.utils.DateTimeUtils.DATE_TIME_PATTERN_STR; + + +/** + * 数据解析日志 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@RestController +@RequestMapping("script/log") +@Tag(name = "数据解析日志") +public class ScriptLogController { + @Autowired + private ScriptLogService scriptLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "scriptId", description = "数据解析主键"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = scriptLogService.page(params); + + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + scriptLogService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("clear/{scriptId}") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear(@PathVariable Long scriptId) { + + scriptLogService.deleteByScriptId(scriptId); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = scriptLogService.list(params); + List collect = list.stream().map(item -> new ScriptLogExcel(DateTimeUtils.format(item.getCreateDate(),DATE_TIME_PATTERN_STR), + item.getMsg())).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "数据解析日志", null, ScriptLogExcel.class, "数据解析日志.xls", response); + } + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/dto/QueryMsg.java b/common/script/src/main/java/com/thing/modules/dto/QueryMsg.java new file mode 100644 index 0000000..71e51ce --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/dto/QueryMsg.java @@ -0,0 +1,40 @@ +package com.thing.modules.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @author zhenghh. 2023-03-09 + **/ +@Data +public class QueryMsg { + + /** + * 编号(A/B/C) + */ + @NotBlank(message = "查询参数编号不能为空", groups = DefaultGroup.class) + private String label; + /** + * 物编码 + */ + @NotBlank(message = "查询参数物编码不能为空", groups = DefaultGroup.class) + private String code; + /** + * 物属性 + */ + @NotBlank(message = "查询参数物属性不能为空", groups = DefaultGroup.class) + private String attr; + /** + * 默认值 (根据是否有值判断是否勾选默认值) + */ + private String defVal; + /** + * 是否同时间(勾选为 false, 不勾选为true) + */ + @NotNull(message = "查询参数同时间不能为空", groups = DefaultGroup.class) + private boolean last; + +} diff --git a/common/script/src/main/java/com/thing/modules/dto/ScriptInfoDTO.java b/common/script/src/main/java/com/thing/modules/dto/ScriptInfoDTO.java new file mode 100644 index 0000000..4613bcc --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/dto/ScriptInfoDTO.java @@ -0,0 +1,67 @@ +package com.thing.modules.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serializable; +import java.util.Date; + +/** +* 数据解析 +* +* @author zhh zhh +* @since 3.0 2022-12-27 +*/ +@Data +@Schema( name= "数据解析") +public class ScriptInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "名称") + @NotBlank(message="名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema(description = "语法类型") + @NotBlank(message="语法类型不能为空", groups = DefaultGroup.class) + private String scriptType; + @Schema(description = "方法体") + @NotBlank(message="方法体不能为空", groups = DefaultGroup.class) + private String scriptBody; + @Schema(description = "调试") + @NotNull(message="调试不能为空", groups = DefaultGroup.class) + private Boolean debug; + @Schema(description = "辅助参数") + private String supMsg; + @Schema(description = "查询参数") + private String queryMsg; + @Schema(description = "描述") + private String remark; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/dto/ScriptLogDTO.java b/common/script/src/main/java/com/thing/modules/dto/ScriptLogDTO.java new file mode 100644 index 0000000..afef14f --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/dto/ScriptLogDTO.java @@ -0,0 +1,39 @@ +package com.thing.modules.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 数据解析日志 +* +* @author zhh zhh +* @since 3.0 2022-12-27 +*/ +@Data +@Schema( name= "数据解析日志") +public class ScriptLogDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "数据解析主键") + private Long scriptId; + @Schema(description = "消息") + private String msg; + @Schema(description = "创建者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/dto/ScriptTypeDTO.java b/common/script/src/main/java/com/thing/modules/dto/ScriptTypeDTO.java new file mode 100644 index 0000000..8e46510 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/dto/ScriptTypeDTO.java @@ -0,0 +1,21 @@ +package com.thing.modules.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author zhenghh. 2022-12-27 + **/ +@Data +@AllArgsConstructor +@Schema( name= "数据解析类型") +public class ScriptTypeDTO { + + @Schema(description = "类型值") + private String value; + + @Schema(description = "类型名称") + private String name; +} diff --git a/common/script/src/main/java/com/thing/modules/entity/ScriptInfoEntity.java b/common/script/src/main/java/com/thing/modules/entity/ScriptInfoEntity.java new file mode 100644 index 0000000..af4fdfd --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/entity/ScriptInfoEntity.java @@ -0,0 +1,55 @@ +package com.thing.modules.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 数据解析 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("script_info") +public class ScriptInfoEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + /** + * 语法类型 + */ + private String scriptType; + /** + * 方法体 + */ + private String scriptBody; + /** + * 调试 + */ + private Boolean debug; + /** + * 辅助参数 + */ + private String supMsg; + /** + * 查询参数 + */ + private String queryMsg; + /** + * 描述 + */ + private String remark; + /** + * 扩展字段 + */ + private String extendData; + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/entity/ScriptLogEntity.java b/common/script/src/main/java/com/thing/modules/entity/ScriptLogEntity.java new file mode 100644 index 0000000..88ad24e --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/entity/ScriptLogEntity.java @@ -0,0 +1,31 @@ +package com.thing.modules.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 数据解析日志 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("script_log") +public class ScriptLogEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 数据解析主键 + */ + private Long scriptId; + /** + * 消息 + */ + private String msg; + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/excel/ScriptInfoExcel.java b/common/script/src/main/java/com/thing/modules/excel/ScriptInfoExcel.java new file mode 100644 index 0000000..8bc4ac4 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/excel/ScriptInfoExcel.java @@ -0,0 +1,28 @@ +package com.thing.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 数据解析 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ScriptInfoExcel { + @Excel(name = "名称") + private String name; + @Excel(name = "语法类型") + private String scriptType; + @Excel(name = "方法体") + private String scriptBody; + @Excel(name = "调试") + private String debug; + @Excel(name = "描述") + private String remark; +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/excel/ScriptLogExcel.java b/common/script/src/main/java/com/thing/modules/excel/ScriptLogExcel.java new file mode 100644 index 0000000..48972ee --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/excel/ScriptLogExcel.java @@ -0,0 +1,22 @@ +package com.thing.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 数据解析日志 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ScriptLogExcel { + @Excel(name = "调用时间") + private String createDate; + @Excel(name = "消息") + private String msg; +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/mapper/ScriptInfoMapper.java b/common/script/src/main/java/com/thing/modules/mapper/ScriptInfoMapper.java new file mode 100644 index 0000000..4bcf912 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/mapper/ScriptInfoMapper.java @@ -0,0 +1,16 @@ +package com.thing.modules.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.modules.entity.ScriptInfoEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 数据解析 +* +* @author zhh zhh +* @since 3.0 2022-12-27 +*/ +@Mapper +public interface ScriptInfoMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/mapper/ScriptLogMapper.java b/common/script/src/main/java/com/thing/modules/mapper/ScriptLogMapper.java new file mode 100644 index 0000000..868a849 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/mapper/ScriptLogMapper.java @@ -0,0 +1,16 @@ +package com.thing.modules.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.modules.entity.ScriptLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 数据解析日志 +* +* @author zhh zhh +* @since 3.0 2022-12-27 +*/ +@Mapper +public interface ScriptLogMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/service/ScriptInfoService.java b/common/script/src/main/java/com/thing/modules/service/ScriptInfoService.java new file mode 100644 index 0000000..62dc723 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/service/ScriptInfoService.java @@ -0,0 +1,101 @@ +package com.thing.modules.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.modules.dto.ScriptInfoDTO; +import com.thing.modules.dto.ScriptTypeDTO; +import com.thing.modules.entity.ScriptInfoEntity; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 数据解析 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +public interface ScriptInfoService extends IBaseService { + + /** + * 所有列表 + * + * @return list + */ + List allList(); + + /** + * 主键获取列表 + * + * @param ids 主键集合 + * @return list + */ + List selectBatchIds(List ids); + + /** + * 类型 + * + * @return list + */ + List type(); + + /** + * 获取扩展 + * + * @param id 主键 + * @return String + */ + JsonNode getExtendData(Long id); + + /** + * 调试 + * + * @param inputParams 入参 + * @return String + */ + JsonNode debug(JsonNode inputParams); + + /** + * 开启/关闭调试模式 + * + * @param id 主键 + */ + void debugMode(Long id); + + /** + * 批量保存 + * + * @param list 列表 + */ + void saveBatch(List list); + + /** + * 名称查询 + * + * @param calcNames 名称集合 + * @param tenantCode 租户编码 + * @param companyId 企业主键 + * @param deptId 部门主键 + * @return list + */ + List selectByName(List calcNames, Long tenantCode, Long companyId, Long deptId); + + + /** + * 导入 + * @return + */ + void importExcel(MultipartFile file); + + void saveDto(ScriptInfoDTO dto); + + void updateDto(ScriptInfoDTO dto); + + void deleteDto(Long[] ids); + + PageData page(Map params); + + List list(Map params); +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/service/ScriptLogService.java b/common/script/src/main/java/com/thing/modules/service/ScriptLogService.java new file mode 100644 index 0000000..3915826 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/service/ScriptLogService.java @@ -0,0 +1,43 @@ +package com.thing.modules.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.ScriptLog; +import com.thing.modules.dto.ScriptLogDTO; +import com.thing.modules.entity.ScriptLogEntity; + +import java.util.List; +import java.util.Map; + +/** + * 数据解析日志 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +public interface ScriptLogService extends IBaseService { + + /** + * 日志保存 + * @param scriptLog 日志 + */ + void saveLog(ScriptLog scriptLog); + + /** + * 清空 + * @param scriptId 数据解析主键 + */ + void deleteByScriptId(Long scriptId); + + /** + * 最新入参 + * @param scriptId 数据解析主键 + * @return String + */ + JsonNode getLastByScriptId(Long scriptId); + + PageData page(Map params); + + List list(Map params); +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/service/impl/ScriptInfoServiceImpl.java b/common/script/src/main/java/com/thing/modules/service/impl/ScriptInfoServiceImpl.java new file mode 100644 index 0000000..580e18a --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/service/impl/ScriptInfoServiceImpl.java @@ -0,0 +1,301 @@ +package com.thing.modules.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.api.ScriptEngine; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.modules.cache.bean.ScriptCache; +import com.thing.modules.cache.service.ScriptCacheService; +import com.thing.modules.dto.ScriptInfoDTO; +import com.thing.modules.dto.ScriptTypeDTO; +import com.thing.modules.entity.ScriptInfoEntity; +import com.thing.modules.excel.ScriptInfoExcel; +import com.thing.modules.mapper.ScriptInfoMapper; +import com.thing.modules.service.ScriptInfoService; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 数据解析 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Slf4j +@Service +public class ScriptInfoServiceImpl extends BaseServiceImpl implements ScriptInfoService { + + @Autowired + private ScriptCacheService scriptCacheService; + @Autowired + private ScriptCreateService scriptCreateService; + @Autowired + private ScriptLogService scriptLogService; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + String type = (String) params.get("type"); + List ids = (List) params.get("ids"); + QueryWrapper wrapper = new QueryWrapper(); + + wrapper.like( ScriptInfoEntity::getName, name,StringUtils.isNotBlank(name)) + .eq( ScriptInfoEntity::getScriptType, type,StringUtils.isNotBlank(type)); + wrapper.in(ScriptInfoEntity::getId, ids,CollectionUtil.isNotEmpty(ids)); + return wrapper; + } + + /** + * 主键获取列表 + * + * @param ids 主键集合 + * @return list + */ + @Override + public List selectBatchIds(List ids) { + return mapper.selectListByIds(ids); + } + + /** + * 所有列表 + * + * @return list + */ + @Override + public List allList() { + List list = mapper.selectAll(); + return ConvertUtils.sourceToTarget(list, ScriptInfoDTO.class); + } + + /** + * 类型 + * + * @return list + */ + @Override + public List type() { + return Arrays.stream(ScriptLanguage.values()) + .map(script -> new ScriptTypeDTO(script.name(), script.getAlias())) + .collect(Collectors.toList()); + } + + /** + * 获取扩展 + * + * @param id 主键 + * @return String + */ + @Override + public JsonNode getExtendData(Long id) { + JsonNode inMsg = scriptLogService.getLastByScriptId(id); + if (ObjectUtil.isNotNull(inMsg)) { + return inMsg; + } + ScriptInfoEntity entity = mapper.selectOneById(id); + return Objects.nonNull(entity) ? JacksonUtil.toJsonNode(entity.getExtendData()) : null; + } + + /** + * 调试 + * + * @param inputParams 入参 + * @return JsonNode + */ + @Override + public JsonNode debug(JsonNode inputParams) { + String type = inputParams.get("type").asText(); + String script = inputParams.get("script").asText(); + String msg = inputParams.get("msg").asText(); + JsonNode metadataJson = Objects.isNull(inputParams.get("metadata")) ? JacksonUtil.toJsonNode("{}") : JacksonUtil.toJsonNode(inputParams.get("metadata").asText()); + if (StringUtils.isBlank(type) || StringUtils.isBlank(script) || StringUtils.isBlank(msg)) { + throw new SysException("参数缺失"); + } + JsonNode result = scriptCreateService.executeJson(new SnowFlakeIDKeyGenerator().nextId(), ScriptLanguage.get(type), script, false, + msg, metadataJson.get("sup"), metadataJson.get("query"), true); + //修改入参 + updateExtendData(inputParams.get("id"), JacksonUtil.toJsonNode(msg), metadataJson); + return result; + } + + /** + * 调试入参保存 + * + * @param id 主键 + * @param msgJson 消息 + * @param metadataJson 元数据 + */ + private void updateExtendData(JsonNode id, JsonNode msgJson, JsonNode metadataJson) { + try { + if (Objects.nonNull(id) && !id.isNull()) { + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.set("msg", msgJson); + jsonNodes.set("metadata", metadataJson); + ScriptInfoEntity entity = new ScriptInfoEntity(); + entity.setExtendData(JacksonUtil.toString(jsonNodes)); + entity.setId(id.asLong()); + mapper.update(entity); + } + } catch (Exception e) { + log.error("debug出现问题: {}", e.getMessage()); + } + } + + /** + * 开启/关闭调试模式 + * + * @param id 主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void debugMode(Long id) { + ScriptInfoEntity dto = mapper.selectOneById(id); + if (Objects.isNull(dto)) { + throw new SysException("未找到数据"); + } + //取反 + dto.setDebug(BooleanUtils.isNotTrue(dto.getDebug())); + mapper.update(dto); + ScriptInfoDTO scriptInfoDTO =ConvertUtils.convertWithTypeAdapt(dto,ScriptInfoDTO.class); + saveCache(scriptInfoDTO); + } + + /** + * 批量保存 + * + * @param list 列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveBatch(List list) { + mapper.insertBatch(list); + for (ScriptInfoEntity entity : list) { + saveCache(ConvertUtils.sourceToTarget(entity, ScriptInfoDTO.class)); + } + } + + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveDto(ScriptInfoDTO dto) { + if (!ScriptLanguage.contains(dto.getScriptType())) { + throw new SysException("不支持的类型:" + dto.getScriptType()); + } + mapper.insert(ConvertUtils.sourceToTarget(dto,ScriptInfoEntity.class)); + saveCache(dto); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateDto(ScriptInfoDTO dto) { + if (!ScriptLanguage.contains(dto.getScriptType())) { + throw new SysException("不支持的类型:" + dto.getScriptType()); + } + super.updateDto(dto); + saveCache(dto); + } + + /** + * 保存缓存 + * + * @param dto 数据解析 + */ + private void saveCache(ScriptInfoDTO dto) { + try { + scriptCacheService.register(new ScriptCache(String.valueOf(dto.getId()),dto.getId(),dto.getScriptType(), dto.getScriptBody(), dto.getDebug())); + } catch (Exception e) { + log.error("{} 解析失败: {}", dto.getId(), e.getMessage()); + throw new SysException("解析失败, 请检查"); + } + } + + @Override + public void deleteDto(Long[] ids) { + mapper.deleteBatchByIds(Arrays.stream(ids).toList()); + for (Long id : ids) { + String cacheId = String.valueOf(id); + scriptCacheService.unRegister(cacheId); + } + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, ScriptInfoDTO.class); + + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params),ScriptInfoDTO.class); + } + + @Override + public List selectByName(List calcNames, Long tenantCode, Long companyId, Long deptId) { + if (CollectionUtil.isEmpty(calcNames)) { + return Lists.newArrayList(); + } + return mapper.selectListByQuery(QueryWrapper.create().in(ScriptInfoEntity::getName, calcNames) + .eq( ScriptInfoEntity::getTenantCode, tenantCode,tenantCode != null) + .eq( ScriptInfoEntity::getCompanyId, companyId,companyId != null) + .eq( ScriptInfoEntity::getDeptId, deptId,deptId != null)); + } + + @Override + public void importExcel(MultipartFile file) { + List sheetData = ExcelUtils.importExcel(file, 1, 1, ScriptInfoExcel.class, 0); + if (CollectionUtil.isEmpty(sheetData)) { + throw new SysException("导入数据为空"); + } + List collect = sheetData.parallelStream().filter(scriptInfoExcel -> ObjectUtil.isNull(scriptInfoExcel.getName()) || ObjectUtil.isNull(scriptInfoExcel.getScriptType())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(collect)) { + throw new SysException("导入数据解析类型或者解析名称不能为空"); + } + + List existsExtList = mapper.selectListExt(QueryWrapper.create().in(ScriptInfoEntity::getName, collect.stream().map(ScriptInfoExcel::getName).collect(Collectors.toList()))); + if (CollectionUtil.isNotEmpty(existsExtList)) { + String result = existsExtList.stream().map(ScriptInfoEntity::getName).collect(Collectors.joining(",")); + throw new SysException(result + "数据解析名称已经存在"); + } + //去重 + List distinctStudentFile = sheetData.stream() + .collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getName()))), ArrayList::new)); + List list = Lists.newArrayList(); + for (ScriptInfoExcel scriptInfoExcel : distinctStudentFile) { + String name = scriptInfoExcel.getName(); + boolean existsExt = mapper.existsExt(QueryWrapper.create().eq(ScriptInfoEntity::getName, name)); + if (existsExt) { + continue; + } + ScriptInfoEntity scriptInfoEntity = ConvertUtils.sourceToTarget(scriptInfoExcel, ScriptInfoEntity.class); + list.add(scriptInfoEntity); + } + if (CollectionUtil.isNotEmpty(list)) { + this.saveBatch(list); + } + } +} \ No newline at end of file diff --git a/common/script/src/main/java/com/thing/modules/service/impl/ScriptLogServiceImpl.java b/common/script/src/main/java/com/thing/modules/service/impl/ScriptLogServiceImpl.java new file mode 100644 index 0000000..4ad9c46 --- /dev/null +++ b/common/script/src/main/java/com/thing/modules/service/impl/ScriptLogServiceImpl.java @@ -0,0 +1,107 @@ +package com.thing.modules.service.impl; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.ScriptLog; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.modules.mapper.ScriptLogMapper; +import com.thing.modules.dto.ScriptLogDTO; +import com.thing.modules.entity.ScriptLogEntity; +import com.thing.modules.service.ScriptLogService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 数据解析日志 + * + * @author zhh zhh + * @since 3.0 2022-12-27 + */ +@Slf4j +@Service +public class ScriptLogServiceImpl extends BaseServiceImpl implements ScriptLogService { + @Override + public QueryWrapper getWrapper(Map params) { + String scriptId = (String) params.get("scriptId"); + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(ScriptLogEntity::getScriptId, StringUtils.isNotBlank(scriptId) ? Long.parseLong(scriptId) : -1L) + .ge( ScriptLogEntity::getCreateDate, startTime,StringUtils.isNotBlank(startTime)) + .le(ScriptLogEntity::getCreateDate, endTime,StringUtils.isNotBlank(endTime)); + return wrapper; + } + + /** + * 日志保存 + * + * @param scriptLog 日志 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveLog(ScriptLog scriptLog) { + ScriptLogEntity entity = new ScriptLogEntity(); + entity.setScriptId(Long.parseLong(scriptLog.getScriptId())); + entity.setMsg(JacksonUtil.toString(scriptLog)); + mapper.insert(entity); + } + + /** + * 清空 + * + * @param scriptId 数据解析主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByScriptId(Long scriptId) { + if (Objects.isNull(scriptId)) { + return; + } + mapper.deleteByQuery(QueryWrapper.create().eq(ScriptLogEntity::getScriptId, scriptId)); + } + + /** + * 最新入参 + * + * @param scriptId 数据解析主键 + * @return JsonNode + */ + @Override + public JsonNode getLastByScriptId(Long scriptId) { + try { + ScriptLogEntity entity = mapper.selectOneByQuery(QueryWrapper.create().eq(ScriptLogEntity::getScriptId, scriptId) + .orderBy(ScriptLogEntity::getCreateDate).desc().offset(1)); + if(Objects.nonNull(entity)) { + JsonNode jsonNode = JacksonUtil.toJsonNode(entity.getMsg()); + return jsonNode.get("inMsg"); + } + } catch (Exception e) { + log.error("获取最新日志失败:{}", e.getMessage()); + } + return null; + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, ScriptLogDTO.class); + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params), ScriptLogDTO.class); + } +} \ No newline at end of file diff --git a/common/script/src/main/resources/script/ScriptInfoMapper.xml b/common/script/src/main/resources/script/ScriptInfoMapper.xml new file mode 100644 index 0000000..d7e3d65 --- /dev/null +++ b/common/script/src/main/resources/script/ScriptInfoMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/script/src/main/resources/script/ScriptLogMapper.xml b/common/script/src/main/resources/script/ScriptLogMapper.xml new file mode 100644 index 0000000..5a92f5c --- /dev/null +++ b/common/script/src/main/resources/script/ScriptLogMapper.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/security/pom.xml b/common/security/pom.xml new file mode 100644 index 0000000..d2dfa40 --- /dev/null +++ b/common/security/pom.xml @@ -0,0 +1,139 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + + com.thing.common + security + jar + ThingBI Server Common Security + + + ${basedir}/.. + 17 + 17 + UTF-8 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + + + + + + + + + + + + + + + + + + + + + + + + + + org.jsoup + jsoup + + + + + org.apache.shiro + shiro-spring + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + + + + org.apache.shiro + shiro-core + jakarta + ${shiro.version} + + + commons-collections + commons-collections + + + + + + org.apache.shiro + shiro-web + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-core + + + + + + + + + + + org.apache.commons + commons-lang3 + + + + org.apache.httpcomponents + httpcore + + + + + + + + + com.thing.common + cache + + + + com.thing.common + core + + + + + + + + \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/cache/SecurityCache.java b/common/security/src/main/java/com/thing/cache/SecurityCache.java new file mode 100644 index 0000000..0e278ae --- /dev/null +++ b/common/security/src/main/java/com/thing/cache/SecurityCache.java @@ -0,0 +1,69 @@ +package com.thing.cache; + +import com.thing.common.cache.config.LocalCacheConfig; +import com.thing.common.cache.config.RedisConfig; +import com.thing.common.core.utils.SpringContextUtils; +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.springframework.cache.CacheManager; + +import java.util.Collection; +import java.util.Objects; +import java.util.Set; + +/** + * Author: SiYang + * Date: 2023/12/15 15:15 + * Description: 认证授权缓存 + */ +public class SecurityCache implements Cache { + + /** + * 常量解释: + * cacheManager为{@link LocalCacheConfig}或者{@link RedisConfig}中定义的bean + * security 为 cache模块中注入的一个Cache的名称,该名称维护在 application-cache.yml 文件中 + */ + @Override + public V get(K k) throws CacheException { + CacheManager cacheManager = SpringContextUtils.getBean("cacheManager", CacheManager.class); + org.springframework.cache.Cache cache = + Objects.requireNonNull(cacheManager.getCache("security")); + org.springframework.cache.Cache.ValueWrapper valueWrapper = cache.get(k); + if (Objects.nonNull(valueWrapper)) { + return (V) valueWrapper.get(); + } + return null; + } + + @Override + public V put(K k, V v) throws CacheException { + CacheManager cacheManager = SpringContextUtils.getBean("cacheManager", CacheManager.class); + org.springframework.cache.Cache cache = + Objects.requireNonNull(cacheManager.getCache("security")); + cache.put(k, v); + return v; + } + + @Override + public V remove(K k) throws CacheException { + return null; + } + + @Override + public void clear() throws CacheException {} + + @Override + public int size() { + return 0; + } + + @Override + public Set keys() { + return null; + } + + @Override + public Collection values() { + return null; + } +} diff --git a/common/security/src/main/java/com/thing/cache/SecurityCacheManager.java b/common/security/src/main/java/com/thing/cache/SecurityCacheManager.java new file mode 100644 index 0000000..4f56fc8 --- /dev/null +++ b/common/security/src/main/java/com/thing/cache/SecurityCacheManager.java @@ -0,0 +1,18 @@ +package com.thing.cache; + +import org.apache.shiro.cache.Cache; +import org.apache.shiro.cache.CacheException; +import org.apache.shiro.cache.CacheManager; + +/** + * Author: SiYang + * Date: 2023/12/15 15:10 + * Description: 认证授权的缓存管理器, + */ +public class SecurityCacheManager implements CacheManager { + + @Override + public Cache getCache(String cacheName) throws CacheException { + return new SecurityCache<>(); + } +} diff --git a/common/security/src/main/java/com/thing/config/FilterConfig.java b/common/security/src/main/java/com/thing/config/FilterConfig.java new file mode 100644 index 0000000..a3b35e3 --- /dev/null +++ b/common/security/src/main/java/com/thing/config/FilterConfig.java @@ -0,0 +1,45 @@ + + +package com.thing.config; + + +import com.thing.xss.XssFilter; +import jakarta.servlet.DispatcherType; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.filter.DelegatingFilterProxy; + +/** + * Filter配置 + * + * @author Mark sunlightcs@gmail.com + */ +@Configuration +public class FilterConfig { + + @Bean + public FilterRegistrationBean shiroFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setFilter(new DelegatingFilterProxy("shiroFilter")); + //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 + registration.addInitParameter("targetFilterLifecycle", "true"); + registration.setEnabled(true); + registration.setOrder(Integer.MAX_VALUE - 100); + registration.addUrlPatterns("/*"); + //shiro增加异步支持 + registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC); + return registration; + } + + @Bean + public FilterRegistrationBean xssFilterRegistration() { + FilterRegistrationBean registration = new FilterRegistrationBean(); + registration.setDispatcherTypes(DispatcherType.REQUEST); + registration.setFilter(new XssFilter()); + registration.addUrlPatterns("/*"); + registration.setName("xssFilter"); + registration.setOrder(Integer.MAX_VALUE); + return registration; + } +} diff --git a/common/security/src/main/java/com/thing/config/ShiroConfig.java b/common/security/src/main/java/com/thing/config/ShiroConfig.java new file mode 100644 index 0000000..15f3f75 --- /dev/null +++ b/common/security/src/main/java/com/thing/config/ShiroConfig.java @@ -0,0 +1,120 @@ + + +package com.thing.config; + +import com.thing.oauth2.Oauth2Filter; +import jakarta.servlet.Filter; +import org.apache.shiro.mgt.SecurityManager; +import org.apache.shiro.spring.LifecycleBeanPostProcessor; +import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; +import org.apache.shiro.spring.web.ShiroFilterFactoryBean; +import org.apache.shiro.web.session.mgt.DefaultWebSessionManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Shiro的配置文件 + * + * @author Mark sunlightcs@gmail.com + */ +@Configuration +public class ShiroConfig { + + @Bean + public DefaultWebSessionManager sessionManager() { + DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); + sessionManager.setSessionValidationSchedulerEnabled(false); + sessionManager.setSessionIdUrlRewritingEnabled(false); + + return sessionManager; + } + + @Bean("shiroFilter") + public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { + ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); + shiroFilter.setSecurityManager(securityManager); + + //oauth过滤 + Map filters = new HashMap<>(); + filters.put("oauth2", new Oauth2Filter()); + shiroFilter.setFilters(filters); + + Map filterMap = new LinkedHashMap<>(); + filterMap.put("/webjars/**", "anon"); + filterMap.put("/druid/**", "anon"); + filterMap.put("/login", "anon"); + filterMap.put("/login/noCaptcha", "anon"); + filterMap.put("/title", "anon"); + filterMap.put("/sign", "anon"); + filterMap.put("/support", "anon"); + filterMap.put("/v2/manage/clearCache", "anon"); + filterMap.put("/logo/*", "anon"); + filterMap.put("/v3/api-docs/**", "anon"); + filterMap.put("/swagger-resources", "anon"); + filterMap.put("/swagger-ui/**", "anon"); + filterMap.put("/swagger-ui.html", "anon"); + filterMap.put("/doc.html", "anon"); + + + filterMap.put("/v2/sys/industry/type/**", "anon"); + filterMap.put("/sys/region/tree", "anon"); + filterMap.put("/sys/tenant/detail/upload", "anon"); + filterMap.put("/sys/tenant/detail/list", "anon"); + filterMap.put("/v2/enterprisee/**", "anon"); + + filterMap.put("/sys/user/bindOpenId", "anon"); + filterMap.put("/preview/**", "anon"); + filterMap.put("/dashboard/iotdashboard/listShare/**", "anon"); + filterMap.put("/dashboard/iotdashboard/svg/xml/**", "anon"); + filterMap.put("/dashboard/getDashboardElementLatestAttr", "anon"); + filterMap.put("/office/**", "anon"); + filterMap.put("/dashboard/websocket", "anon"); + filterMap.put("/dashboard/iotdashboardelement/getElementByDashboardId", "anon"); + filterMap.put("/iotconfigurationdesig/getDetailByBoardManageId", "anon"); + filterMap.put("/section/iotsectiondetail/queryListByName", "anon"); + filterMap.put("/v2/api/telemetryById", "anon"); + filterMap.put("/websocket", "anon"); + filterMap.put("/device/control/control", "anon"); + filterMap.put("/app/rest/**", "anon"); + filterMap.put("/views/**", "anon"); + filterMap.put("/static/**", "anon"); + filterMap.put("/bootstrap/**", "anon"); + filterMap.put("/bootstrap-table/**", "anon"); + filterMap.put("/css/**", "anon"); + filterMap.put("/gitalk/**", "anon"); + filterMap.put("/images/**", "anon"); + filterMap.put("/js/**", "anon"); + filterMap.put("/ofd/**", "anon"); + filterMap.put("/pdfjs/**", "anon"); + filterMap.put("/plyr/**", "anon"); + filterMap.put("/actuator/prometheus", "anon"); + + // 国网公共数据api接口:shiro放行,交由其他过滤器拦截验证 + filterMap.put("/v2/carbon/pub/**", "anon"); + + filterMap.put("/captcha", "anon"); + filterMap.put("/favicon.ico", "anon"); + filterMap.put("/**", "oauth2"); + shiroFilter.setFilterChainDefinitionMap(filterMap); + + return shiroFilter; + } + + + @Bean("lifecycleBeanPostProcessor") + public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { + return new LifecycleBeanPostProcessor(); + } + + @Bean + public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { + AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); + advisor.setSecurityManager(securityManager); + return advisor; + } + +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/config/WebMvcConfig.java b/common/security/src/main/java/com/thing/config/WebMvcConfig.java new file mode 100644 index 0000000..3c9cdbb --- /dev/null +++ b/common/security/src/main/java/com/thing/config/WebMvcConfig.java @@ -0,0 +1,74 @@ + + +package com.thing.config; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.ByteArrayHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.ResourceHttpMessageConverter; +import org.springframework.http.converter.StringHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.List; +import java.util.TimeZone; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOriginPatterns("*") + .allowCredentials(true) + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .maxAge(3600); + } + + @Override + public void configureMessageConverters(List> converters) { + converters.add(new ByteArrayHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(new ResourceHttpMessageConverter()); + converters.add(new AllEncompassingFormHttpMessageConverter()); + converters.add(new StringHttpMessageConverter()); + converters.add(jackson2HttpMessageConverter()); + } + + @Bean + public MappingJackson2HttpMessageConverter jackson2HttpMessageConverter() { + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(); + ObjectMapper mapper = new ObjectMapper(); + + //忽略未知属性 + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); + + //日期格式转换 + //mapper.setDateFormat(new SimpleDateFormat(DateUtils.DATE_TIME_PATTERN)); + mapper.setTimeZone(TimeZone.getTimeZone("GMT+8")); + + //Long类型转String类型 + SimpleModule simpleModule = new SimpleModule(); + simpleModule.addSerializer(Long.class, ToStringSerializer.instance); + simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance); + mapper.registerModule(simpleModule); + + converter.setObjectMapper(mapper); + return converter; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/"); + registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); + } + +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/oauth2/AbstractOauth2Realm.java b/common/security/src/main/java/com/thing/oauth2/AbstractOauth2Realm.java new file mode 100644 index 0000000..5835e2d --- /dev/null +++ b/common/security/src/main/java/com/thing/oauth2/AbstractOauth2Realm.java @@ -0,0 +1,31 @@ +package com.thing.oauth2; + +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.realm.AuthorizingRealm; +import org.apache.shiro.subject.PrincipalCollection; + +public abstract class AbstractOauth2Realm extends AuthorizingRealm { + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof Oauth2Token; + } + + /** + * 认证(登录时调用) + */ + @Override + protected abstract AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException; + + + /** + * 授权(验证权限时调用) + */ + @Override + protected abstract AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals); + +} + diff --git a/common/security/src/main/java/com/thing/oauth2/Oauth2Filter.java b/common/security/src/main/java/com/thing/oauth2/Oauth2Filter.java new file mode 100644 index 0000000..674964e --- /dev/null +++ b/common/security/src/main/java/com/thing/oauth2/Oauth2Filter.java @@ -0,0 +1,96 @@ +package com.thing.oauth2; + +import com.google.gson.Gson; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.http.HttpStatus; +import org.apache.shiro.authc.AuthenticationException; +import org.apache.shiro.authc.AuthenticationToken; +import org.apache.shiro.web.filter.authc.AuthenticatingFilter; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.IOException; + +/** + * oauth2过滤器 + * + * @author Mark sunlightcs@gmail.com + */ +public class Oauth2Filter extends AuthenticatingFilter { + + @Override + protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) { + //获取请求token + String token = getRequestToken((HttpServletRequest) request); + + if (StringUtils.isBlank(token)) { + return null; + } + + return new Oauth2Token(token); + } + + @Override + protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { + return ((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name()); + } + + @Override + protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { + //获取请求token,如果token不存在,直接返回401 + String token = getRequestToken((HttpServletRequest) request); + if (StringUtils.isBlank(token)) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); + + String json = new Gson().toJson(new Result<>().error(ErrorCode.UNAUTHORIZED)); + + httpResponse.getWriter().print(json); + + return false; + } + + return executeLogin(request, response); + } + + @Override + protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { + HttpServletResponse httpResponse = (HttpServletResponse) response; + httpResponse.setContentType("application/json;charset=utf-8"); + httpResponse.setHeader("Access-Control-Allow-Credentials", "true"); + httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtils.getOrigin()); + try { + //处理登录失败的异常 + Throwable throwable = e.getCause() == null ? e : e.getCause(); + String json = new Gson().toJson(new Result<>().error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage())); + httpResponse.getWriter().print(json); + } catch (IOException ignore) { + } + + return false; + } + + /** + * 获取请求的token + */ + private String getRequestToken(HttpServletRequest httpRequest) { + //从header中获取token + String token = httpRequest.getHeader(Constant.TOKEN_HEADER); + + //如果header中不存在token,则从参数中获取token + if (StringUtils.isBlank(token)) { + token = httpRequest.getParameter(Constant.TOKEN_HEADER); + } + + return token; + } +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/oauth2/Oauth2Token.java b/common/security/src/main/java/com/thing/oauth2/Oauth2Token.java new file mode 100644 index 0000000..7fdf145 --- /dev/null +++ b/common/security/src/main/java/com/thing/oauth2/Oauth2Token.java @@ -0,0 +1,28 @@ + + +package com.thing.oauth2; + +import org.apache.shiro.authc.AuthenticationToken; + +/** + * token + * + * @author Mark sunlightcs@gmail.com + */ +public class Oauth2Token implements AuthenticationToken { + private final String token; + + public Oauth2Token(String token){ + this.token = token; + } + + @Override + public String getPrincipal() { + return token; + } + + @Override + public Object getCredentials() { + return token; + } +} diff --git a/common/security/src/main/java/com/thing/password/BCrypt.java b/common/security/src/main/java/com/thing/password/BCrypt.java new file mode 100644 index 0000000..5d23ad5 --- /dev/null +++ b/common/security/src/main/java/com/thing/password/BCrypt.java @@ -0,0 +1,657 @@ +package com.thing.password; + +import java.io.ByteArrayOutputStream; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; + +/** + * BCrypt implements OpenBSD-style Blowfish password hashing using the scheme described in + * "A Future-Adaptable Password Scheme" by Niels Provos and David Mazieres. + *

+ * This password hashing system tries to thwart off-line password cracking using a + * computationally-intensive hashing algorithm, based on Bruce Schneier's Blowfish cipher. + * The work factor of the algorithm is parameterised, so it can be increased as computers + * get faster. + *

+ * Usage is really simple. To hash a password for the first time, call the hashpw method + * with a random salt, like this: + *

+ * + * String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt());
+ *
+ *

+ * To check whether a plaintext password matches one that has been hashed previously, use + * the checkpw method: + *

+ * + * if (BCrypt.checkpw(candidate_password, stored_hash))
+ *     System.out.println("It matches");
+ * else
+ *     System.out.println("It does not match");
+ *
+ *

+ * The gensalt() method takes an optional parameter (log_rounds) that determines the + * computational complexity of the hashing: + *

+ * + * String strong_salt = BCrypt.gensalt(10)
+ * String stronger_salt = BCrypt.gensalt(12)
+ *
+ *

+ * The amount of work increases exponentially (2**log_rounds), so each increment is twice + * as much work. The default log_rounds is 10, and the valid range is 4 to 31. + * + * @author Damien Miller + */ +public class BCrypt { + // BCrypt parameters + + private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10; + private static final int BCRYPT_SALT_LEN = 16; + // Blowfish parameters + private static final int BLOWFISH_NUM_ROUNDS = 16; + // Initial contents of key schedule + private static final int[] P_orig = { 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, + 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, + 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b }; + private static final int[] S_orig = { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, + 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, + 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, + 0x9c30d539, 0x2af26013, 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, + 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, + 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, + 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, + 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, + 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, + 0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, + 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, + 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, + 0x7d84a5c3, 0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, + 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, + 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, + 0x5e5c9ec2, 0x196a2463, 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, + 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, + 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, + 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, + 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, + 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, + 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, + 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, + 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, + 0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, + 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, + 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, + 0xc75442f5, 0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, + 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, + 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, + 0x1b510052, 0x9a532915, 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, + 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, + 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, + 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, + 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, + 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 0x96eb27b3, 0x55fd3941, + 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, + 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, + 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 0x1dc9faf7, 0x4b6d1856, + 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, + 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, + 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802, 0x04272f70, + 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, + 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, + 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1, + 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, + 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, + 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, + 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, + 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, + 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 0x6a124237, 0xb79251e7, + 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, + 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, + 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 0x83426b33, 0xf01eab71, + 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, + 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, + 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1, 0xbb8205d0, + 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, + 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, + 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641, + 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, + 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, + 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, + 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, + 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, + 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, + 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, + 0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, + 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, + 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, + 0x3f046f69, 0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, + 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, + 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, + 0xf8d56629, 0x79132e28, 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, + 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, + 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, + 0xabcc5167, 0xccad925f, 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, + 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, + 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, + 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, + 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, + 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, + 0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, + 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, + 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, + 0x8df9317c, 0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, + 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, + 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, + 0xd3a0342b, 0x8971f21e, 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, + 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, + 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, + 0x6e163697, 0x88d273cc, 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, + 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, + 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, + 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, + 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, + 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 }; + // bcrypt IV: "OrpheanBeholderScryDoubt" + static private final int[] bf_crypt_ciphertext = { 0x4f727068, 0x65616e42, + 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 }; + // Table for Base64 encoding + static private final char[] base64_code = { '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', + 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', + 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' }; + // Table for Base64 decoding + static private final byte[] index_64 = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, -1, 2, 3, 4, 5, 6, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, + -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1 }; + static final int MIN_LOG_ROUNDS = 4; + static final int MAX_LOG_ROUNDS = 31; + // Expanded Blowfish key + private int[] P; + private int[] S; + + /** + * Encode a byte array using bcrypt's slightly-modified base64 encoding scheme. Note + * that this is not compatible with the standard MIME-base64 + * encoding. + * + * @param d the byte array to encode + * @param len the number of bytes to encode + * @param rs the destination buffer for the base64-encoded string + * @exception IllegalArgumentException if the length is invalid + */ + static void encode_base64(byte[] d, int len, StringBuilder rs) + throws IllegalArgumentException { + int off = 0; + int c1, c2; + + if (len <= 0 || len > d.length) { + throw new IllegalArgumentException("Invalid len"); + } + + while (off < len) { + c1 = d[off++] & 0xff; + rs.append(base64_code[(c1 >> 2) & 0x3f]); + c1 = (c1 & 0x03) << 4; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 4) & 0x0f; + rs.append(base64_code[c1 & 0x3f]); + c1 = (c2 & 0x0f) << 2; + if (off >= len) { + rs.append(base64_code[c1 & 0x3f]); + break; + } + c2 = d[off++] & 0xff; + c1 |= (c2 >> 6) & 0x03; + rs.append(base64_code[c1 & 0x3f]); + rs.append(base64_code[c2 & 0x3f]); + } + } + + /** + * Look up the 3 bits base64-encoded by the specified character, range-checking + * against conversion table + * @param x the base64-encoded value + * @return the decoded value of x + */ + private static byte char64(char x) { + if (x > index_64.length) { + return -1; + } + return index_64[x]; + } + + /** + * Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that + * this is *not* compatible with the standard MIME-base64 encoding. + * @param s the string to decode + * @param maxolen the maximum number of bytes to decode + * @return an array containing the decoded bytes + * @throws IllegalArgumentException if maxolen is invalid + */ + static byte[] decode_base64(String s, int maxolen) throws IllegalArgumentException { + ByteArrayOutputStream out = new ByteArrayOutputStream(maxolen); + int off = 0, slen = s.length(), olen = 0; + byte c1, c2, c3, c4, o; + + if (maxolen <= 0) { + throw new IllegalArgumentException("Invalid maxolen"); + } + + while (off < slen - 1 && olen < maxolen) { + c1 = char64(s.charAt(off++)); + c2 = char64(s.charAt(off++)); + if (c1 == -1 || c2 == -1) { + break; + } + o = (byte) (c1 << 2); + o |= (c2 & 0x30) >> 4; + out.write(o); + if (++olen >= maxolen || off >= slen) { + break; + } + c3 = char64(s.charAt(off++)); + if (c3 == -1) { + break; + } + o = (byte) ((c2 & 0x0f) << 4); + o |= (c3 & 0x3c) >> 2; + out.write(o); + if (++olen >= maxolen || off >= slen) { + break; + } + c4 = char64(s.charAt(off++)); + o = (byte) ((c3 & 0x03) << 6); + o |= c4; + out.write(o); + ++olen; + } + + return out.toByteArray(); + } + + /** + * Blowfish encipher a single 64-bit block encoded as two 32-bit halves + * @param lr an array containing the two 32-bit half blocks + * @param off the position in the array of the blocks + */ + private final void encipher(int[] lr, int off) { + int i, n, l = lr[off], r = lr[off + 1]; + + l ^= P[0]; + for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) { + // Feistel substitution on left word + n = S[(l >> 24) & 0xff]; + n += S[0x100 | ((l >> 16) & 0xff)]; + n ^= S[0x200 | ((l >> 8) & 0xff)]; + n += S[0x300 | (l & 0xff)]; + r ^= n ^ P[++i]; + + // Feistel substitution on right word + n = S[(r >> 24) & 0xff]; + n += S[0x100 | ((r >> 16) & 0xff)]; + n ^= S[0x200 | ((r >> 8) & 0xff)]; + n += S[0x300 | (r & 0xff)]; + l ^= n ^ P[++i]; + } + lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; + lr[off + 1] = l; + } + + /** + * Cycically extract a word of key material + * @param data the string to extract the data from + * @param offp a "pointer" (as a one-entry array) to the current offset into data + * @return the next word of material from data + */ + private static int streamtoword(byte[] data, int[] offp) { + int i; + int word = 0; + int off = offp[0]; + + for (i = 0; i < 4; i++) { + word = (word << 8) | (data[off] & 0xff); + off = (off + 1) % data.length; + } + + offp[0] = off; + return word; + } + + /** + * Initialise the Blowfish key schedule + */ + private void init_key() { + P = (int[]) P_orig.clone(); + S = (int[]) S_orig.clone(); + } + + /** + * Key the Blowfish cipher + * @param key an array containing the key + */ + private void key(byte[] key) { + int i; + int[] koffp = { 0 }; + int[] lr = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) { + P[i] = P[i] ^ streamtoword(key, koffp); + } + + for (i = 0; i < plen; i += 2) { + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + /** + * Perform the "enhanced key schedule" step described by Provos and Mazieres in + * "A Future-Adaptable Password Scheme" http://www.openbsd.org/papers/bcrypt-paper.ps + * @param data salt information + * @param key password information + */ + private void ekskey(byte[] data, byte[] key) { + int i; + int[] koffp = { 0 }, doffp = { 0 }; + int[] lr = { 0, 0 }; + int plen = P.length, slen = S.length; + + for (i = 0; i < plen; i++) { + P[i] = P[i] ^ streamtoword(key, koffp); + } + + for (i = 0; i < plen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + P[i] = lr[0]; + P[i + 1] = lr[1]; + } + + for (i = 0; i < slen; i += 2) { + lr[0] ^= streamtoword(data, doffp); + lr[1] ^= streamtoword(data, doffp); + encipher(lr, 0); + S[i] = lr[0]; + S[i + 1] = lr[1]; + } + } + + static long roundsForLogRounds(int log_rounds) { + if (log_rounds < 4 || log_rounds > 31) { + throw new IllegalArgumentException("Bad number of rounds"); + } + return 1L << log_rounds; + } + + /** + * Perform the central password hashing step in the bcrypt scheme + * @param password the password to hash + * @param salt the binary salt to hash with the password + * @param log_rounds the binary logarithm of the number of rounds of hashing to apply + * @return an array containing the binary hashed password + */ + private byte[] crypt_raw(byte[] password, byte[] salt, int log_rounds) { + int[] cdata = (int[]) bf_crypt_ciphertext.clone(); + int clen = cdata.length; + byte[] ret; + + long rounds = roundsForLogRounds(log_rounds); + + init_key(); + ekskey(salt, password); + for (long i = 0; i < rounds; i++) { + key(password); + key(salt); + } + + for (int i = 0; i < 64; i++) { + for (int j = 0; j < (clen >> 1); j++) { + encipher(cdata, j << 1); + } + } + + ret = new byte[clen * 4]; + for (int i = 0, j = 0; i < clen; i++) { + ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); + ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); + ret[j++] = (byte) (cdata[i] & 0xff); + } + return ret; + } + + /** + * Hash a password using the OpenBSD bcrypt scheme + * @param password the password to hash + * @param salt the salt to hash with (perhaps generated using BCrypt.gensalt) + * @return the hashed password + * @throws IllegalArgumentException if invalid salt is passed + */ + public static String hashpw(String password, String salt) throws IllegalArgumentException { + BCrypt B; + String real_salt; + byte[] passwordb, saltb, hashed; + char minor = (char) 0; + int rounds, off = 0; + StringBuilder rs = new StringBuilder(); + + if (salt == null) { + throw new IllegalArgumentException("salt cannot be null"); + } + + int saltLength = salt.length(); + + if (saltLength < 28) { + throw new IllegalArgumentException("Invalid salt"); + } + + if (salt.charAt(0) != '$' || salt.charAt(1) != '2') { + throw new IllegalArgumentException("Invalid salt version"); + } + if (salt.charAt(2) == '$') { + off = 3; + } + else { + minor = salt.charAt(2); + if (minor != 'a' || salt.charAt(3) != '$') { + throw new IllegalArgumentException("Invalid salt revision"); + } + off = 4; + } + + if (saltLength - off < 25) { + throw new IllegalArgumentException("Invalid salt"); + } + + // Extract number of rounds + if (salt.charAt(off + 2) > '$') { + throw new IllegalArgumentException("Missing salt rounds"); + } + rounds = Integer.parseInt(salt.substring(off, off + 2)); + + real_salt = salt.substring(off + 3, off + 25); + passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes(StandardCharsets.UTF_8); + + saltb = decode_base64(real_salt, BCRYPT_SALT_LEN); + + B = new BCrypt(); + hashed = B.crypt_raw(passwordb, saltb, rounds); + + rs.append("$2"); + if (minor >= 'a') { + rs.append(minor); + } + rs.append("$"); + if (rounds < 10) { + rs.append("0"); + } + rs.append(rounds); + rs.append("$"); + encode_base64(saltb, saltb.length, rs); + encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1, rs); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work + * factor therefore increases as 2**log_rounds. Minimum 4, maximum 31. + * @param random an instance of SecureRandom to use + * @return an encoded salt value + */ + public static String gensalt(int log_rounds, SecureRandom random) { + if (log_rounds < MIN_LOG_ROUNDS || log_rounds > MAX_LOG_ROUNDS) { + throw new IllegalArgumentException("Bad number of rounds"); + } + StringBuilder rs = new StringBuilder(); + byte[] rnd = new byte[BCRYPT_SALT_LEN]; + + random.nextBytes(rnd); + + rs.append("$2a$"); + if (log_rounds < 10) { + rs.append("0"); + } + rs.append(log_rounds); + rs.append("$"); + encode_base64(rnd, rnd.length, rs); + return rs.toString(); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method + * @param log_rounds the log2 of the number of rounds of hashing to apply - the work + * factor therefore increases as 2**log_rounds. Minimum 4, maximum 31. + * @return an encoded salt value + */ + public static String gensalt(int log_rounds) { + return gensalt(log_rounds, new SecureRandom()); + } + + /** + * Generate a salt for use with the BCrypt.hashpw() method, selecting a reasonable + * default for the number of hashing rounds to apply + * @return an encoded salt value + */ + public static String gensalt() { + return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS); + } + + /** + * Check that a plaintext password matches a previously hashed one + * @param plaintext the plaintext password to verify + * @param hashed the previously-hashed password + * @return true if the passwords match, false otherwise + */ + public static boolean checkpw(String plaintext, String hashed) { + return equalsNoEarlyReturn(hashed, hashpw(plaintext, hashed)); + } + + static boolean equalsNoEarlyReturn(String a, String b) { + char[] caa = a.toCharArray(); + char[] cab = b.toCharArray(); + + if (caa.length != cab.length) { + return false; + } + + byte ret = 0; + for (int i = 0; i < caa.length; i++) { + ret |= caa[i] ^ cab[i]; + } + return ret == 0; + } +} diff --git a/common/security/src/main/java/com/thing/password/BCryptPasswordEncoder.java b/common/security/src/main/java/com/thing/password/BCryptPasswordEncoder.java new file mode 100644 index 0000000..512fe12 --- /dev/null +++ b/common/security/src/main/java/com/thing/password/BCryptPasswordEncoder.java @@ -0,0 +1,82 @@ +package com.thing.password; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.security.SecureRandom; +import java.util.regex.Pattern; + +/** + * Implementation of PasswordEncoder that uses the BCrypt strong hashing function. Clients + * can optionally supply a "strength" (a.k.a. log rounds in BCrypt) and a SecureRandom + * instance. The larger the strength parameter the more work will have to be done + * (exponentially) to hash the passwords. The default value is 10. + * + * @author Dave Syer + * + */ +public class BCryptPasswordEncoder implements PasswordEncoder { + private final Pattern BCRYPT_PATTERN = Pattern + .compile("\\A\\$2a?\\$\\d\\d\\$[./0-9A-Za-z]{53}"); + private final Log logger = LogFactory.getLog(getClass()); + + private final int strength; + + private final SecureRandom random; + + public BCryptPasswordEncoder() { + this(-1); + } + + /** + * @param strength the log rounds to use, between 4 and 31 + */ + public BCryptPasswordEncoder(int strength) { + this(strength, null); + } + + /** + * @param strength the log rounds to use, between 4 and 31 + * @param random the secure random instance to use + * + */ + public BCryptPasswordEncoder(int strength, SecureRandom random) { + if (strength != -1 && (strength < BCrypt.MIN_LOG_ROUNDS || strength > BCrypt.MAX_LOG_ROUNDS)) { + throw new IllegalArgumentException("Bad strength"); + } + this.strength = strength; + this.random = random; + } + + @Override + public String encode(CharSequence rawPassword) { + String salt; + if (strength > 0) { + if (random != null) { + salt = BCrypt.gensalt(strength, random); + } + else { + salt = BCrypt.gensalt(strength); + } + } + else { + salt = BCrypt.gensalt(); + } + return BCrypt.hashpw(rawPassword.toString(), salt); + } + + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + if (encodedPassword == null || encodedPassword.isEmpty()) { + logger.warn("Empty encoded password"); + return false; + } + + if (!BCRYPT_PATTERN.matcher(encodedPassword).matches()) { + logger.warn("Encoded password does not look like BCrypt"); + return false; + } + + return BCrypt.checkpw(rawPassword.toString(), encodedPassword); + } +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/password/PasswordEncoder.java b/common/security/src/main/java/com/thing/password/PasswordEncoder.java new file mode 100644 index 0000000..01d3831 --- /dev/null +++ b/common/security/src/main/java/com/thing/password/PasswordEncoder.java @@ -0,0 +1,30 @@ +package com.thing.password; + +/** + * Service interface for encoding passwords. + *

+ * The preferred implementation is {@code BCryptPasswordEncoder}. + * + * @author Keith Donald + */ +public interface PasswordEncoder { + + /** + * Encode the raw password. Generally, a good encoding algorithm applies a SHA-1 or + * greater hash combined with an 8-byte or greater randomly generated salt. + */ + String encode(CharSequence rawPassword); + + /** + * Verify the encoded password obtained from storage matches the submitted raw + * password after it too is encoded. Returns true if the passwords match, false if + * they do not. The stored password itself is never decoded. + * + * @param rawPassword the raw password to encode and match + * @param encodedPassword the encoded password from storage to compare with + * @return true if the raw password, after encoding, matches the encoded password from + * storage + */ + boolean matches(CharSequence rawPassword, String encodedPassword); + +} diff --git a/common/security/src/main/java/com/thing/password/PasswordUtils.java b/common/security/src/main/java/com/thing/password/PasswordUtils.java new file mode 100644 index 0000000..7c8abbe --- /dev/null +++ b/common/security/src/main/java/com/thing/password/PasswordUtils.java @@ -0,0 +1,56 @@ +/* + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + *

+ * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + *

+ * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.thing.password; + +/** + * 密码工具类 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class PasswordUtils { + private static final PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); + + /** + * 加密 + * @param str 字符串 + * @return 返回加密字符串 + */ + public static String encode(String str){ + return passwordEncoder.encode(str); + } + + + /** + * 比较密码是否相等 + * @param str 明文密码 + * @param password 加密后密码 + * @return true:成功 false:失败 + */ + public static boolean matches(String str, String password){ + return passwordEncoder.matches(str, password); + } + +// public static void main(String[] args) { +// String str = "admin"; +// String password = encode(str); +// +// System.out.println(password); +// System.out.println(matches(str, password)); +// } + +} diff --git a/common/security/src/main/java/com/thing/xss/SqlFilter.java b/common/security/src/main/java/com/thing/xss/SqlFilter.java new file mode 100644 index 0000000..08949a3 --- /dev/null +++ b/common/security/src/main/java/com/thing/xss/SqlFilter.java @@ -0,0 +1,42 @@ + + +package com.thing.xss; + +import org.apache.commons.lang3.StringUtils; + +/** + * SQL过滤 + * @author Mark sunlightcs@gmail.com + */ +public class SqlFilter { + + /** + * SQL注入过滤 + * @param str 待验证的字符串 + */ + public static String sqlInject(String str){ + if(StringUtils.isBlank(str)){ + return null; + } + //去掉'|"|;|\字符 + str = StringUtils.replace(str, "'", ""); + str = StringUtils.replace(str, "\"", ""); + str = StringUtils.replace(str, ";", ""); + str = StringUtils.replace(str, "\\", ""); + + //转换成小写 + str = str.toLowerCase(); + + //非法字符 + String[] keywords = {"master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop"}; + + //判断是否包含非法字符 + for(String keyword : keywords){ + if(str.indexOf(keyword) != -1){ + //throw new SysException(ErrorCode.INVALID_SYMBOL); + } + } + + return str; + } +} diff --git a/common/security/src/main/java/com/thing/xss/XssFilter.java b/common/security/src/main/java/com/thing/xss/XssFilter.java new file mode 100644 index 0000000..3f331c7 --- /dev/null +++ b/common/security/src/main/java/com/thing/xss/XssFilter.java @@ -0,0 +1,30 @@ +package com.thing.xss; + +import jakarta.servlet.*; + +import java.io.IOException; + + +/** + * XSS过滤 + * @author Mark sunlightcs@gmail.com + */ +public class XssFilter implements Filter { + + @Override + public void init(FilterConfig config) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + /*XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper( + (HttpServletRequest) request);*/ + chain.doFilter(request, response); + } + + @Override + public void destroy() { + } + +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/xss/XssHttpServletRequestWrapper.java b/common/security/src/main/java/com/thing/xss/XssHttpServletRequestWrapper.java new file mode 100644 index 0000000..8a04d08 --- /dev/null +++ b/common/security/src/main/java/com/thing/xss/XssHttpServletRequestWrapper.java @@ -0,0 +1,140 @@ + + +package com.thing.xss; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.LinkedHashMap; +import java.util.Map; + + +/** + * XSS过滤处理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { + HttpServletRequest orgRequest; + + public XssHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + orgRequest = request; + } + + @Override + public ServletInputStream getInputStream() throws IOException { + //非json类型,直接返回 + if(!MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(super.getHeader(HttpHeaders.CONTENT_TYPE))){ + return super.getInputStream(); + } + + //为空,直接返回 + String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8); + if (StringUtils.isBlank(json)) { + return super.getInputStream(); + } + //xss过滤 + json = xssEncode(json); + final ByteArrayInputStream bis = new ByteArrayInputStream(json.getBytes(StandardCharsets.UTF_8)); + return new ServletInputStream() { + @Override + public boolean isFinished() { + return true; + } + + @Override + public boolean isReady() { + return true; + } + + @Override + public void setReadListener(ReadListener readListener) { + + } + + @Override + public int read() { + return bis.read(); + } + }; + } + + @Override + public String getParameter(String name) { + String value = super.getParameter(xssEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + @Override + public String[] getParameterValues(String name) { + String[] parameters = super.getParameterValues(name); + if (parameters == null || parameters.length == 0) { + return null; + } + + for (int i = 0; i < parameters.length; i++) { + parameters[i] = xssEncode(parameters[i]); + } + return parameters; + } + + @Override + public Map getParameterMap() { + Map map = new LinkedHashMap<>(); + Map parameters = super.getParameterMap(); + for (String key : parameters.keySet()) { + String[] values = parameters.get(key); + for (int i = 0; i < values.length; i++) { + values[i] = xssEncode(values[i]); + } + map.put(key, values); + } + return map; + } + + @Override + public String getHeader(String name) { + String value = super.getHeader(xssEncode(name)); + if (StringUtils.isNotBlank(value)) { + value = xssEncode(value); + } + return value; + } + + private String xssEncode(String input) { + return XssUtils.filter(input); + } + + /** + * 获取最原始的request + */ + public HttpServletRequest getOrgRequest() { + return orgRequest; + } + + /** + * 获取最原始的request + */ + public static HttpServletRequest getOrgRequest(HttpServletRequest request) { + if (request instanceof XssHttpServletRequestWrapper) { + return ((XssHttpServletRequestWrapper) request).getOrgRequest(); + } + + return request; + } + +} \ No newline at end of file diff --git a/common/security/src/main/java/com/thing/xss/XssUtils.java b/common/security/src/main/java/com/thing/xss/XssUtils.java new file mode 100644 index 0000000..b32b644 --- /dev/null +++ b/common/security/src/main/java/com/thing/xss/XssUtils.java @@ -0,0 +1,69 @@ + + +package com.thing.xss; + +import org.jsoup.Jsoup; +import org.jsoup.safety.Safelist; + +/** + * XSS过滤工具类 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public class XssUtils extends Safelist { + + /** + * XSS过滤 + */ + public static String filter(String html){ + return Jsoup.clean(html, xssWhitelist()); + } + + /** + * XSS过滤白名单 + */ + private static Safelist xssWhitelist(){ + return new Safelist() + //支持的标签 + .addTags("a", "b", "blockquote", "br", "caption", "cite", "code", "col", "colgroup", "dd", "div", "dl", + "dt", "em", "h1", "h2", "h3", "h4", "h5", "h6", "i", "img", "li", "ol", "p", "pre", "q", "small", + "strike", "strong","sub", "sup", "table", "tbody", "td","tfoot", "th", "thead", "tr", "u","ul", + "embed","object","param","span") + + //支持的标签属性 + .addAttributes("a", "href", "class", "style", "target", "rel", "nofollow") + .addAttributes("blockquote", "cite") + .addAttributes("code", "class", "style") + .addAttributes("col", "span", "width") + .addAttributes("colgroup", "span", "width") + .addAttributes("img", "align", "alt", "height", "src", "title", "width", "class", "style") + .addAttributes("ol", "start", "type") + .addAttributes("q", "cite") + .addAttributes("table", "summary", "width", "class", "style") + .addAttributes("tr", "abbr", "axis", "colspan", "rowspan", "width", "style") + .addAttributes("td", "abbr", "axis", "colspan", "rowspan", "width", "style") + .addAttributes("th", "abbr", "axis", "colspan", "rowspan", "scope","width", "style") + .addAttributes("ul", "type", "style") + .addAttributes("pre", "class", "style") + .addAttributes("div", "class", "id", "style") + .addAttributes("embed", "src", "wmode", "flashvars", "pluginspage", "allowFullScreen", "allowfullscreen", + "quality", "width", "height", "align", "allowScriptAccess", "allowscriptaccess", "allownetworking", "type") + .addAttributes("object", "type", "id", "name", "data", "width", "height", "style", "classid", "codebase") + .addAttributes("param", "name", "value") + .addAttributes("span", "class", "style") + + //标签属性对应的协议 + .addProtocols("a", "href", "ftp", "http", "https", "mailto") + .addProtocols("img", "src", "http", "https") + .addProtocols("blockquote", "cite", "http", "https") + .addProtocols("cite", "cite", "http", "https") + .addProtocols("q", "cite", "http", "https") + .addProtocols("embed", "src", "http", "https"); + } + +// public static void main(String[] args) { +// System.out.println(filter("物管理")); +// } + +} \ No newline at end of file diff --git a/common/transport/pom.xml b/common/transport/pom.xml new file mode 100644 index 0000000..505dcd1 --- /dev/null +++ b/common/transport/pom.xml @@ -0,0 +1,73 @@ + + + 4.0.0 + + com.thing + common + 5.1 + ../pom.xml + + + jar + transport + com.thing.common + ThingBI Server Common transport + + ${basedir}/../.. + + + + + com.thing.common + core + + + com.thing.common + orm + + + com.thing.common + queue + + + com.thing.common + script + + + io.netty + netty-all + + + com.github.vladimir-bukhtoyarov + bucket4j-core + + + + org.springframework.retry + spring-retry + + + org.thingsboard.common + data + + + org.java-websocket + Java-WebSocket + + + + + + + com.github.rholder + guava-retrying + + + org.javadelight + delight-nashorn-sandbox + + + + \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/api/AbstractTransportContext.java b/common/transport/src/main/java/com/thing/transport/api/AbstractTransportContext.java new file mode 100644 index 0000000..b046617 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/AbstractTransportContext.java @@ -0,0 +1,45 @@ +package com.thing.transport.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.Data; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author zhenghh. 2022-08-29 + **/ +@Data +@Slf4j +@Service +@ConditionalOnExpression("'${transport.api_enabled}'=='true'") +public abstract class AbstractTransportContext { + + protected final ObjectMapper mapper = new ObjectMapper(); + + @Autowired + private TransportService transportService; + @Getter + private ExecutorService executor; + + @PostConstruct + public void init() { + log.info("AbstractTransportContext init....."); + executor = Executors.newWorkStealingPool(50); + } + + + @PreDestroy + public void stop() { + if (executor != null) { + executor.shutdownNow(); + } + } +} diff --git a/common/transport/src/main/java/com/thing/transport/api/SessionMetaData.java b/common/transport/src/main/java/com/thing/transport/api/SessionMetaData.java new file mode 100644 index 0000000..cc7eb77 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/SessionMetaData.java @@ -0,0 +1,61 @@ +package com.thing.transport.api; + +import com.thing.gen.queue.QueueProto.SessionInfoProto; +import lombok.Data; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicInteger; + +@Data +class SessionMetaData { + + private final SessionInfoProto sessionInfo; + + private ScheduledFuture scheduledFuture; + + private volatile long lastActivityTime; + private volatile long lastReportedActivityTime; + private volatile AtomicInteger msgNum; + private volatile boolean debug; + private volatile boolean subscribedToAttributes; + + SessionMetaData(SessionInfoProto sessionInfo, boolean debug) { + this.sessionInfo = sessionInfo; + this.debug = debug; + this.msgNum = new AtomicInteger(0); + this.lastActivityTime = System.currentTimeMillis(); + this.scheduledFuture = null; + } + + void updateLastActivityTime() { + this.lastActivityTime = System.currentTimeMillis(); + } + + void setScheduledFuture(ScheduledFuture scheduledFuture) { + this.scheduledFuture = scheduledFuture; + } + + public ScheduledFuture getScheduledFuture() { + return scheduledFuture; + } + + public boolean hasScheduledFuture() { + return null != this.scheduledFuture; + } + + public Integer getMsgNum() { + return msgNum.get(); + } + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + + public void updateMsgNum() { + this.msgNum.incrementAndGet(); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/api/TransportService.java b/common/transport/src/main/java/com/thing/transport/api/TransportService.java new file mode 100644 index 0000000..313748a --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/TransportService.java @@ -0,0 +1,96 @@ +package com.thing.transport.api; + +import com.thing.transport.modules.dto.MqttBrokerConnectDTO; +import io.netty.buffer.ByteBuf; +import com.thing.gen.queue.QueueProto.SessionInfoProto; +import com.thing.gen.queue.QueueProto.TransportMsg; + +import java.util.List; +import java.util.UUID; + +/** + * @author zhenghh. 2022-08-29 + **/ +public interface TransportService { + + /** + * 连接限制 + * + * @param sessionInfo session + * @param msg 消息 + * @param callback 回调 + * @return boolean + */ + boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback callback); + + /** + * 处理传输信息 + * + * @param sessionInfo session + * @param transportMsg 遥测信息 + * @param callback 回调 + */ + void process(SessionInfoProto sessionInfo, TransportMsg transportMsg, TransportServiceCallback callback); + + /** + * 注册session + * + * @param sessionInfo session + */ + void registerSession(SessionInfoProto sessionInfo); + + /** + * 更新连接时间 + * + * @param sessionInfo session + */ + void reportActivity(SessionInfoProto sessionInfo); + + /** + * 更新消息数据 + * + * @param sessionId sessionId + */ + void reportMsgNum(UUID sessionId); + + /** + * 移除session + * + * @param sessionInfo session + */ + void deregisterSession(SessionInfoProto sessionInfo); + + /** + * 移除所有session + */ + void deregisterSession(); + + /** + * 在线列表 + * + * @return list + */ + List onlineList(); + + /** + * 调试模式 + * + * @param sessionId sessionId + * @param debug true开启, false关闭 + */ + void debug(String sessionId, Boolean debug); + + /** + * 关闭所有调试 + */ + void debugClose(); + + /** + * 调试模式 消息入库 + * + * @param sessionId sessionId + * @param topicName topic + * @param payload 消息 + */ + void debugSave(UUID sessionId, String topicName, ByteBuf payload); +} diff --git a/common/transport/src/main/java/com/thing/transport/api/TransportServiceCallback.java b/common/transport/src/main/java/com/thing/transport/api/TransportServiceCallback.java new file mode 100644 index 0000000..a8df046 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/TransportServiceCallback.java @@ -0,0 +1,24 @@ +package com.thing.transport.api; + +/** + * Created by ashvayka on 04.10.18. + */ +public interface TransportServiceCallback { + + TransportServiceCallback EMPTY = new TransportServiceCallback() { + @Override + public void onSuccess(Void msg) { + + } + + @Override + public void onError(Throwable e) { + + } + }; + + void onSuccess(T msg); + + void onError(Throwable e); + +} diff --git a/common/transport/src/main/java/com/thing/transport/api/TransportServiceImpl.java b/common/transport/src/main/java/com/thing/transport/api/TransportServiceImpl.java new file mode 100644 index 0000000..5f00ba2 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/TransportServiceImpl.java @@ -0,0 +1,324 @@ +package com.thing.transport.api; + +import com.thing.common.core.exception.SysException; +import com.thing.common.util.thread.ThingThreadFactory; +import io.netty.buffer.ByteBuf; +import com.thing.gen.queue.QueueProto; +import com.thing.gen.queue.QueueProto.SessionInfoProto; +import com.thing.queue.QueueCallback; +import com.thing.queue.QueueMsgMetadata; +import com.thing.queue.cluster.ClusterService; +import com.thing.transport.api.adaptor.AdaptorException; +import com.thing.transport.api.session.TransportSessionUtil; +import com.thing.transport.api.tools.RateLimits; +import com.thing.transport.modules.dto.MqttBrokerConnectDTO; +import com.thing.transport.modules.entity.MqttBrokerConnectEntity; +import com.thing.transport.modules.entity.MqttBrokerMsgEntity; +import com.thing.transport.modules.service.MqttBrokerConnectService; +import com.thing.transport.modules.service.MqttBrokerMsgService; +import com.thing.transport.mqtt.broker.adaptors.JsonMqttAdaptor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.UUID; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-08-30 + **/ +@Slf4j +@Service +@ConditionalOnExpression("'${transport.api_enabled:true}'=='true'") +public class TransportServiceImpl implements TransportService { + + @Autowired + private ClusterService clusterService; + @Autowired + private MqttBrokerConnectService connectService; + @Autowired + private MqttBrokerMsgService msgService; + + @Value("${transport.rate_limits.enabled}") + private boolean rateLimitEnabled; + @Value("${transport.rate_limits.device}") + private String perDevicesLimitsConf; + @Value("${transport.sessions.inactivity_timeout}") + private long sessionInactivityTimeout; + @Value("${transport.sessions.report_timeout}") + private long sessionReportTimeout; + + protected ScheduledExecutorService schedulerExecutor; + protected ExecutorService transportCallbackExecutor; + private final ConcurrentMap sessions = new ConcurrentHashMap<>(); + private final ConcurrentMap perDeviceLimits = new ConcurrentHashMap<>(); + + + @PostConstruct + public void init() { + if (rateLimitEnabled) { + //Just checking the configuration parameters + new RateLimits(perDevicesLimitsConf); + } + this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingThreadFactory.forName("transport-scheduler")); + this.transportCallbackExecutor = Executors.newWorkStealingPool(20); + this.schedulerExecutor.scheduleAtFixedRate(this::checkInactivityAndReportActivity, new Random().nextInt((int) sessionReportTimeout), sessionReportTimeout, TimeUnit.MILLISECONDS); + + } + + @PreDestroy + public void destroy() { + if (rateLimitEnabled) { + perDeviceLimits.clear(); + } + if (transportCallbackExecutor != null) { + transportCallbackExecutor.shutdownNow(); + } + } + + /** + * 处理传输信息 + * + * @param sessionInfo session + * @param transportMsg 遥测信息 + * @param callback 回调 + */ + @Override + public void process(SessionInfoProto sessionInfo, QueueProto.TransportMsg transportMsg, TransportServiceCallback callback) { + + + if (checkLimits(sessionInfo, transportMsg, callback)) { + reportActivityInternal(sessionInfo); + MsgPackCallback packCallback = new MsgPackCallback(callback); + clusterService.pushMsgToCore(TransportSessionUtil.toSessionId(sessionInfo), transportMsg, packCallback); + } + } + + /** + * 连接限制 + * + * @param sessionInfo session + * @param msg 消息 + * @param callback 回调 + * @return boolean + */ + @Override + public boolean checkLimits(SessionInfoProto sessionInfo, Object msg, TransportServiceCallback callback) { + if (log.isTraceEnabled()) { + log.trace("[{}] Processing msg: {}", TransportSessionUtil.toSessionId(sessionInfo), msg); + } + if (!rateLimitEnabled) { + return true; + } + UUID deviceId = TransportSessionUtil.toSessionId(sessionInfo); + RateLimits rateLimits = perDeviceLimits.computeIfAbsent(deviceId, id -> new RateLimits(perDevicesLimitsConf)); + if (!rateLimits.tryConsume()) { + if (callback != null) { + callback.onError(new SysException("超出连接限制")); + } + if (log.isTraceEnabled()) { + log.trace("[{}] Device level rate limit detected: {}", deviceId, msg); + } + return false; + } + + return true; + } + + @Override + public void registerSession(SessionInfoProto sessionInfo) { + SessionMetaData currentSession = new SessionMetaData(sessionInfo, false); + UUID sessionId = TransportSessionUtil.toSessionId(sessionInfo); + sessions.putIfAbsent(sessionId, currentSession); + //保存记录 + MqttBrokerConnectEntity entity = convertEntity(sessionInfo); + connectService.save(entity); + //每5分钟更新一次最后连接时间 + ScheduledFuture executorFuture = schedulerExecutor.scheduleAtFixedRate(() -> { + SessionMetaData sessionMetaData = sessions.get(sessionId); + connectService.updateLastTime(sessionId.toString(), sessionMetaData.getLastActivityTime(), sessionMetaData.getMsgNum()); + }, 3, 5, TimeUnit.MINUTES); + + currentSession.setScheduledFuture(executorFuture); + } + + @Override + public void reportActivity(SessionInfoProto sessionInfo) { + reportActivityInternal(sessionInfo); + } + + /** + * 更新消息数据 + * + * @param sessionId sessionId + */ + @Override + public void reportMsgNum(UUID sessionId) { + SessionMetaData sessionMetaData = sessions.get(sessionId); + if (sessionMetaData != null) { + sessionMetaData.updateMsgNum(); + } + } + + @Override + public void deregisterSession(SessionInfoProto sessionInfo) { + UUID sessionId = TransportSessionUtil.toSessionId(sessionInfo); + SessionMetaData currentSession = sessions.get(sessionId); + if (currentSession != null && currentSession.hasScheduledFuture()) { + log.debug("Stopping scheduler to avoid resending response if request has been ack."); + currentSession.getScheduledFuture().cancel(false); + } + connectService.updateLastTime(sessionId.toString(), System.currentTimeMillis(), Objects.isNull(currentSession) ? null : currentSession.getMsgNum()); + sessions.remove(sessionId); + } + + /** + * 移除所有session + */ + @Override + public void deregisterSession() { + //连接数大时需优化 + sessions.forEach((sessionId, sessionMetaData) -> { + if (sessionMetaData.hasScheduledFuture()) { + sessionMetaData.getScheduledFuture().cancel(false); + } + connectService.updateLastTime(sessionId.toString(), System.currentTimeMillis(), sessionMetaData.getMsgNum()); + }); + sessions.clear(); + } + + /** + * 在线列表 + * + * @return list + */ + @Override + public List onlineList() { + return sessions.values().parallelStream() + .map(session -> { + SessionInfoProto sessionInfo = session.getSessionInfo(); + return new MqttBrokerConnectDTO(TransportSessionUtil.toSessionId(sessionInfo).toString(), sessionInfo.getClientId(), + sessionInfo.getUserName(), sessionInfo.getStartTime(), session.getLastActivityTime(), sessionInfo.getClientIp(), + session.getMsgNum(), session.isDebug(), true); + }).collect(Collectors.toList()); + } + + /** + * 调试模式 + * + * @param sessionId sessionId + * @param debug true开启, false关闭 + */ + @Override + public void debug(String sessionId, Boolean debug) { + SessionMetaData currentSession = sessions.get(UUID.fromString(sessionId)); + if (Objects.isNull(currentSession)) { + throw new SysException("连接已断开"); + } + currentSession.setDebug(debug); + } + + /** + * 关闭所有调试 + */ + @Override + public void debugClose() { + for (SessionMetaData sessionMetaData : sessions.values()) { + sessionMetaData.setDebug(false); + } + } + + /** + * 调试模式 消息入库 + * + * @param sessionId sessionId + * @param topicName topic + * @param payload 消息 + */ + @Override + public void debugSave(UUID sessionId, String topicName, ByteBuf payload) { + try { + SessionMetaData currentSession = sessions.get(sessionId); + if (Objects.nonNull(currentSession) && currentSession.isDebug()) { + String validatePayload = JsonMqttAdaptor.validatePayload(sessionId, payload, true); + msgService.save(convertEntity(sessionId, topicName, currentSession.getSessionInfo(), validatePayload)); + } + } catch (AdaptorException e) { + log.error("{} 推送消息入库失败: {}", sessionId, e.getMessage()); + } + + } + + private MqttBrokerMsgEntity convertEntity(UUID sessionId, String topicName, SessionInfoProto sessionInfo, String payload) { + MqttBrokerMsgEntity entity = new MqttBrokerMsgEntity(); + entity.setClientId(sessionInfo.getClientId()); + entity.setUserName(sessionInfo.getUserName()); + entity.setMsg(payload); + entity.setTopic(topicName); + entity.setSessionId(sessionId.toString()); + return entity; + } + + private MqttBrokerConnectEntity convertEntity(SessionInfoProto sessionInfo) { + MqttBrokerConnectEntity entity = new MqttBrokerConnectEntity(); + entity.setId(TransportSessionUtil.toSessionId(sessionInfo).toString()); + entity.setClientId(sessionInfo.getClientId()); + entity.setUserName(sessionInfo.getUserName()); + entity.setStartTime(sessionInfo.getStartTime()); + entity.setLastTime(sessionInfo.getStartTime()); + entity.setClientIp(sessionInfo.getClientIp()); + return entity; + } + + private void reportActivityInternal(SessionInfoProto sessionInfo) { + UUID sessionId = TransportSessionUtil.toSessionId(sessionInfo); + SessionMetaData sessionMetaData = sessions.get(sessionId); + if (sessionMetaData != null) { + sessionMetaData.updateLastActivityTime(); + } + } + + private void checkInactivityAndReportActivity() { + long expTime = System.currentTimeMillis() - sessionInactivityTimeout; + sessions.forEach((uuid, sessionMetaData) -> { + long lastActivityTime = sessionMetaData.getLastActivityTime(); + SessionInfoProto sessionInfo = sessionMetaData.getSessionInfo(); + if (lastActivityTime < expTime) { + if (log.isDebugEnabled()) { + log.debug("[{}] Session has expired due to last activity time: {}", TransportSessionUtil.toSessionId(sessionInfo), lastActivityTime); + } + sessions.remove(uuid); + } else { + if (lastActivityTime > sessionMetaData.getLastReportedActivityTime()) { + sessionMetaData.setLastReportedActivityTime(lastActivityTime); + } + } + }); + } + + private class MsgPackCallback implements QueueCallback { + private final TransportServiceCallback callback; + + public MsgPackCallback(TransportServiceCallback callback) { + this.callback = callback; + } + + @Override + public void onSuccess(QueueMsgMetadata metadata) { + TransportServiceImpl.this.transportCallbackExecutor.submit(() -> callback.onSuccess(null)); + } + + @Override + public void onFailure(Throwable t) { + callback.onError(t); + } + } +} diff --git a/common/transport/src/main/java/com/thing/transport/api/adaptor/AdaptorException.java b/common/transport/src/main/java/com/thing/transport/api/adaptor/AdaptorException.java new file mode 100644 index 0000000..dbe021b --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/adaptor/AdaptorException.java @@ -0,0 +1,19 @@ +package com.thing.transport.api.adaptor; + +public class AdaptorException extends Exception { + + private static final long serialVersionUID = 1L; + + public AdaptorException() { + super(); + } + + public AdaptorException(String cause) { + super(cause); + } + + public AdaptorException(Exception cause) { + super(cause); + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/api/adaptor/JsonConverter.java b/common/transport/src/main/java/com/thing/transport/api/adaptor/JsonConverter.java new file mode 100644 index 0000000..27c6670 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/adaptor/JsonConverter.java @@ -0,0 +1,172 @@ +package com.thing.transport.api.adaptor; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.gson.*; +import com.thing.gen.queue.QueueProto.TelemetryMsg; +import com.thing.gen.queue.QueueProto.TelemetryProto; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.gen.queue.QueueProto.TsKvProto; +import org.apache.commons.collections4.CollectionUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +/** + * 数据转换 + * + * @author zhenghh + */ +public class JsonConverter { + + private static final String CAN_T_PARSE_VALUE = "Can't parse value: "; + private static final String VALUES = "values"; + private static final String TS = "ts"; + + private static final Gson gson = new Gson(); + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + /** + * 单设备 + * + * @param thingCode 设备编码 + * @param jsonElement json + * [ + * { + * "ts": "", + * "values": { + * "key1": "val1", + * "key2": "val2" + * } + * } + * ] + * @param originType 数据来源 + * @return TelemetryMsg + * @throws JsonSyntaxException e + */ + public static TransportMsg convertToTelemetryProto(String thingCode, JsonElement jsonElement, String originType) throws JsonSyntaxException { + TransportMsg.Builder builder = TransportMsg.newBuilder(); + if (jsonElement.isJsonArray()) { + builder.setTelemetryMsg(TelemetryMsg.newBuilder().addTelemetryList(parseObject(thingCode, jsonElement.getAsJsonArray()))); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + return builder.setOrigin(originType).build(); + } + + public static TransportMsg convertToTelemetryProto(JsonElement jsonElement, String originType, Long tenantCode, Long companyId, Long deptId) { + TransportMsg.Builder builder = TransportMsg.newBuilder().setTelemetryMsg(convertToTelemetryBuilder(jsonElement)); + return builder.setOrigin(originType).setTenantCode(tenantCode).setCompanyId(companyId).setDeptId(deptId).build(); + } + /** + * 多设备 + * + * @param jsonElement json + * { + * "thingCode": [ + * { + * "ts": "", + * "values": { + * "key1": "val1", + * "key2": "val2" + * } + * } + * ] + * } + * @param originType 数据来源 + * @return TelemetryMsg + * @throws JsonSyntaxException e + */ + public static TransportMsg convertToTelemetryProto(JsonElement jsonElement, String originType) throws JsonSyntaxException { + TransportMsg.Builder builder = TransportMsg.newBuilder().setTelemetryMsg(convertToTelemetryBuilder(jsonElement)); + return builder.setOrigin(originType).build(); + } + + private static TelemetryMsg.Builder convertToTelemetryBuilder(JsonElement jsonElement) throws JsonSyntaxException { + TelemetryMsg.Builder builder = TelemetryMsg.newBuilder(); + if (jsonElement.isJsonObject()) { + for (Entry entry : jsonElement.getAsJsonObject().entrySet()) { + JsonElement value = entry.getValue(); + if (value.isJsonArray()) { + JsonArray jsonArray = value.getAsJsonArray(); + builder.addTelemetryList(parseObject(entry.getKey(), jsonArray)); + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + value); + } + } + } else { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + jsonElement); + } + return builder; + } + + private static TelemetryProto parseObject(String thingCode, JsonArray jsonArray) { + TelemetryProto.Builder builder = TelemetryProto.newBuilder(); + builder.setThingCode(thingCode); + List tsKvList = new ArrayList<>(); + for (JsonElement jsonElement : jsonArray) { + JsonObject jo = jsonElement.getAsJsonObject(); + if (jo.has(TS) && jo.has(VALUES)) { + tsKvList.addAll(parseProtoValues(jo.get(TS).getAsLong(), jo.get(VALUES).getAsJsonObject())); + } + } + builder.addAllTsKvList(tsKvList); + return builder.build(); + } + + private static List parseProtoValues(long ts, JsonObject valuesObject) { + List result = new ArrayList<>(); + for (Entry valueEntry : valuesObject.entrySet()) { + JsonElement element = valueEntry.getValue(); + if (element.isJsonPrimitive()) { + result.add(TsKvProto.newBuilder().setTs(ts).setKey(valueEntry.getKey()) + .setVal(element.getAsJsonPrimitive().getAsString()).build()); + } else if (element.isJsonObject() || element.isJsonArray()) { + result.add(TsKvProto.newBuilder().setTs(ts).setKey(valueEntry.getKey()) + .setVal(element.toString()).build()); + } else if (!element.isJsonNull()) { + throw new JsonSyntaxException(CAN_T_PARSE_VALUE + element); + } + } + return result; + } + + public static List convertToJsonObjectList(List records) { + if(CollectionUtils.isEmpty(records)){ + return Collections.emptyList(); + } + return records.stream() + .map(record -> gson.toJsonTree(record).getAsJsonObject()) + .collect(Collectors.toList()); + } + + public static List convertToJsonObjectListObjectNode(List records) { + if(CollectionUtils.isEmpty(records)){ + return Collections.emptyList(); + } + return records.stream() + .map(record -> objectMapper.valueToTree(record)) + .filter(node -> node instanceof ObjectNode) + .map(node -> (ObjectNode) node) + .collect(Collectors.toList()); + } + public static List convertToJsonObjectList2(List records) { + if(CollectionUtils.isEmpty(records)){ + return Collections.emptyList(); + } + return records.stream() + .map(record -> JSON.parseObject(JSON.toJSONString(record, String.valueOf(SerializerFeature.WriteMapNullValue)))) + .collect(Collectors.toList()); + } + + + +} diff --git a/common/transport/src/main/java/com/thing/transport/api/session/SessionContext.java b/common/transport/src/main/java/com/thing/transport/api/session/SessionContext.java new file mode 100644 index 0000000..c501b7b --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/session/SessionContext.java @@ -0,0 +1,10 @@ +package com.thing.transport.api.session; + +import java.util.UUID; + +public interface SessionContext { + + UUID getSessionId(); + + int nextMsgId(); +} diff --git a/common/transport/src/main/java/com/thing/transport/api/session/TransportSessionUtil.java b/common/transport/src/main/java/com/thing/transport/api/session/TransportSessionUtil.java new file mode 100644 index 0000000..27e16d8 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/session/TransportSessionUtil.java @@ -0,0 +1,52 @@ +package com.thing.transport.api.session; + +import com.thing.gen.queue.QueueProto; + +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * @author zhenghh. 2022-09-22 + **/ +public class TransportSessionUtil { + + + public static QueueProto.SessionInfoProto createSession(String sessionId) { + return createSession(sessionId.getBytes(StandardCharsets.UTF_8)); + } + + public static QueueProto.SessionInfoProto createSession(byte[] bytes) { + return createSession(UUID.nameUUIDFromBytes(bytes), "", ""); + } + + public static QueueProto.SessionInfoProto createSession(UUID sessionId) { + return createSession(sessionId, "", ""); + } + + public static QueueProto.SessionInfoProto createSession(UUID sessionId, String thingCode, String gateway) { + return QueueProto.SessionInfoProto.newBuilder() + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .setThingCode(thingCode) + .setGateway(gateway) + .build(); + } + + public static QueueProto.SessionInfoProto createSession(UUID sessionId, String thingCode, String gateway, String clientId, + String userName, String clientIp, Long startTime) { + return QueueProto.SessionInfoProto.newBuilder() + .setSessionIdMSB(sessionId.getMostSignificantBits()) + .setSessionIdLSB(sessionId.getLeastSignificantBits()) + .setThingCode(thingCode) + .setGateway(gateway) + .setClientId(clientId) + .setUserName(userName) + .setClientIp(clientIp) + .setStartTime(startTime) + .build(); + } + + public static UUID toSessionId(QueueProto.SessionInfoProto sessionInfo) { + return new UUID(sessionInfo.getSessionIdMSB(), sessionInfo.getSessionIdLSB()); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/api/tools/RateLimits.java b/common/transport/src/main/java/com/thing/transport/api/tools/RateLimits.java new file mode 100644 index 0000000..124fbe2 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/api/tools/RateLimits.java @@ -0,0 +1,38 @@ +package com.thing.transport.api.tools; + +import io.github.bucket4j.Bandwidth; +import io.github.bucket4j.Bucket4j; +import io.github.bucket4j.local.LocalBucket; +import io.github.bucket4j.local.LocalBucketBuilder; + +import java.time.Duration; + +/** + * Created by ashvayka on 22.10.18. + */ +public class RateLimits { + private final LocalBucket bucket; + + public RateLimits(String limitsConfiguration) { + LocalBucketBuilder builder = Bucket4j.builder(); + boolean initialized = false; + for (String limitSrc : limitsConfiguration.split(",")) { + long capacity = Long.parseLong(limitSrc.split(":")[0]); + long duration = Long.parseLong(limitSrc.split(":")[1]); + builder.addLimit(Bandwidth.simple(capacity, Duration.ofSeconds(duration))); + initialized = true; + } + if (initialized) { + bucket = builder.build(); + } else { + throw new IllegalArgumentException("Failed to parse rate limits configuration: " + limitsConfiguration); + } + + + } + + public boolean tryConsume() { + return bucket.tryConsume(1); + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/http/HttpTransportContext.java b/common/transport/src/main/java/com/thing/transport/http/HttpTransportContext.java new file mode 100644 index 0000000..e447fe9 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/http/HttpTransportContext.java @@ -0,0 +1,19 @@ +package com.thing.transport.http; + +import com.thing.transport.api.AbstractTransportContext; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-08-29 + **/ +@ConditionalOnExpression("'${transport.api_enabled}'=='true' && '${transport.http.enabled}'=='true'") +@Component +public class HttpTransportContext extends AbstractTransportContext { + + @Getter + @Value("${transport.http.request_timeout}") + private long defaultTimeout; +} diff --git a/common/transport/src/main/java/com/thing/transport/http/TelemetryController.java b/common/transport/src/main/java/com/thing/transport/http/TelemetryController.java new file mode 100644 index 0000000..dab0d82 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/http/TelemetryController.java @@ -0,0 +1,80 @@ +package com.thing.transport.http; + +import com.google.gson.JsonParser; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.web.response.Result; +import com.thing.transport.api.TransportServiceCallback; +import com.thing.transport.api.adaptor.JsonConverter; +import com.thing.transport.api.session.TransportSessionUtil; +import jakarta.servlet.http.HttpServletRequest; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.context.request.async.DeferredResult; + +import java.nio.charset.StandardCharsets; + +/** + * @author zhenghh. 2022-08-29 + **/ +@Slf4j +@RestController +@RequestMapping("/telemetry") +@ConditionalOnExpression("'${transport.api_enabled}'=='true' && '${transport.http.enabled}'=='true'") +public class TelemetryController { + + @Autowired + private HttpTransportContext transportContext; + + /** + * 入参 + * { + * "thingCode": [ + * { + * "ts": "", + * "values": { + * "key": "", + * "val": "" + * } + * } + * ] + * } + */ + @PostMapping + public DeferredResult postTelemetry(@RequestBody String json, HttpServletRequest request) { + DeferredResult responseWriter = new DeferredResult<>(); + String token = request.getHeader(Constant.TOKEN_HEADER); + if(StringUtils.isBlank(token)) { + responseWriter.setResult(new Result().error("token不存在")); + return responseWriter; + } + transportContext.getTransportService().process(TransportSessionUtil.createSession(token.getBytes(StandardCharsets.UTF_8)), + JsonConverter.convertToTelemetryProto(JsonParser.parseString(json), QueueOriginType.API.name()), + new HttpOkCallback(responseWriter)); + return responseWriter; + } + + private static class HttpOkCallback implements TransportServiceCallback { + private final DeferredResult responseWriter; + + public HttpOkCallback(DeferredResult responseWriter) { + this.responseWriter = responseWriter; + } + + @Override + public void onSuccess(Void msg) { + responseWriter.setResult(new Result()); + } + + @Override + public void onError(Throwable e) { + responseWriter.setResult(new Result().error(e.getMessage())); + } + } +} diff --git a/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerConnectController.java b/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerConnectController.java new file mode 100644 index 0000000..526fe8e --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerConnectController.java @@ -0,0 +1,136 @@ +package com.thing.transport.modules.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.common.core.annotation.LogOperation; + +import com.thing.common.core.utils.DateTimeUtils; + +import com.thing.common.core.validator.AssertUtils; +import com.thing.transport.api.TransportService; +import com.thing.transport.modules.dto.MqttBrokerConnectDTO; +import com.thing.transport.modules.excel.MqttBrokerConnectExcel; +import com.thing.transport.modules.service.MqttBrokerConnectService; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + + +/** + * mqttBroker连接日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@RestController +@RequestMapping("mqtt/broker/connect") +@Tag(name = "mqttBroker连接日志") +public class MqttBrokerConnectController { + @Autowired + private MqttBrokerConnectService mqttBrokerConnectService; + @Autowired + private TransportService transportService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "clientId",description ="客户端ID"), + @Parameter(name = "clientIp",description ="客户端IP"), + @Parameter(name = "userName",description ="用户名"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = mqttBrokerConnectService.page(params); + setOnline(page.getList()); + return new Result>().ok(page); + } + + @GetMapping("debug/{sessionId}/{debug}") + @Operation(summary="调试模式") + public Result debug(@PathVariable String sessionId, @PathVariable Boolean debug) { + transportService.debug(sessionId, debug); + return new Result(); + } + + @GetMapping("debug/close") + @Operation(summary="关闭所有调试模式") + public Result debugClose() { + transportService.debugClose(); + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + mqttBrokerConnectService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("clear") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear() { + + mqttBrokerConnectService.clear(); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name = "clientId",description ="客户端ID"), + @Parameter(name = "clientIp",description ="客户端IP"), + @Parameter(name = "userName",description ="用户名"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = mqttBrokerConnectService.list(params); + List collect = list.stream().map(item -> new MqttBrokerConnectExcel(item.getClientId(), item.getUserName(), + Objects.nonNull(item.getStartTime()) ? DateTimeUtils.parseDateTime(item.getStartTime()).format(DateTimeUtils.DATE_TIME_PATTERN) : null, + Objects.nonNull(item.getLastTime()) ? DateTimeUtils.parseDateTime(item.getLastTime()).format(DateTimeUtils.DATE_TIME_PATTERN) : null, + item.getClientIp(), item.getMsgNum() + )).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "mqtt连接日志", null, MqttBrokerConnectExcel.class, "mqtt连接日志.xls", response); + } + + private void setOnline(List list) { + List onlineList = transportService.onlineList(); + for (MqttBrokerConnectDTO dto : list) { + dto.setOnline(false); + dto.setDebug(false); + onlineList.parallelStream() + .filter(online -> StringUtils.equals(dto.getId(), online.getId())).findFirst() + .ifPresent(online -> { + dto.setOnline(true); + dto.setLastTime(null); + dto.setDebug(online.getDebug()); + }); + } + } +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerMsgController.java b/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerMsgController.java new file mode 100644 index 0000000..7b6d6fd --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/controller/MqttBrokerMsgController.java @@ -0,0 +1,97 @@ +package com.thing.transport.modules.controller; + +import cn.hutool.core.date.DateUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import com.thing.transport.modules.dto.MqttBrokerMsgDTO; +import com.thing.transport.modules.excel.MqttBrokerMsgExcel; +import com.thing.transport.modules.service.MqttBrokerMsgService; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * mqttBroker调试日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@RestController +@RequestMapping("mqtt/broker/msg") +@Tag(name = "mqttBroker调试日志") +public class MqttBrokerMsgController { + @Autowired + private MqttBrokerMsgService mqttBrokerMsgService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "sessionId",description ="sessionId"), + @Parameter(name = "topic",description ="主题"), + @Parameter(name = "msg",description ="用户名"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = mqttBrokerMsgService.page(params); + + return new Result>().ok(page); + } + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + mqttBrokerMsgService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("clear/{sessionId}") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear(@PathVariable String sessionId) { + + mqttBrokerMsgService.deleteBySessionId(sessionId); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name = "sessionId",description ="sessionId"), + @Parameter(name = "topic",description ="主题"), + @Parameter(name = "msg",description ="消息"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = mqttBrokerMsgService.list(params); + List collect = list.stream().map(item -> new MqttBrokerMsgExcel(DateTimeUtils.format(item.getCreateDate(),DateTimeUtils.DATE_TIME_PATTERN_STR), item.getTopic(), item.getMsg())).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "mqtt消息日志", null, MqttBrokerMsgExcel.class, "mqtt消息日志.xls", response); + } +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerConnectDTO.java b/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerConnectDTO.java new file mode 100644 index 0000000..5207da1 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerConnectDTO.java @@ -0,0 +1,43 @@ +package com.thing.transport.modules.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** +* mqttBroker连接日志 +* +* @author zhh zhh +* @since 0.0.1 2022-11-25 +*/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "mqttBroker连接日志") +public class MqttBrokerConnectDTO implements Serializable { + + private static final long serialVersionUID = 797450440629676068L; + @Schema(description = "主键,当前连接的sessionId") + private String id; + @Schema(description = "客户端ID") + private String clientId; + @Schema(description = "用户名(设备token)") + private String userName; + @Schema(description = "开始连接时间") + private Long startTime; + @Schema(description = "最后连接时间") + private Long lastTime; + @Schema(description = "客户端IP") + private String clientIp; + @Schema(description = "发送消息数量") + private Integer msgNum; + @Schema(description = "调试模式") + private Boolean debug; + @Schema(description = "是否在线") + private Boolean online; + +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerMsgDTO.java b/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerMsgDTO.java new file mode 100644 index 0000000..830c6db --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/dto/MqttBrokerMsgDTO.java @@ -0,0 +1,38 @@ +package com.thing.transport.modules.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* mqttBroker调试日志 +* +* @author zhh zhh +* @since 0.0.1 2022-11-25 +*/ +@Data +@Schema( name= "mqttBroker调试日志") +public class MqttBrokerMsgDTO implements Serializable { + + private static final long serialVersionUID = 6333111231096105469L; + private Long id; + @Schema(description = "客户端ID") + private String clientId; + @Schema(description = "用户名(设备token)") + private String userName; + @Schema(description = "消息") + private String msg; + @Schema(description = "topic") + private String topic; + @Schema(description = "推送消息sessionId") + private String sessionId; + @Schema(description = "创建时间") + private Date createDate; + +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerConnectEntity.java b/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerConnectEntity.java new file mode 100644 index 0000000..e9b6a7f --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerConnectEntity.java @@ -0,0 +1,61 @@ +package com.thing.transport.modules.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; + +/** + * mqttBroker连接日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@Data +@Table("mqtt_broker_connect") +public class MqttBrokerConnectEntity{ + + private String id; + /** + * 客户端ID + */ + private String clientId; + /** + * 用户名(设备token) + */ + private String userName; + /** + * 开始连接时间 + */ + private Long startTime; + /** + * 最后连接时间 + */ + private Long lastTime; + /** + * 客户端IP + */ + private String clientIp; + /** + * 发送消息数量 + */ + private Integer msgNum; + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + + /** + * 修改时间 + */ + private Long updateDate; +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerMsgEntity.java b/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerMsgEntity.java new file mode 100644 index 0000000..6d9e4e6 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/entity/MqttBrokerMsgEntity.java @@ -0,0 +1,39 @@ +package com.thing.transport.modules.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * mqttBroker调试日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@Data +@Accessors(chain = true) +@Table("mqtt_broker_msg") +public class MqttBrokerMsgEntity extends BaseDateEntity { + /** + * 客户端ID + */ + private String clientId; + /** + * 用户名(设备token) + */ + private String userName; + /** + * 消息 + */ + private String msg; + /** + * topic + */ + private String topic; + /** + * sessionId + */ + private String sessionId; + +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerConnectExcel.java b/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerConnectExcel.java new file mode 100644 index 0000000..481d478 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerConnectExcel.java @@ -0,0 +1,27 @@ +package com.thing.transport.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2022-11-29 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MqttBrokerConnectExcel { + @Excel(name = "客户端ID") + private String clientId; + @Excel(name = "用户名") + private String userName; + @Excel(name = "开始连接时间") + private String startTime; + @Excel(name = "最后连接时间") + private String lastTime; + @Excel(name = "客户端IP") + private String clientIp; + @Excel(name = "发送消息数量") + private Integer msgNum; +} diff --git a/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerMsgExcel.java b/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerMsgExcel.java new file mode 100644 index 0000000..0c361f8 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/excel/MqttBrokerMsgExcel.java @@ -0,0 +1,21 @@ +package com.thing.transport.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2022-12-20 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class MqttBrokerMsgExcel { + @Excel(name = "接收时间") + private String createDate; + @Excel(name = "主题") + private String topic; + @Excel(name = "消息") + private String msg; +} diff --git a/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerConnectMapper.java b/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerConnectMapper.java new file mode 100644 index 0000000..d68cb31 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerConnectMapper.java @@ -0,0 +1,22 @@ +package com.thing.transport.modules.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.transport.modules.entity.MqttBrokerConnectEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; + +/** +* mqttBroker连接日志 +* +* @author zhh zhh +* @since 0.0.1 2022-11-25 +*/ +@Mapper +public interface MqttBrokerConnectMapper extends PowerBaseMapper { + + /** + * 清空表 + */ + @Update("truncate table mqtt_broker_connect") + void clear(); +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerMsgMapper.java b/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerMsgMapper.java new file mode 100644 index 0000000..8def1e6 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/mapper/MqttBrokerMsgMapper.java @@ -0,0 +1,22 @@ +package com.thing.transport.modules.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.transport.modules.entity.MqttBrokerMsgEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; + +/** +* mqttBroker调试日志 +* +* @author zhh zhh +* @since 0.0.1 2022-11-25 +*/ +@Mapper +public interface MqttBrokerMsgMapper extends PowerBaseMapper { + + /** + * 清空表 + */ + @Update("truncate table mqtt_broker_msg") + void clear(); +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerConnectService.java b/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerConnectService.java new file mode 100644 index 0000000..88bc970 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerConnectService.java @@ -0,0 +1,37 @@ +package com.thing.transport.modules.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.transport.modules.dto.MqttBrokerConnectDTO; +import com.thing.transport.modules.entity.MqttBrokerConnectEntity; + +import java.util.List; +import java.util.Map; + +/** + * mqttBroker连接日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +public interface MqttBrokerConnectService extends IBaseService { + + /** + * 修改最新连接时间 + * + * @param id 主键 + * @param lastActivityTime 最新连接时间 + * @param msgNum 发送消息数量 + */ + void updateLastTime(String id, long lastActivityTime, Integer msgNum); + + /** + * 清空 + */ + void clear(); + + PageData page(Map params); + + List list(Map params); +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerMsgService.java b/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerMsgService.java new file mode 100644 index 0000000..66e3c19 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/service/MqttBrokerMsgService.java @@ -0,0 +1,29 @@ +package com.thing.transport.modules.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.transport.modules.dto.MqttBrokerMsgDTO; +import com.thing.transport.modules.entity.MqttBrokerMsgEntity; + +import java.util.List; +import java.util.Map; + +/** + * mqttBroker调试日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +public interface MqttBrokerMsgService extends IBaseService { + + /** + * 删除 + * @param sessionId 连接主键 + */ + void deleteBySessionId(String sessionId); + + PageData page(Map params); + + List list(Map params); +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerConnectServiceImpl.java b/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerConnectServiceImpl.java new file mode 100644 index 0000000..0f8012e --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerConnectServiceImpl.java @@ -0,0 +1,88 @@ +package com.thing.transport.modules.service.impl; + + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.transport.modules.mapper.MqttBrokerConnectMapper; +import com.thing.transport.modules.mapper.MqttBrokerMsgMapper; +import com.thing.transport.modules.dto.MqttBrokerConnectDTO; +import com.thing.transport.modules.entity.MqttBrokerConnectEntity; +import com.thing.transport.modules.service.MqttBrokerConnectService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * mqttBroker连接日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@Service +public class MqttBrokerConnectServiceImpl extends BaseServiceImpl implements MqttBrokerConnectService { + + @Autowired + private MqttBrokerMsgMapper mqttBrokerMsgDao; + + @Override + public QueryWrapper getWrapper(Map params) { + String clientId = (String) params.get("clientId"); + String clientIp = (String) params.get("clientIp"); + String userName = (String) params.get("userName"); + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like( MqttBrokerConnectEntity::getClientId, clientId,StringUtils.isNotBlank(clientId)) + .like( MqttBrokerConnectEntity::getUserName, userName,StringUtils.isNotBlank(userName)) + .like(MqttBrokerConnectEntity::getClientIp, clientIp,StringUtils.isNotBlank(clientIp)) + .ge(MqttBrokerConnectEntity::getStartTime, StringUtils.isNotBlank(startTime) ? Long.parseLong(startTime) : -1L,StringUtils.isNotBlank(startTime)) + .le(MqttBrokerConnectEntity::getStartTime, StringUtils.isNotBlank(endTime) ? Long.parseLong(endTime) : -1L,StringUtils.isNotBlank(endTime)) + .orderBy(MqttBrokerConnectEntity::getStartTime,Boolean.FALSE); + return wrapper; + } + + /** + * 修改最新连接时间 + * + * @param id 主键 + * @param lastActivityTime 最新连接时间 + * @param msgNum 发送消息数量 + */ + @Override + public void updateLastTime(String id, long lastActivityTime, Integer msgNum) { + MqttBrokerConnectEntity entity =new MqttBrokerConnectEntity(); + entity.setLastTime(lastActivityTime); + entity.setMsgNum(Objects.isNull(msgNum) ? 0 : msgNum); + entity.setId(id); + mapper.update(entity); + } + + /** + * 清空 + */ + @Override + public void clear() { + this.clear(); + mqttBrokerMsgDao.clear(); + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, MqttBrokerConnectDTO.class); + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params), MqttBrokerConnectDTO.class); + } +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerMsgServiceImpl.java b/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerMsgServiceImpl.java new file mode 100644 index 0000000..15fcb00 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/modules/service/impl/MqttBrokerMsgServiceImpl.java @@ -0,0 +1,64 @@ +package com.thing.transport.modules.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.transport.modules.mapper.MqttBrokerMsgMapper; +import com.thing.transport.modules.dto.MqttBrokerMsgDTO; +import com.thing.transport.modules.entity.MqttBrokerMsgEntity; +import com.thing.transport.modules.service.MqttBrokerMsgService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * mqttBroker调试日志 + * + * @author zhh zhh + * @since 0.0.1 2022-11-25 + */ +@Service +public class MqttBrokerMsgServiceImpl extends BaseServiceImpl implements MqttBrokerMsgService { + @Override + public QueryWrapper getWrapper(Map params) { + String sessionId = (String) params.get("sessionId"); + String topic = (String) params.get("topic"); + String msg = (String) params.get("msg"); + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(MqttBrokerMsgEntity::getSessionId, StringUtils.isNotBlank(sessionId) ? sessionId : "-1") + .like( MqttBrokerMsgEntity::getTopic, topic,StringUtils.isNotBlank(topic)) + .like( MqttBrokerMsgEntity::getMsg, msg,StringUtils.isNotBlank(msg)) + .ge( MqttBrokerMsgEntity::getCreateDate, startTime,StringUtils.isNotBlank(startTime)) + .le( MqttBrokerMsgEntity::getCreateDate, endTime,StringUtils.isNotBlank(endTime)); + return wrapper; + } + + /** + * 删除 + * + * @param sessionId 连接主键 + */ + @Override + public void deleteBySessionId(String sessionId) { + mapper.deleteByQuery(QueryWrapper.create().eq(MqttBrokerMsgEntity::getSessionId, sessionId)); + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, MqttBrokerMsgDTO.class); + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params), MqttBrokerMsgDTO.class); + } +} \ No newline at end of file diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTopics.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTopics.java new file mode 100644 index 0000000..c7aa916 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTopics.java @@ -0,0 +1,18 @@ +package com.thing.transport.mqtt.broker; + +/** + * @author zhenghh. 2022-08-30 + **/ +public class MqttTopics { + + + public static final String BASE_DEVICE_API_TOPIC = "v1/devices"; + public static final String DEVICE_TELEMETRY_TOPIC = BASE_DEVICE_API_TOPIC + "/me/telemetry"; + + public static final String BASE_GATEWAY_API_TOPIC = "v1/gateway"; + public static final String GATEWAY_TELEMETRY_TOPIC = BASE_GATEWAY_API_TOPIC + "/telemetry"; + + private MqttTopics() { + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportContext.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportContext.java new file mode 100644 index 0000000..a05fc2f --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportContext.java @@ -0,0 +1,28 @@ +package com.thing.transport.mqtt.broker; + +import com.thing.transport.api.AbstractTransportContext; +import com.thing.transport.mqtt.broker.adaptors.MqttTransportAdaptor; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-08-30 + **/ +@Getter +@Component +@ConditionalOnExpression("'${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true'") +public class MqttTransportContext extends AbstractTransportContext { + + @Autowired + private MqttTransportAdaptor adaptor; + +// @Autowired +// private ThingService thingService; + + @Value("${transport.mqtt.netty.max_payload_size}") + private Integer maxPayloadSize; + +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportHandler.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportHandler.java new file mode 100644 index 0000000..507e0de --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportHandler.java @@ -0,0 +1,303 @@ +package com.thing.transport.mqtt.broker; + +import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_ACCEPTED; +import static io.netty.handler.codec.mqtt.MqttConnectReturnCode.CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD; +import static io.netty.handler.codec.mqtt.MqttMessageType.*; +import static io.netty.handler.codec.mqtt.MqttQoS.*; + +import com.thing.common.core.enumeration.GateWayStatus; +import com.thing.gen.queue.QueueProto.SessionInfoProto; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.transport.api.TransportService; +import com.thing.transport.api.TransportServiceCallback; +import com.thing.transport.api.adaptor.AdaptorException; +import com.thing.transport.api.session.TransportSessionUtil; +import com.thing.transport.mqtt.broker.adaptors.MqttTransportAdaptor; +import com.thing.transport.mqtt.broker.session.DeviceSessionCtx; +import com.thing.transport.mqtt.broker.session.MqttTopicMatcher; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.codec.mqtt.*; +import io.netty.util.ReferenceCountUtil; +import io.netty.util.concurrent.Future; +import io.netty.util.concurrent.GenericFutureListener; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; + +import java.net.InetSocketAddress; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +@Slf4j +public class MqttTransportHandler extends ChannelInboundHandlerAdapter implements GenericFutureListener> { + + private static final MqttQoS MAX_SUPPORTED_QOS_LVL = AT_LEAST_ONCE; + + private final UUID sessionId; + private final MqttTransportContext context; + private final MqttTransportAdaptor adaptor; + private final TransportService transportService; + // private final ThingService thingService; + private final ConcurrentMap mqttQoSMap; + + private volatile SessionInfoProto sessionInfo; + private final DeviceSessionCtx deviceSessionCtx; + + MqttTransportHandler(MqttTransportContext context) { + this.sessionId = UUID.randomUUID(); + this.context = context; + this.transportService = context.getTransportService(); +// this.thingService = context.getThingService(); + this.adaptor = context.getAdaptor(); + this.mqttQoSMap = new ConcurrentHashMap<>(); + this.deviceSessionCtx = new DeviceSessionCtx(sessionId, mqttQoSMap); + } + + /** + * 从客户端收到新的数据时,这个方法会在收到消息时被调用 + */ + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) { + log.trace("[{}] Processing msg: {}", sessionId, msg); + try { + if (msg instanceof MqttMessage) { + processMqttMsg(ctx, (MqttMessage) msg); + } else { + ctx.close(); + } + } catch (Exception e) { + log.warn("[{}] Failed to submit session event", sessionId, e); + ctx.writeAndFlush(createMqttConnAckMsg(MqttConnectReturnCode.CONNECTION_REFUSED_SERVER_UNAVAILABLE)); + } finally { + ReferenceCountUtil.safeRelease(msg); + } + } + + private void processMqttMsg(ChannelHandlerContext ctx, MqttMessage msg) { + InetSocketAddress address = (InetSocketAddress) ctx.channel().remoteAddress(); + if (msg.fixedHeader() == null) { + log.info("[{}:{}] Invalid message received", address.getHostName(), address.getPort()); + processDisconnect(ctx); + return; + } + deviceSessionCtx.setChannel(ctx); + switch (msg.fixedHeader().messageType()) { + case CONNECT: + processConnect(ctx, (MqttConnectMessage) msg); + break; + case PUBLISH: + processPublish(ctx, (MqttPublishMessage) msg); + break; + /* todo 暂不支持订阅 + case SUBSCRIBE: + processSubscribe(ctx, (MqttSubscribeMessage) msg); + break; + case UNSUBSCRIBE: + processUnsubscribe(ctx, (MqttUnsubscribeMessage) msg); + break;*/ + case PINGREQ: + if (checkConnected(ctx, msg)) { + ctx.writeAndFlush(new MqttMessage(new MqttFixedHeader(PINGRESP, false, AT_MOST_ONCE, false, 0))); + transportService.reportActivity(sessionInfo); + } + break; + case DISCONNECT: + if (checkConnected(ctx, msg)) { + processDisconnect(ctx); + } + break; + default: + break; + } + } + + @Override + public void operationComplete(Future future) throws Exception { + doDisconnect(); + } + + private void processPublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg) { + if (!checkConnected(ctx, mqttMsg)) { + return; + } + String topicName = mqttMsg.variableHeader().topicName(); + int msgId = mqttMsg.variableHeader().packetId(); + log.trace("[{}] Processing publish msg [{}][{}]!", sessionId, topicName, msgId); + processDevicePublish(ctx, mqttMsg, topicName, msgId); + } + + private void processDevicePublish(ChannelHandlerContext ctx, MqttPublishMessage mqttMsg, String topicName, int msgId) { + try { + UUID sessionId = TransportSessionUtil.toSessionId(sessionInfo); + if (topicName.equals(MqttTopics.DEVICE_TELEMETRY_TOPIC)) { + //单设备 + TransportMsg postTelemetryMsg = adaptor.convertToPostTelemetry(sessionInfo.getThingCode(), deviceSessionCtx, mqttMsg); + transportService.process(sessionInfo, postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + } else if (topicName.startsWith(MqttTopics.GATEWAY_TELEMETRY_TOPIC)) { + //网关 多设备 + TransportMsg postTelemetryMsg = adaptor.convertToPostTelemetry(deviceSessionCtx, mqttMsg); + if(GateWayStatus.GATE_WAY.getValue().equals(sessionInfo.getGateway())) { + transportService.process(sessionInfo, postTelemetryMsg, getPubAckCallback(ctx, msgId, postTelemetryMsg)); + } else { + log.warn("【{}】 is not gateway, message {} will be filtered", sessionInfo.getThingCode(), postTelemetryMsg.getTelemetryMsg()); + transportService.reportActivity(sessionInfo); + } + } else { + transportService.reportActivity(sessionInfo); + } + //修改消息数量 + transportService.reportMsgNum(sessionId); + //调试模式 消息入库 + transportService.debugSave(sessionId, topicName, mqttMsg.payload()); + } catch (AdaptorException e) { + log.warn("[{}] Failed to process publish msg [{}][{}]", sessionId, topicName, msgId, e); + log.info("[{}] Closing current session due to invalid publish msg [{}][{}]", sessionId, topicName, msgId); + ctx.close(); + } + } + + private TransportServiceCallback getPubAckCallback(final ChannelHandlerContext ctx, final int msgId, final T msg) { + return new TransportServiceCallback<>() { + @Override + public void onSuccess(Void dummy) { + log.trace("[{}] Published msg: {}", sessionId, msg); + if (msgId > 0) { + ctx.writeAndFlush(createMqttPubAckMsg(msgId)); + } + } + + @Override + public void onError(Throwable e) { + log.trace("[{}] Failed to publish msg: {}", sessionId, msg, e); + processDisconnect(ctx); + } + }; + } + + private void processConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { + log.info("[{}] Processing connect msg for client: {}!", sessionId, msg.payload().clientIdentifier()); + processAuthTokenConnect(ctx, msg); + } + + private void processAuthTokenConnect(ChannelHandlerContext ctx, MqttConnectMessage msg) { + String userName = msg.payload().userName(); + String clientId = msg.payload().clientIdentifier(); + String clientIp = ctx.pipeline().channel().remoteAddress().toString(); + log.info("[{}] Processing connect msg for client with user name: {}! ==连接IP==>{}", sessionId, userName, clientIp); +// Optional optional = thingService.getByToken(userName); +// if (optional.isPresent()) { +// connectSuccess(ctx, userName, clientId, clientIp, optional.get()); +// } else { +// log.info("[{}] connection refused bad username or password user name: {}!", sessionId, userName); +// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_REFUSED_BAD_USER_NAME_OR_PASSWORD)); +// ctx.close(); +// } + } + +// private void connectSuccess(ChannelHandlerContext ctx, String userName, String clientId, String clientIp, ThingModel thingModel) { +// deviceSessionCtx.setConnected(true); +// sessionInfo = TransportSessionUtil.createSession(sessionId, thingModel.getCode(), thingModel.getGateway(), +// clientId, userName, StringUtils.stripStart(clientIp, "/"), System.currentTimeMillis()); +// transportService.registerSession(sessionInfo); +// ctx.writeAndFlush(createMqttConnAckMsg(CONNECTION_ACCEPTED)); +// } + + + private boolean checkConnected(ChannelHandlerContext ctx, MqttMessage msg) { + if (deviceSessionCtx.isConnected()) { + return true; + } else { + log.info("[{}] Closing current session due to invalid msg order: {}", sessionId, msg); + ctx.close(); + return false; + } + } + + private MqttConnAckMessage createMqttConnAckMsg(MqttConnectReturnCode returnCode) { + MqttFixedHeader mqttFixedHeader = + new MqttFixedHeader(CONNACK, false, AT_MOST_ONCE, false, 0); + MqttConnAckVariableHeader mqttConnAckVariableHeader = + new MqttConnAckVariableHeader(returnCode, true); + return new MqttConnAckMessage(mqttFixedHeader, mqttConnAckVariableHeader); + } + + private void processDisconnect(ChannelHandlerContext ctx) { + ctx.close(); + log.info("[{}] Client disconnected!", sessionId); + doDisconnect(); + } + + private void doDisconnect() { + if (deviceSessionCtx.isConnected()) { + transportService.deregisterSession(sessionInfo); + deviceSessionCtx.setDisconnected(); + } + } + + public static MqttPubAckMessage createMqttPubAckMsg(int requestId) { + MqttFixedHeader mqttFixedHeader = + new MqttFixedHeader(PUBACK, false, AT_MOST_ONCE, false, 0); + MqttMessageIdVariableHeader mqttMsgIdVariableHeader = + MqttMessageIdVariableHeader.from(requestId); + return new MqttPubAckMessage(mqttFixedHeader, mqttMsgIdVariableHeader); + } + + private void processSubscribe(ChannelHandlerContext ctx, MqttSubscribeMessage mqttMsg) { + if (!checkConnected(ctx, mqttMsg)) { + return; + } + log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); + List grantedQoSList = new ArrayList<>(); + //todo 暂不允许订阅 + for (MqttTopicSubscription subscription : mqttMsg.payload().topicSubscriptions()) { + grantedQoSList.add(FAILURE.value()); + } + transportService.reportActivity(sessionInfo); + ctx.writeAndFlush(createSubAckMessage(mqttMsg.variableHeader().messageId(), grantedQoSList)); + } + + private void registerSubQoS(String topic, List grantedQoSList, MqttQoS reqQoS) { + grantedQoSList.add(getMinSupportedQos(reqQoS)); + mqttQoSMap.put(new MqttTopicMatcher(topic), getMinSupportedQos(reqQoS)); + } + + + private static MqttSubAckMessage createSubAckMessage(Integer msgId, List grantedQoSList) { + MqttFixedHeader mqttFixedHeader = + new MqttFixedHeader(SUBACK, false, AT_MOST_ONCE, false, 0); + MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); + MqttSubAckPayload mqttSubAckPayload = new MqttSubAckPayload(grantedQoSList); + return new MqttSubAckMessage(mqttFixedHeader, mqttMessageIdVariableHeader, mqttSubAckPayload); + } + + private static int getMinSupportedQos(MqttQoS reqQoS) { + return Math.min(reqQoS.value(), MAX_SUPPORTED_QOS_LVL.value()); + } + + + private void processUnsubscribe(ChannelHandlerContext ctx, MqttUnsubscribeMessage mqttMsg) { + if (!checkConnected(ctx, mqttMsg)) { + return; + } + log.trace("[{}] Processing subscription [{}]!", sessionId, mqttMsg.variableHeader().messageId()); + for (String topicName : mqttMsg.payload().topics()) { + mqttQoSMap.remove(new MqttTopicMatcher(topicName)); + } + transportService.reportActivity(sessionInfo); + ctx.writeAndFlush(createUnSubAckMessage(mqttMsg.variableHeader().messageId())); + } + + private MqttMessage createUnSubAckMessage(int msgId) { + MqttFixedHeader mqttFixedHeader = + new MqttFixedHeader(UNSUBACK, false, AT_MOST_ONCE, false, 0); + MqttMessageIdVariableHeader mqttMessageIdVariableHeader = MqttMessageIdVariableHeader.from(msgId); + return new MqttMessage(mqttFixedHeader, mqttMessageIdVariableHeader); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportServerInitializer.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportServerInitializer.java new file mode 100644 index 0000000..06cbfb8 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportServerInitializer.java @@ -0,0 +1,32 @@ +package com.thing.transport.mqtt.broker; + +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.mqtt.MqttDecoder; +import io.netty.handler.codec.mqtt.MqttEncoder; + +/** + * @author Andrew Shvayka + */ +public class MqttTransportServerInitializer extends ChannelInitializer { + + private final MqttTransportContext context; + + public MqttTransportServerInitializer(MqttTransportContext context) { + this.context = context; + } + + @Override + public void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + pipeline.addLast("decoder", new MqttDecoder(context.getMaxPayloadSize())); + pipeline.addLast("encoder", MqttEncoder.INSTANCE); + + MqttTransportHandler handler = new MqttTransportHandler(context); + + pipeline.addLast(handler); + ch.closeFuture().addListener(handler); + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportService.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportService.java new file mode 100644 index 0000000..3e48a4a --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/MqttTransportService.java @@ -0,0 +1,79 @@ +package com.thing.transport.mqtt.broker; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelOption; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.util.ResourceLeakDetector; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +/** + * @author Andrew Shvayka + */ +@Service("MqttTransportService") +@ConditionalOnExpression("'${transport.api_enabled:true}'=='true' && '${transport.mqtt.enabled}'=='true'") +@Slf4j +public class MqttTransportService { + + @Value("${transport.mqtt.bind_address}") + private String host; + @Value("${transport.mqtt.bind_port}") + private Integer port; + + @Value("${transport.mqtt.netty.leak_detector_level}") + private String leakDetectorLevel; + @Value("${transport.mqtt.netty.boss_group_thread_count}") + private Integer bossGroupThreadCount; + @Value("${transport.mqtt.netty.worker_group_thread_count}") + private Integer workerGroupThreadCount; + @Value("${transport.mqtt.netty.so_keep_alive}") + private boolean keepAlive; + + @Autowired + private MqttTransportContext context; + + private Channel serverChannel; + private EventLoopGroup bossGroup; + private EventLoopGroup workerGroup; + + @PostConstruct + public void init() throws Exception { + log.info("Setting resource leak detector level to {}", leakDetectorLevel); + ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.valueOf(leakDetectorLevel.toUpperCase())); + + log.info("Starting MQTT transport..."); + bossGroup = new NioEventLoopGroup(bossGroupThreadCount); + workerGroup = new NioEventLoopGroup(workerGroupThreadCount); + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new MqttTransportServerInitializer(context)) + .childOption(ChannelOption.SO_KEEPALIVE, keepAlive); + + serverChannel = b.bind(host, port).sync().channel(); + log.info("Mqtt transport started!"); + } + + @PreDestroy + public void shutdown() throws InterruptedException { + log.info("Stopping MQTT transport!"); + try { + //修改连接日志 + context.getTransportService().deregisterSession(); + serverChannel.close().sync(); + } finally { + workerGroup.shutdownGracefully(); + bossGroup.shutdownGracefully(); + } + log.info("MQTT transport stopped!"); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/JsonMqttAdaptor.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/JsonMqttAdaptor.java new file mode 100644 index 0000000..d592da0 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/JsonMqttAdaptor.java @@ -0,0 +1,87 @@ +package com.thing.transport.mqtt.broker.adaptors; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.google.gson.JsonSyntaxException; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.gen.queue.QueueProto; +import com.thing.transport.api.adaptor.AdaptorException; +import com.thing.transport.api.adaptor.JsonConverter; +import com.thing.transport.mqtt.broker.session.AbstractMqttDeviceAwareSessionContext; +import io.netty.buffer.ByteBuf; +import io.netty.handler.codec.mqtt.MqttPublishMessage; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.UUID; + +/** + * @author Andrew Shvayka + */ +@Component("JsonMqttAdaptor") +@Slf4j +public class JsonMqttAdaptor implements MqttTransportAdaptor { + + private static final Charset UTF8 = StandardCharsets.UTF_8; + + /** + * 多设备转换mqtt消息 + * + * @param ctx session + * @param inbound message + * @return TelemetryMsg + * @throws AdaptorException e + */ + @Override + public QueueProto.TransportMsg convertToPostTelemetry(AbstractMqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); + try { + return JsonConverter.convertToTelemetryProto(JsonParser.parseString(payload), QueueOriginType.MQTT_BROKER.name()); + } catch (IllegalStateException | JsonSyntaxException ex) { + throw new AdaptorException(ex); + } + } + + /** + * 单设备转换mqtt消息 + * + * @param thingCode 设备编码 + * @param ctx session + * @param inbound message + * @return TelemetryMsg + * @throws AdaptorException e + */ + @Override + public QueueProto.TransportMsg convertToPostTelemetry(String thingCode, AbstractMqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException { + String payload = validatePayload(ctx.getSessionId(), inbound.payload(), false); + try { + return JsonConverter.convertToTelemetryProto(thingCode, JsonParser.parseString(payload), QueueOriginType.MQTT_BROKER.name()); + } catch (IllegalStateException | JsonSyntaxException ex) { + throw new AdaptorException(ex); + } + } + + public static JsonElement validateJsonPayload(UUID sessionId, ByteBuf payloadData) throws AdaptorException { + String payload = validatePayload(sessionId, payloadData, false); + try { + return JsonParser.parseString(payload); + } catch (JsonSyntaxException ex) { + log.warn("Payload is in incorrect format: {}", payload); + throw new AdaptorException(ex); + } + } + + public static String validatePayload(UUID sessionId, ByteBuf payloadData, boolean isEmptyPayloadAllowed) throws AdaptorException { + String payload = payloadData.toString(UTF8); + if (payload == null) { + log.warn("[{}] Payload is empty!", sessionId); + if (!isEmptyPayloadAllowed) { + throw new AdaptorException(new IllegalArgumentException("Payload is empty!")); + } + } + return payload; + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/MqttTransportAdaptor.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/MqttTransportAdaptor.java new file mode 100644 index 0000000..84595bc --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/adaptors/MqttTransportAdaptor.java @@ -0,0 +1,33 @@ +package com.thing.transport.mqtt.broker.adaptors; + +import io.netty.handler.codec.mqtt.MqttPublishMessage; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.transport.api.adaptor.AdaptorException; +import com.thing.transport.mqtt.broker.session.AbstractMqttDeviceAwareSessionContext; + +/** + * @author zhenghh. 2022-08-30 + **/ +public interface MqttTransportAdaptor { + + /** + * 多设备转换mqtt消息 + * + * @param ctx session + * @param inbound message + * @return TransportMsg + * @throws AdaptorException e + */ + TransportMsg convertToPostTelemetry(AbstractMqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; + + /** + * 单设备转换mqtt消息 + * + * @param entityId 设备ID + * @param ctx session + * @param inbound message + * @return TransportMsg + * @throws AdaptorException e + */ + TransportMsg convertToPostTelemetry(String entityId, AbstractMqttDeviceAwareSessionContext ctx, MqttPublishMessage inbound) throws AdaptorException; +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractDeviceAwareSessionContext.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractDeviceAwareSessionContext.java new file mode 100644 index 0000000..bff0f9e --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractDeviceAwareSessionContext.java @@ -0,0 +1,26 @@ +package com.thing.transport.mqtt.broker.session; + +import com.thing.transport.api.session.SessionContext; +import lombok.Data; +import lombok.Getter; + +import java.util.UUID; + +/** + * @author zhenghh + */ +@Data +public abstract class AbstractDeviceAwareSessionContext implements SessionContext { + + @Getter + protected final UUID sessionId; + private volatile boolean connected; + + public boolean isConnected() { + return connected; + } + + public void setDisconnected() { + this.connected = false; + } +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractMqttDeviceAwareSessionContext.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractMqttDeviceAwareSessionContext.java new file mode 100644 index 0000000..60e2055 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/AbstractMqttDeviceAwareSessionContext.java @@ -0,0 +1,40 @@ +package com.thing.transport.mqtt.broker.session; + +import io.netty.handler.codec.mqtt.MqttQoS; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentMap; +import java.util.stream.Collectors; + +/** + * Created by ashvayka on 30.08.18. + */ +public abstract class AbstractMqttDeviceAwareSessionContext extends AbstractDeviceAwareSessionContext { + + private final ConcurrentMap mqttQoSMap; + + public AbstractMqttDeviceAwareSessionContext(UUID sessionId, ConcurrentMap mqttQoSMap) { + super(sessionId); + this.mqttQoSMap = mqttQoSMap; + } + + public ConcurrentMap getMqttQoSMap() { + return mqttQoSMap; + } + + public MqttQoS getQoSForTopic(String topic) { + List qosList = mqttQoSMap.entrySet() + .stream() + .filter(entry -> entry.getKey().matches(topic)) + .map(Map.Entry::getValue) + .collect(Collectors.toList()); + if (!qosList.isEmpty()) { + return MqttQoS.valueOf(qosList.get(0)); + } else { + return MqttQoS.AT_LEAST_ONCE; + } + } + +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/DeviceSessionCtx.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/DeviceSessionCtx.java new file mode 100644 index 0000000..6d2d19c --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/DeviceSessionCtx.java @@ -0,0 +1,33 @@ +package com.thing.transport.mqtt.broker.session; + +import io.netty.channel.ChannelHandlerContext; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.util.UUID; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Andrew Shvayka + */ +@Slf4j +public class DeviceSessionCtx extends AbstractMqttDeviceAwareSessionContext { + + @Getter + private ChannelHandlerContext channel; + private AtomicInteger msgIdSeq = new AtomicInteger(0); + + public DeviceSessionCtx(UUID sessionId, ConcurrentMap mqttQoSMap) { + super(sessionId, mqttQoSMap); + } + + public void setChannel(ChannelHandlerContext channel) { + this.channel = channel; + } + + @Override + public int nextMsgId() { + return msgIdSeq.incrementAndGet(); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/MqttTopicMatcher.java b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/MqttTopicMatcher.java new file mode 100644 index 0000000..17e1751 --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/mqtt/broker/session/MqttTopicMatcher.java @@ -0,0 +1,40 @@ +package com.thing.transport.mqtt.broker.session; + +import java.util.regex.Pattern; + +public class MqttTopicMatcher { + + private final String topic; + private final Pattern topicRegex; + + public MqttTopicMatcher(String topic) { + if(topic == null){ + throw new NullPointerException("topic"); + } + this.topic = topic; + this.topicRegex = Pattern.compile(topic.replace("+", "[^/]+").replace("#", ".+") + "$"); + } + + public String getTopic() { + return topic; + } + + public boolean matches(String topic){ + return this.topicRegex.matcher(topic).matches(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + MqttTopicMatcher that = (MqttTopicMatcher) o; + + return topic.equals(that.topic); + } + + @Override + public int hashCode() { + return topic.hashCode(); + } +} diff --git a/common/transport/src/main/java/com/thing/transport/util/ConvertSendUtil.java b/common/transport/src/main/java/com/thing/transport/util/ConvertSendUtil.java new file mode 100644 index 0000000..f4a44fc --- /dev/null +++ b/common/transport/src/main/java/com/thing/transport/util/ConvertSendUtil.java @@ -0,0 +1,67 @@ +package com.thing.transport.util; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.data.dto.TelemetrySaveDTO; +import com.thing.gen.queue.QueueProto; +import com.thing.transport.api.TransportService; +import com.thing.transport.api.TransportServiceCallback; +import com.thing.transport.api.session.TransportSessionUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ConvertSendUtil { + + private final TransportService transportService; + + /** + * List saveDTOList 转换为 List protoList + * @param saveDTOList + */ + public void convertPushMsg(List saveDTOList){ + List tsKvList = new ArrayList<>(); + List protoList = new ArrayList<>(); + saveDTOList.forEach(temp->{ + String thingCode = temp.getThingCode(); + temp.getTsKvList().forEach(info->{ + Long ts = info.getTs(); + Map tskvMap = info.getValues(); + tskvMap.forEach((key, val) -> { + tsKvList.add(QueueProto.TsKvProto.newBuilder().setTs(ts).setKey(key).setVal(val.toString()).build()); + }); + }); + if (CollectionUtil.isNotEmpty(tsKvList)) { + protoList.add(QueueProto.TelemetryProto.newBuilder().setThingCode(thingCode).addAllTsKvList(tsKvList).build()); + } + }); + if (CollectionUtil.isNotEmpty(protoList)) { + transportService.process(TransportSessionUtil.createSession(UUID.randomUUID()), + QueueProto.TransportMsg.newBuilder().setTelemetryMsg(QueueProto.TelemetryMsg.newBuilder().addAllTelemetryList(protoList)) + .setOrigin(QueueOriginType.API.name()).build(), + TransportServiceCallback.EMPTY); + } + + } + + + + + + + + + + + + + +} diff --git a/common/tskv/pom.xml b/common/tskv/pom.xml new file mode 100644 index 0000000..b40950f --- /dev/null +++ b/common/tskv/pom.xml @@ -0,0 +1,111 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + jar + tskv + com.thing.common + ThingBI Server Common TsKv + + ${basedir}/../.. + + + + + com.thing.common + orm + + + + com.google.guava + guava + provided + + + com.thing.common + util + + + + com.thing.common + queue + + + org.projectlombok + lombok + + + joda-time + joda-time + + + org.thingsboard.common + data + 3.2.2-SNAPSHOT + + + com.mysql + mysql-connector-j + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/common/tskv/src/main/java/com/thing/common/tskv/NativeSQLTool.java b/common/tskv/src/main/java/com/thing/common/tskv/NativeSQLTool.java new file mode 100644 index 0000000..e9a327c --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/NativeSQLTool.java @@ -0,0 +1,508 @@ +package com.thing.common.tskv; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.RowUtil; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.DataBaseType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.util.time.DaXiaUtils; +import org.apache.commons.collections4.CollectionUtils; +import org.joda.time.DateTime; + +import java.util.*; + +public class NativeSQLTool { + + private static final String VIEW_TS_KV = "view_ts_kv"; + private static final String LATEST = "ts_kv_latest"; + public static final String TS_KV = "thing_ts_kv"; + private static final String THING_CODE = "thing_code"; + private static final String ATTR_KEY = "attr_key"; + private static final String VAL = "val"; + private static final String TS = "ts"; + + /** + * 根据聚合间隔时长,物编号,属性编号分组求每个间隔时间内的最大值,用于技术每个AM + * + * @param thingCode 物编号 + * @param attrKey 属性编号 + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param interval 聚合间隔时长(单位:毫秒) + * @return + */ + public static String amQueryMax(String thingCode, String attrKey, long stTs, long enTs, long interval) { + StringBuilder sql = new StringBuilder("SELECT "). + append(THING_CODE).append(", "). + append(ATTR_KEY).append(", "). + append(AggType.MAX).append("(").append(VAL).append(") AS value , "). + append(enTs). + append(" - (FLOOR((").append(enTs).append(" - ").append(TS).append(" ) / "). + append(interval).append(") * ").append(interval).append(") AS time ").append("FROM "). + append(TS_KV).append(" WHERE ").append(THING_CODE).append(" = '"). + append(thingCode).append("' ").append("AND ").append(ATTR_KEY).append(" = '").append(attrKey). + append("' ").append("AND ").append(TS).append(" >= ").append(stTs).append(" AND ").append(TS). + append(" <= ").append(enTs).append(" GROUP BY ").append(THING_CODE).append(", ").append(ATTR_KEY).append(", "). + append(" time ORDER BY time DESC;"); + return sql.toString(); + } + + /** + * 根据物编号,属性编号,开始和结束时间 分组汇总求和,将根据数量判断是否用IN + * + * @param setMap 物对应属性编号map + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param dataType 数据库类型 + * @return String + */ + public static String thingsAndKeysSum(Map> setMap, long stTs, long enTs, String dataType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE + ", ").append(ATTR_KEY + ", "); + sql.append(" SUM(CAST(").append(VAL); + if ("mysql".equals(dataType) || "tidb".equals(dataType)) { + sql.append(" AS double )) as value "); + } else { + sql.append(" AS numeric)) as value "); + } + sql.append(" from ").append(TS_KV).append(" where "); + sql.append(TS).append(" >= ").append(stTs).append(" AND ").append(TS).append(" < ").append(enTs).append(" AND "); + List thingCodes = setMap.keySet().stream().toList(); + if (thingCodes.size() == 1) { + sql.append(THING_CODE + " = '").append(thingCodes.get(0)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = setMap.get(thingCodes.get(0)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("' "); + } else { + sql.append(" IN ("); + for (int i = 0; i < attrKey.size(); i++) { + sql.append("'"); + if (i == attrKey.size() - 1) { + sql.append(attrKey.get(i)).append("') "); + } else { + sql.append(attrKey.get(i)).append("',"); + } + } + } + } else if (thingCodes.size() > 1) { + sql.append("("); + for (int i = 0; i < thingCodes.size(); i++) { + sql.append("(" + THING_CODE + " = '").append(thingCodes.get(i)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = setMap.get(thingCodes.get(i)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("'"); + } else { + sql.append(" IN ("); + for (int j = 0; j < attrKey.size(); j++) { + sql.append("'"); + if (j == attrKey.size() - 1) { + sql.append(attrKey.get(j)).append("')"); + } else { + sql.append(attrKey.get(j)).append("',"); + } + } + } + if (i != thingCodes.size() - 1) { + sql.append(") OR "); + } else { + sql.append("))"); + } + } + } + sql.append(" group by ").append(THING_CODE + ", ").append(ATTR_KEY + ";"); + return sql.toString(); + } + + /** + * 拼接 每小时数据汇总的SQL语句,注意 MsySql 的数据类型 + * + * @param thingCodes 物编号列表 + * @param attrKey 属性列表 + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param interval 时间间隔(单位:分),默认60分 + * @param dataType 数据库类型,不同数据库sql函数不同 + * @return String + */ + public static String hhQuerySum(List thingCodes, List attrKey, long stTs, long enTs, Integer interval, String dataType) { + int inter = interval * 60 * 1000; + StringBuilder sql = new StringBuilder("SELECT "). + append(THING_CODE).append(", "). + append(ATTR_KEY).append(", "). + append(" (FLOOR(( ").append(TS).append(" - ").append(stTs).append(") / ").append(inter).append(") * ").append(inter).append(" + "). + append(stTs).append(" ) AS time, SUM(CAST( ").append(VAL); + if ("mysql".equals(dataType) || "tidb".equals(dataType)) { + sql.append(" AS double )) as value "); + } else { + sql.append(" AS numeric)) as value "); + } + sql.append(" from ").append(TS_KV).append(" where "); + sql.append(THING_CODE); + if (thingCodes.size() == 1) { + sql.append(" = '").append(thingCodes.get(0)).append("' and "); + } else { + sql.append(" in ("); + for (int i = 0; i < thingCodes.size(); i++) { + sql.append("'"); + if (i == thingCodes.size() - 1) { + sql.append(thingCodes.get(i)).append("' ) and "); + } else { + sql.append(thingCodes.get(i)).append("',"); + } + } + } + sql.append(ATTR_KEY); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("' and "); + } else { + sql.append(" in ("); + for (int i = 0; i < attrKey.size(); i++) { + sql.append("'"); + if (i == attrKey.size() - 1) { + sql.append(attrKey.get(i)).append("' ) and "); + } else { + sql.append(attrKey.get(i)).append("',"); + } + } + } + sql.append(TS).append(" >= ").append(stTs).append(" and ").append(TS).append(" < ").append(enTs). + append(" GROUP BY thing_code, attr_key, time ").append(" order by time DESC;"); + return sql.toString(); + } + + /** + * 获取列表中 年/月/日的开始时间 + * + * @param list 传入数据集合 + * @param timeType 开始时间的类型 如: dd/mm/yy + * @return Map + */ + public static Map>> getStartTime(List list, String timeType) { + Map>> ddStart = new HashMap<>(); + list.forEach(dataProto -> { + String key = dataProto.getTskvProto().getKey(); + String thingCode = dataProto.getTskvProto().getThingCode(); + DateTime stDD = DaXiaUtils.getHhDdMmYyStartTs(dataProto.getTskvProto().getTs(), timeType, null, null, null, null); + Map> things = ddStart.get(String.valueOf(stDD.getMillis())); + // 判断天的开始时间戳是否存在 + if (things != null) { + // 判断 thingCode 是否存在 + if (things.get(thingCode) != null) { + things.get(thingCode).add(key); + } else { + ddStart.get(String.valueOf(stDD.getMillis())).put(thingCode, Set.of(key)); + } + } else { + ddStart.put(stDD.getMillis(), Map.of(thingCode, Set.of(key))); + } + }); + return ddStart; + } + + + public static void spliceCodesAndAttrsSql(Collection codeList, Collection attrList, StringBuilder sql,Long startTime,Long endTime) { + List codes = codeList.stream().toList(); + List attrs = attrList.stream().toList(); + if (CollectionUtils.size(codeList) == 1) { + sql.append(" WHERE (thing_code = '").append(codes.get(0)).append("' "); + } else { + sql.append(" WHERE (thing_code IN ('"); + for (int i = 0; i < codeList.size(); i++) { + if (i == codeList.size() - 1) { + sql.append(codes.get(i)).append("') "); + } else { + sql.append(codes.get(i)).append("','"); + } + } + } + if (CollectionUtils.size(attrList) == 1) { + sql.append(" AND attr_key = '").append(attrs.get(0)).append("' "); + } else { + sql.append(" AND attr_key IN ('"); + for (int i = 0; i < attrList.size(); i++) { + if (i == attrList.size() - 1) { + sql.append(attrs.get(i)).append("') "); + } else { + sql.append(attrs.get(i)).append("','"); + } + } + } + sql.append(" ) "); + timeSql(startTime,endTime,sql); + } + + public static void spliceMapSql(Map> multiMap, StringBuilder sql, Long startTime, Long endTime) { + int j = 0; + for (Map.Entry> entry : multiMap.entrySet()) { + String code = entry.getKey(); + Collection attrList = entry.getValue(); + List attrs = attrList.stream().toList(); + j++; + if (j != 1) { + sql.append(" OR ( thing_code ='").append(code).append("' "); + } else { + sql.append(" ( thing_code ='").append(code).append("' "); + } + if (CollectionUtils.size(attrs) == 1) { + sql.append(" and attr_key = '").append(attrs.get(0)).append("') "); + } else { + sql.append(" and attr_key in ('"); + for (int i = 0; i < attrList.size(); i++) { + if ((i + 1) == attrList.size()) { + sql.append(attrs.get(i)).append("'))"); + } else { + sql.append(attrs.get(i)).append("','"); + } + } + } + } + timeSql(startTime,endTime,sql); + } + + + public static StringBuilder spliceAggTimeByMapSql(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT thing_code, attr_key,time as ts ,val FROM ("); + sql.append("SELECT thing_code, attr_key,MIN(ts) as time,"); + sql.append(DataBaseType.statisticalAggFun(dataBaseType,agg)).append(" as val FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + sql.append(" WHERE ( "); + spliceMapSql(multiMap, sql,null,null); + sql.append(" ) "); + timeSql(startTime, endTime, sql); + sql.append(" GROUP BY thing_code,attr_key "); + sql.append(" ) as l "); + return sql; + } + + + public static void timeSql(Long startTime, Long endTime, StringBuilder sql) { + if(ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)){ + sql.append(" AND ts BETWEEN ") + .append(startTime).append(" AND ").append(endTime); + } + } + + public static String countSql(String sql,Boolean isAlias) { + return isAlias ? "SELECT COUNT(1) FROM ( "+sql + ") AS COUNT " : "SELECT COUNT(1) FROM ( "+sql + ") "; + } + + public static PageData getTsKvDTOPageData(String sql,long count,Boolean isAsc, Integer page, Integer limit) { + List result = Lists.newArrayList(); + if (ObjectUtil.equals(count, 0L)) { + return new PageData<>(result, count); + } + if (isAsc) { + result = RowUtil.toEntityList(Db.selectListBySql(sql + " ORDER BY ts ASC LIMIT " + limit + " OFFSET " + (page - 1) * limit), TsKvDTO.class); + } else { + result = RowUtil.toEntityList(Db.selectListBySql(sql + " ORDER BY ts DESC LIMIT " + limit + " OFFSET " + (page - 1) * limit), TsKvDTO.class); + } + return new PageData<>(result, count); + } + + //-----------------------------------不要改动,重要方法 start ---------------------------------------------- + public static String getMAXWrapper(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, DataBaseType dataBaseType) { + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE){ + return spliceAggregationCKSql(codeList, attrList, startTime, endTime,agg,dataBaseType).append(" ORDER BY val DESC,ts DESC LIMIT 1 ").toString(); + } + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts, val FROM "); + sql.append(TS_KV); + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + sql.append(" ORDER BY val DESC,ts DESC LIMIT 1 "); + return sql.toString(); + } + + public static String getMINWrapper(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE){ + return spliceAggregationCKSql(codeList, attrList, startTime, endTime,agg,dataBaseType).append(" ORDER BY val ASC,ts DESC LIMIT 1 ").toString(); + } + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts,val FROM thing_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + sql.append(" ORDER BY val ASC,ts DESC LIMIT 1 "); + return sql.toString(); + } + + public static String getSUMWrapper(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE){ + return spliceAggregationCKSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + return spliceAggregationSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + + public static String getAVGWrapper(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE){ + return spliceAggregationCKSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + return spliceAggregationSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + + public static String getCOUNTWrapper(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE){ + return spliceAggregationCKSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + return spliceAggregationSql(codeList, attrList, startTime, endTime, agg,dataBaseType).toString(); + } + + private static StringBuilder spliceAggregationCKSql(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + if (Objects.requireNonNull(agg) != AggType.MAX && Objects.requireNonNull(agg) != AggType.MIN){ + sql.append(" SELECT thing_code, attr_key,time as ts,val FROM ( "); + sql.append(" SELECT thing_code, attr_key, "); + sql.append(AggType.MAX).append("(ts)").append(" as time , "); + }else{ + sql.append(" SELECT thing_code, attr_key, ts, "); + } + sql.append(DataBaseType.statisticalAggFun(dataBaseType,agg)); + sql.append(" as val ").append(" FROM ").append(VIEW_TS_KV); + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + if (Objects.requireNonNull(agg) != AggType.MAX && Objects.requireNonNull(agg) != AggType.MIN){ + sql.append(" GROUP BY thing_code,attr_key )"); + }else{ + sql.append(" GROUP BY thing_code, attr_key,ts "); + } + return sql; + } + + private static StringBuilder spliceAggregationSql(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, "); + sql.append(AggType.MAX).append("(ts)").append(" as ts , "); + sql.append(DataBaseType.statisticalAggFun(dataBaseType,agg)); + sql.append(" as val "); + sql.append(" FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + sql.append(" GROUP BY thing_code,attr_key "); + return sql; + } + + public static String getMAXWrappers(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts, val FROM ( ") + .append(" SELECT thing_code, attr_key, ts, val, ROW_NUMBER() OVER (PARTITION BY thing_code, attr_key ORDER BY val DESC ) AS row_num FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + sql.append(" ) as a where a.row_num =1 "); + return sql.toString(); + } + + public static String getMINWrappers(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts, val FROM ( ") + .append(" SELECT thing_code, attr_key, ts, val, ROW_NUMBER() OVER (PARTITION BY thing_code, attr_key ORDER BY val ASC ) AS row_num FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + NativeSQLTool.spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + sql.append(" ) as a where a.row_num =1 "); + return sql.toString(); + } + + public static String getSUMWrappers(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return getSUMWrapper(codeList, attrList, startTime, endTime, agg,dataBaseType); + } + + public static String getAVGWrappers(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return getAVGWrapper(codeList, attrList, startTime, endTime, agg,dataBaseType); + } + + public static String getCOUNTWrappers(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return getCOUNTWrapper(codeList, attrList, startTime, endTime, agg,dataBaseType); + } + + public static String getMAXMapWrapper(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts, val FROM ( ") + .append(" SELECT thing_code, attr_key, ts, val, ROW_NUMBER() OVER (PARTITION BY thing_code, attr_key ORDER BY val DESC ) AS row_num FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + sql.append(" WHERE ( "); + spliceMapSql(multiMap, sql, null, null); + sql.append(" ) "); + NativeSQLTool.timeSql(startTime, endTime, sql); + sql.append(" ) as a where a.row_num =1 "); + return sql.toString(); + } + + public static String getMINMapWrapper(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + StringBuilder sql = new StringBuilder(); + sql.append(" SELECT thing_code, attr_key, ts, val FROM ( ") + .append(" SELECT thing_code, attr_key, ts, val, ROW_NUMBER() OVER (PARTITION BY thing_code, attr_key ORDER BY val ASC ) AS row_num FROM "); + if (Objects.requireNonNull(dataBaseType) == DataBaseType.CLICKHOUSE) { + sql.append(VIEW_TS_KV); + }else { + sql.append(TS_KV); + } + sql.append(" WHERE ( "); + spliceMapSql(multiMap, sql, null, null); + sql.append(" ) "); + NativeSQLTool.timeSql(startTime, endTime, sql); + sql.append(" ) as a where a.row_num =1 "); + return sql.toString(); + } + + public static String getSUMMapWrapper(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return NativeSQLTool.spliceAggTimeByMapSql(multiMap, startTime, endTime, agg,dataBaseType).toString(); + } + + + public static String getAVGMapWrapper(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return NativeSQLTool.spliceAggTimeByMapSql(multiMap, startTime, endTime, agg,dataBaseType).toString(); + } + + public static String getCOUNTMapWrapper(Map> multiMap, Long startTime, Long endTime, AggType agg,DataBaseType dataBaseType) { + return NativeSQLTool.spliceAggTimeByMapSql(multiMap, startTime, endTime, agg,dataBaseType).toString(); + } + + //-----------------------------------不要改动,重要方法 end ---------------------------------------------- + + + public static String deleteTskvSql(TsKvDTO kvDTO) { + StringBuilder sql = new StringBuilder(); + sql.append("DELETE FROM thing_ts_kv where thing_code= '"); + sql.append(kvDTO.getThingCode()); + sql.append("' and attr_key = '"); + sql.append(kvDTO.getAttrKey()); + sql.append("' and ts = '"); + sql.append(kvDTO.getTs()); + sql.append("'"); + return sql.toString(); + } + + + public static String deleteLastTskvSql(TsKvDTO kvDTO) { + StringBuilder sql = new StringBuilder(); + sql.append("DELETE FROM ts_kv_latest where thing_code= '"); + sql.append(kvDTO.getThingCode()); + sql.append("' and attr_key = '"); + sql.append(kvDTO.getAttrKey()); + sql.append("' and ts = '"); + sql.append(kvDTO.getTs()); + sql.append("'"); + return sql.toString(); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/QueryWrapperUtil.java b/common/tskv/src/main/java/com/thing/common/tskv/QueryWrapperUtil.java new file mode 100644 index 0000000..88abb3f --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/QueryWrapperUtil.java @@ -0,0 +1,88 @@ +package com.thing.common.tskv; + +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.tskv.entity.TsKvLatestMy; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.Collection; +import java.util.Comparator; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +public class QueryWrapperUtil { + + public static QueryWrapper getQueryWrapper( + String code, + Collection codeList, + String attrKey, + Collection attrList, + Long startTime, + Long endTime, + Boolean isAsc, + Function getCodeFunction, + Function getAttrKeyFunction, + Function getTsFunction) { + + QueryWrapper queryWrapper = QueryWrapper.create(); + + // 判断字段非空后进行相应的查询条件设置 + if(StringUtils.isNotBlank(code)) { + queryWrapper.eq(getCodeFunction.toString(), code); + } + if(StringUtils.isNotBlank(attrKey)) { + queryWrapper.eq(getAttrKeyFunction.toString(), attrKey); + } + if(CollectionUtils.isNotEmpty(attrList)) { + queryWrapper.in(getAttrKeyFunction.toString(), attrList); + } + if(CollectionUtils.isNotEmpty(codeList)) { + queryWrapper.in(getCodeFunction.toString(), codeList); + } + if(startTime != null && endTime != null) { + queryWrapper.between(getTsFunction.toString(), startTime, endTime); + } + + // 根据时间戳排序 + queryWrapper.orderBy((QueryColumn) getTsFunction, isAsc); + return queryWrapper; + } + + + public static QueryWrapper getLastestQueryWrapper( String code, + Collection codeList, + String attrKey, + Collection attrList, + Long startTime, + Long endTime, + Boolean isAsc, + String codeFunction, + String attrKeyFunction, + String tsFunction) { + QueryWrapper queryWrapper = QueryWrapper.create(); + + // 判断字段非空后进行相应的查询条件设置 + if(StringUtils.isNotBlank(code)) { + queryWrapper.eq(codeFunction, code); + } + if(StringUtils.isNotBlank(attrKey)) { + queryWrapper.eq(attrKeyFunction, attrKey); + } + if(CollectionUtils.isNotEmpty(attrList)) { + queryWrapper.in(attrKeyFunction, attrList); + } + if(CollectionUtils.isNotEmpty(codeList)) { + queryWrapper.in(codeFunction, codeList); + } + if(startTime != null && endTime != null) { + queryWrapper.between(tsFunction, startTime, endTime); + } + // 根据时间戳排序 + queryWrapper.orderBy(tsFunction, isAsc); + return queryWrapper; + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvCk.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvCk.java new file mode 100644 index 0000000..8f95188 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvCk.java @@ -0,0 +1,34 @@ +package com.thing.common.tskv.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.sql.Date; +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + + +@Table(value = "thing_ts_kv", dataSource = "ck") +public class TsKvCk extends TsKvEntity { + private Date partition; + + public TsKvCk(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + this.partition = new Date(ts); + } + + public TsKvCk(String thingCode, String attrKey, Long ts, String val, Date partition) { + super(thingCode, attrKey, ts, val); + this.partition = partition; + } + + public static Collection toTsKvCk(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvCk(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestCk.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestCk.java new file mode 100644 index 0000000..a577c30 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestCk.java @@ -0,0 +1,28 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + +@Table(value = "ts_kv_latest", dataSource = "ck") +public class TsKvLatestCk extends TsKvEntity { + public TsKvLatestCk(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + public static Collection toTsKvLatestCk(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvLatestCk(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } + + public static Collection removeDuplicates(List kvDTOList) { + List list = TsKvDTO.removeDuplicates(kvDTOList); + return list.stream().map(tsKvDTO -> new TsKvLatestCk(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestMy.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestMy.java new file mode 100644 index 0000000..5dc1b18 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestMy.java @@ -0,0 +1,22 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + +@Table(value = "ts_kv_latest", dataSource = "my") +public class TsKvLatestMy extends TsKvEntity { + public TsKvLatestMy(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + public static Collection toTsKvLatestMy(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvLatestMy(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestPg.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestPg.java new file mode 100644 index 0000000..8e7fe4b --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvLatestPg.java @@ -0,0 +1,24 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + +@Table(value = "ts_kv_latest", dataSource = "pg") +public class TsKvLatestPg extends TsKvEntity { + public TsKvLatestPg(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + + public static Collection toTsKvLatestPg(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvLatestPg(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvMy.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvMy.java new file mode 100644 index 0000000..e0819fd --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvMy.java @@ -0,0 +1,24 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + +@Table(value = "thing_ts_kv", dataSource = "my") +public class TsKvMy extends TsKvEntity { + + public TsKvMy(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + public static Collection toTsKvMy(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvMy(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvPg.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvPg.java new file mode 100644 index 0000000..885c410 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/TsKvPg.java @@ -0,0 +1,24 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; + +import java.util.List; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.stream.Collectors; + +@Table(value = "thing_ts_kv", dataSource = "pg") +public class TsKvPg extends TsKvEntity { + + public TsKvPg(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } + + + public static Collection toTsKvPg(List kvDTOList) { + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + return list.stream().map(tsKvDTO -> new TsKvPg(tsKvDTO.getThingCode(), tsKvDTO.getAttrKey(), tsKvDTO.getTs(), tsKvDTO.getVal())).collect(Collectors.toList()); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKv.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKv.java new file mode 100644 index 0000000..6c194bd --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKv.java @@ -0,0 +1,11 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvEntity; + +@Table(value = "view_ts_kv", dataSource = "ck") +public class ViewTsKv extends TsKvEntity { + public ViewTsKv(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKvLatest.java b/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKvLatest.java new file mode 100644 index 0000000..686b375 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/entity/ViewTsKvLatest.java @@ -0,0 +1,11 @@ +package com.thing.common.tskv.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.data.tskv.TsKvEntity; + +@Table(value = "view_latest", dataSource = "ck") +public class ViewTsKvLatest extends TsKvEntity { + public ViewTsKvLatest(String thingCode, String attrKey, Long ts, String val) { + super(thingCode, attrKey, ts, val); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/CalcLogSaveEvent.java b/common/tskv/src/main/java/com/thing/common/tskv/event/CalcLogSaveEvent.java new file mode 100644 index 0000000..ce4a06b --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/CalcLogSaveEvent.java @@ -0,0 +1,18 @@ +package com.thing.common.tskv.event; + +import com.thing.common.data.event.AbstractQueueEvent; +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-04 + * @description + */ +public class CalcLogSaveEvent extends AbstractQueueEvent { + + public CalcLogSaveEvent(Object source, List list) { + super(source, list); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/FilterLogSaveEvent.java b/common/tskv/src/main/java/com/thing/common/tskv/event/FilterLogSaveEvent.java new file mode 100644 index 0000000..76727d6 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/FilterLogSaveEvent.java @@ -0,0 +1,27 @@ +package com.thing.common.tskv.event; + +import com.thing.common.data.event.AbstractQueueEvent; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-21 + * @description + */ +public class FilterLogSaveEvent extends AbstractQueueEvent { + + public FilterLogSaveEvent(Object source, List list) { + super(source, list); + } + + public List parse2Tskv() { + return getList().stream() + .map(QueueProto.DataProto::getTskvProto) + .map(e -> new TsKvDTO(e.getThingCode(), e.getKey(), e.getTs(), e.getVal())) + .toList(); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/PeakEvent.java b/common/tskv/src/main/java/com/thing/common/tskv/event/PeakEvent.java new file mode 100644 index 0000000..7eddb90 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/PeakEvent.java @@ -0,0 +1,21 @@ +package com.thing.common.tskv.event; + +import com.thing.common.data.proto.QueueProto; +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + + +@Getter +@Setter +public class PeakEvent extends ApplicationEvent { + + private List protos; + + public PeakEvent(Object source,List protos) { + super(source); + this.protos = protos; + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEvent.java b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEvent.java new file mode 100644 index 0000000..26fb1c2 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEvent.java @@ -0,0 +1,18 @@ +package com.thing.common.tskv.event; + + +import com.thing.common.data.event.AbstractQueueEvent; +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.util.List; +import java.time.Clock; + +public class TsKvEvent extends AbstractQueueEvent { + + public TsKvEvent(Object source, List protoList) { + super(source, protoList); + } + public TsKvEvent(Object source, Clock clock, List protoList) { + super(source, clock, protoList); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventService.java b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventService.java new file mode 100644 index 0000000..0048bdc --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventService.java @@ -0,0 +1,7 @@ +package com.thing.common.tskv.event; + +import org.springframework.context.ApplicationListener; + +public interface TsKvEventService extends ApplicationListener { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventServiceImpl.java new file mode 100644 index 0000000..37aa8ab --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/event/TsKvEventServiceImpl.java @@ -0,0 +1,92 @@ +package com.thing.common.tskv.event; + +import com.alibaba.fastjson2.JSON; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.tskv.service.DBExecutor; +import com.thing.common.tskv.service.TsKvService; +import com.thing.queue.util.Topics; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import javax.annotation.PreDestroy; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class TsKvEventServiceImpl implements TsKvEventService { + @Value("${calculate.am_total.keys}") + private String keys; + @Value("${filter.rule.enable}") + private Boolean enableFilterRule; + private final DBExecutor dbExecutor; + private final TsKvService tsKvService; + private final ApplicationEventPublisher applicationEventPublisher; + + @Override + public void onApplicationEvent(TsKvEvent event) { + List protoList = event.getList(); + if (!protoList.isEmpty()) { + // 只存储主题 为 tskv/save + if (event.getSource().toString().contains(Topics.V1_TSKV_HISTORY.getValue())) { + tsKvService.saveProtoTsKvAndLatest(protoList); + } else if (event.getSource().toString().contains(Topics.V1_TSKV_LATEST.getValue())) { + tsKvService.saveProtoLatest(protoList); + } + + // 物计算日志存储事件 + applicationEventPublisher.publishEvent(new CalcLogSaveEvent(Topics.V1_TSKV_CALC_LOG_SAVE.getValue(), protoList)); + + if (enableFilterRule) { + // 过滤日志存储事件 + applicationEventPublisher.publishEvent(new FilterLogSaveEvent(Topics.V1_TSKV_FILTER_LOG_SAVE.getValue(), protoList)); + } + + Set setKeys = Arrays.stream(this.keys.split(",")).collect(Collectors.toSet()); + List dataProtos = new ArrayList<>(); + List amList = new ArrayList<>(); + Set amKeys = new HashSet<>(); + setKeys.forEach(key -> { + amKeys.add(key + "am"); + }); + // am统计过滤 + protoList.forEach(dataProto -> { + String val = dataProto.getTskvProto().getVal(); + //递增属性过滤 并且数值大于0的将统计计算 + if (setKeys.contains(dataProto.getTskvProto().getKey()) && NumberUtils.isCreatable(val) && new BigDecimal(val).compareTo(BigDecimal.ZERO) > 0) { + dataProtos.add(dataProto); + // am 属性过滤 + } else if (amKeys.contains(dataProto.getTskvProto().getKey())) { + amList.add(dataProto); + } + }); + // 根据过滤的递增属性,计算 am + if (!dataProtos.isEmpty()) { + tsKvService.amSumProtos(dataProtos); + } + // 根据 过滤的 am 属性 统计 hh/dd/mm/yy + if (!amList.isEmpty()) { + applicationEventPublisher.publishEvent(new PeakEvent(this,amList)); + tsKvService.hhDdMmYySum(amList); + } + } else { + log.info("-------其它数据-----{}-------->{}", event.getSource().toString(), protoList); + } + } + + @Override + public boolean supportsAsyncExecution() { + return TsKvEventService.super.supportsAsyncExecution(); + } + + @PreDestroy + void stopActorSystem() { + dbExecutor.destroy(); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvCkMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvCkMapper.java new file mode 100644 index 0000000..ec7be8f --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvCkMapper.java @@ -0,0 +1,14 @@ +package com.thing.common.tskv.mapper; + + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvCk; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvCkMapper extends BaseMapper { + + + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestCkMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestCkMapper.java new file mode 100644 index 0000000..feaa6d7 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestCkMapper.java @@ -0,0 +1,11 @@ +package com.thing.common.tskv.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvLatestCk; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvLatestCkMapper extends BaseMapper { + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestMyMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestMyMapper.java new file mode 100644 index 0000000..dbe5e10 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestMyMapper.java @@ -0,0 +1,9 @@ +package com.thing.common.tskv.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvLatestMy; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvLatestMyMapper extends BaseMapper { +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestPgMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestPgMapper.java new file mode 100644 index 0000000..b02ad65 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvLatestPgMapper.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvLatestPg; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvLatestPgMapper extends BaseMapper { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvMyMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvMyMapper.java new file mode 100644 index 0000000..2b037e1 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvMyMapper.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.mapper; + + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvMy; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvMyMapper extends BaseMapper { +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvPgMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvPgMapper.java new file mode 100644 index 0000000..fccc247 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/TsKvPgMapper.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.mapper; + + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.TsKvPg; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface TsKvPgMapper extends BaseMapper { +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvCkMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvCkMapper.java new file mode 100644 index 0000000..af8982a --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvCkMapper.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.mapper; + + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.ViewTsKv; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ViewTsKvCkMapper extends BaseMapper { +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvLatestCkMapper.java b/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvLatestCkMapper.java new file mode 100644 index 0000000..6e0a84f --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/mapper/ViewTsKvLatestCkMapper.java @@ -0,0 +1,11 @@ +package com.thing.common.tskv.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.common.tskv.entity.ViewTsKvLatest; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface ViewTsKvLatestCkMapper extends BaseMapper { + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/AggType.java b/common/tskv/src/main/java/com/thing/common/tskv/service/AggType.java new file mode 100644 index 0000000..50c6ef7 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/AggType.java @@ -0,0 +1,5 @@ +package com.thing.common.tskv.service; + +public enum AggType { + AVG, SUM, MAX, MIN, COUNT, NO +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/AmExecutor.java b/common/tskv/src/main/java/com/thing/common/tskv/service/AmExecutor.java new file mode 100644 index 0000000..3da9057 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/AmExecutor.java @@ -0,0 +1,19 @@ +package com.thing.common.tskv.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Value; +import com.thing.common.util.thread.AbstractListeningExecutor; + +@Slf4j +@Component +public class AmExecutor extends AbstractListeningExecutor { + + @Value("${calculate.maximumPoolSize:16}") + private int poolSize; + + @Override + protected int getThreadPollSize() { + return this.poolSize; + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/DBExecutor.java b/common/tskv/src/main/java/com/thing/common/tskv/service/DBExecutor.java new file mode 100644 index 0000000..eb62254 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/DBExecutor.java @@ -0,0 +1,26 @@ +package com.thing.common.tskv.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Value; +import com.thing.common.util.thread.AbstractListeningExecutor; + + +@Slf4j +@Component +public class DBExecutor extends AbstractListeningExecutor { + + + @Value("${database.save_maximumPoolSize:0}") + private int poolSize; + + @Override + protected int getThreadPollSize() { + if (poolSize == 0) { + int cores = Runtime.getRuntime().availableProcessors(); + poolSize = Math.max(1, cores / 2); + } + return poolSize; + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/DatabaseType.java b/common/tskv/src/main/java/com/thing/common/tskv/service/DatabaseType.java new file mode 100644 index 0000000..ce7aab0 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/DatabaseType.java @@ -0,0 +1,5 @@ +package com.thing.common.tskv.service; + +public enum DatabaseType { + CK, PG, MYSQL, Tidb, Timescale +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/HhExecutor.java b/common/tskv/src/main/java/com/thing/common/tskv/service/HhExecutor.java new file mode 100644 index 0000000..f0d1d7e --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/HhExecutor.java @@ -0,0 +1,19 @@ +package com.thing.common.tskv.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Value; +import com.thing.common.util.thread.AbstractListeningExecutor; + +@Slf4j +@Component +public class HhExecutor extends AbstractListeningExecutor { + + @Value("${calculate.maximumPoolSize:16}") + private int poolSize; + + @Override + protected int getThreadPollSize() { + return this.poolSize + 4; + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/LatestNativeSQL.java b/common/tskv/src/main/java/com/thing/common/tskv/service/LatestNativeSQL.java new file mode 100644 index 0000000..d10b64d --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/LatestNativeSQL.java @@ -0,0 +1,87 @@ +package com.thing.common.tskv.service; + +import lombok.extern.slf4j.Slf4j; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.proto.QueueProto; +import org.joda.time.DateTime; + +import java.util.List; +import java.util.stream.IntStream; + +@Slf4j +public class LatestNativeSQL { + public static final String LATEST = "ts_kv_latest"; + public static final String THING_CODE = "thing_code"; + public static final String ATTR_KEY = "attr_key"; + public static final String VAL = "val"; + public static final String TS = "ts"; + public static final String YEAR = "year"; + public static final String MONTH = "month"; + public static final String DAY = "day"; + public static final String HOUR = "hour"; + public static final String MINUTE = "minute"; + + public static String sqlSaveDTOLatest(List kvDTOList, DatabaseType dataType) { + StringBuilder sql = new StringBuilder(" INSERT INTO ") + .append(LATEST).append(" (") + .append(THING_CODE).append(",") + .append(ATTR_KEY).append(",") + .append(TS).append(",") + .append(YEAR).append(",") + .append(MONTH).append(",") + .append(DAY).append(",") + .append(HOUR).append(",") + .append(MINUTE).append(",") + .append(VAL).append(") VALUES "); + IntStream.range(0, kvDTOList.size()).forEach(i -> { + long ts = kvDTOList.get(i).getTs(); + DateTime dateTime = new DateTime(ts); + sql.append("('"); + sql.append(kvDTOList.get(i).getThingCode()).append("',"); + sql.append("'").append(kvDTOList.get(i).getAttrKey()).append("',"); + sql.append(ts).append(","); + sql.append(dateTime.getYear()).append(","); + sql.append(dateTime.getMonthOfYear()).append(","); + sql.append(dateTime.getDayOfMonth()).append(","); + sql.append(dateTime.getHourOfDay()).append(","); + sql.append(dateTime.getMinuteOfHour()).append(","); + sql.append("'").append(kvDTOList.get(i).getVal()).append("'"); + if (i == kvDTOList.size() - 1) { + sql.append(")"); + } else { + sql.append("),"); + } + }); + if (DatabaseType.MYSQL.equals(dataType) || DatabaseType.Tidb.equals(dataType)) { + sql.append(" ON DUPLICATE KEY UPDATE val = VALUES(val),ts = VALUES(ts)"); + } else if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" ON CONFLICT (thing_code, attr_key) DO UPDATE SET val = EXCLUDED.val,ts= EXCLUDED.ts"); + } + sql.append(";"); + return sql.toString(); + } + + public static String sqlSaveProtoLatest(List protoList, DatabaseType dataType) { + StringBuilder sql = new StringBuilder(" INSERT INTO ").append(LATEST).append(" (").append(THING_CODE).append(",").append(ATTR_KEY).append(",").append(TS).append(",").append(VAL).append(") VALUES "); + IntStream.range(0, protoList.size()).forEach(i -> { + sql.append("('"); + sql.append(protoList.get(i).getTskvProto().getThingCode()).append("',"); + sql.append("'").append(protoList.get(i).getTskvProto().getKey()).append("',"); + sql.append(protoList.get(i).getTskvProto().getTs()).append(","); + sql.append("'").append(protoList.get(i).getTskvProto().getVal()).append("'"); + if (i == protoList.size() - 1) { + sql.append(")"); + } else { + sql.append("),"); + } + }); + if (DatabaseType.MYSQL.equals(dataType) || DatabaseType.Tidb.equals(dataType)) { + sql.append(" ON DUPLICATE KEY UPDATE val = VALUES(val),ts = VALUES(ts)"); + } else if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" ON CONFLICT (thing_code, attr_key) DO UPDATE SET val = EXCLUDED.val,ts= EXCLUDED.ts"); + } + sql.append(";"); + return sql.toString(); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvNativeSQL.java b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvNativeSQL.java new file mode 100644 index 0000000..7855988 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvNativeSQL.java @@ -0,0 +1,819 @@ +package com.thing.common.tskv.service; + +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.Row; +import com.thing.common.data.event.EventPublisher; +import com.thing.common.data.event.QueueConsumerEvent; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.util.time.DaXiaUtils; +import com.thing.queue.util.Topics; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +@Slf4j +public class TsKvNativeSQL { + + public static final String VIEW_TS_KV = "view_ts_kv"; + public static final String LATEST = "ts_kv_latest"; + public static final String TS_KV = "thing_ts_kv"; + public static final String THING_CODE = "thing_code"; + public static final String ATTR_KEY = "attr_key"; + public static final String VAL = "val"; + public static final String TS = "ts"; + public static final String YEAR = "year"; + public static final String MONTH = "month"; + public static final String DAY = "day"; + public static final String HOUR = "hour"; + public static final String MINUTE = "minute"; + public static final String PARTITION = "partition"; + + + public static Integer saveProtoTsKv(List protoList, DatabaseType dataType) { + if (protoList.isEmpty()) { + return 0; + } + List list = new LinkedHashSet<>(protoList).stream().toList(); + String sql = sqlSaveProtoTsKv(list, dataType); + try { + if(StringUtils.isNotEmpty(sql)){ + Db.insertBySql(sql); + } + } catch (Exception exception) { + log.error("--TsKv----{}-数据存储失败------>{}=======SQL====>{}", dataType, exception.getMessage(), sql); + throw new RuntimeException(exception); + } + return list.size(); + } + + public static Integer saveDTOTsKv(List kvDTOList, DatabaseType dataType,String tableSuffix) { + if (kvDTOList.isEmpty()) { + return 0; + } + List list = new LinkedHashSet<>(kvDTOList).stream().toList(); + String sql = sqlSaveDTOTsKv(list, dataType,tableSuffix); + try { + Db.insertBySql(sql); + } catch (Exception exception) { + log.error("--TsKv----{}-数据存储失败------>{}=======SQL====>{}", dataType, exception.getMessage(), sql); + throw new RuntimeException(exception); + } + return list.size(); + } + + public static String sqlSaveDTOTsKv(List kvDTOList, DatabaseType dataType,String tableSuffix) { + String tableName = TS_KV; + if(StringUtils.isNotEmpty(tableSuffix)){ + tableName = TS_KV+tableSuffix; + } + StringBuilder sql = new StringBuilder(" INSERT INTO ").append(tableName) + .append(" (").append(THING_CODE) + .append(",").append(ATTR_KEY) + .append(",").append(TS) + .append(",").append(VAL) + .append(",").append(YEAR) + .append(",").append(MONTH) + .append(",").append(DAY) + .append(",").append(HOUR) + .append(",").append(MINUTE) + ; + if (DatabaseType.CK.equals(dataType)) { + sql.append(",").append(PARTITION); + } + sql.append(") VALUES "); + IntStream.range(0, kvDTOList.size()).forEach(i -> { + sql.append("('"); + sql.append(kvDTOList.get(i).getThingCode()).append("',"); + sql.append("'").append(kvDTOList.get(i).getAttrKey()).append("',"); + sql.append(kvDTOList.get(i).getTs()).append(","); + sql.append("'").append(kvDTOList.get(i).getVal()).append("',"); + DateTime dateTime = new DateTime(kvDTOList.get(i).getTs()); + sql.append(dateTime.getYear()).append(","); + sql.append(dateTime.getMonthOfYear()).append(","); + sql.append(dateTime.getDayOfMonth()).append(","); + sql.append(dateTime.getHourOfDay()).append(","); + sql.append(dateTime.getMinuteOfHour()).append(""); + if (DatabaseType.CK.equals(dataType)) { + String partition = new DateTime(kvDTOList.get(i).getTs()).toString("yyyy-MM-dd"); + sql.append(",'").append(partition).append("'"); + } + if (i == kvDTOList.size() - 1) { + sql.append(")"); + } else { + sql.append("),"); + } + }); + if (DatabaseType.MYSQL.equals(dataType) || DatabaseType.Tidb.equals(dataType)) { + sql.append(" ON DUPLICATE KEY UPDATE val = VALUES(val)"); + } else if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" ON CONFLICT (thing_code, attr_key, ts) DO UPDATE SET val = EXCLUDED.val"); + } + sql.append(";"); + return sql.toString(); + } + + public static String sqlSaveProtoTsKv(List protoList, DatabaseType dataType) { + StringBuilder sql = new StringBuilder(" INSERT INTO ") + .append(TS_KV).append(" (") + .append(THING_CODE) + .append(",").append(ATTR_KEY) + .append(",").append(TS) + .append(",").append(YEAR) + .append(",").append(MONTH) + .append(",").append(DAY) + .append(",").append(HOUR) + .append(",").append(MINUTE) + .append(",").append(VAL) + ; + if (DatabaseType.CK.equals(dataType)) { + sql.append(",").append(PARTITION); + } + sql.append(") VALUES "); + IntStream.range(0, protoList.size()).forEach(i -> { + + long ts = protoList.get(i).getTskvProto().getTs(); + DateTime dateTime = new DateTime(ts); + sql.append("('"); + sql.append(protoList.get(i).getTskvProto().getThingCode()).append("',"); + sql.append("'").append(protoList.get(i).getTskvProto().getKey()).append("',"); + sql.append(ts).append(","); + sql.append(dateTime.getYear()).append(","); + sql.append(dateTime.getMonthOfYear()).append(","); + sql.append(dateTime.getDayOfMonth()).append(","); + sql.append(dateTime.getHourOfDay()).append(","); + sql.append(dateTime.getMinuteOfHour()).append(","); + sql.append("'").append(protoList.get(i).getTskvProto().getVal()).append("'"); + if (DatabaseType.CK.equals(dataType)) { + String partition = new DateTime(ts).toString("yyyy-MM-dd"); + sql.append(",'").append(partition).append("'"); + } + if (i == protoList.size() - 1) { + sql.append(")"); + } else { + sql.append("),"); + } + }); + if (DatabaseType.MYSQL.equals(dataType) || DatabaseType.Tidb.equals(dataType)) { + sql.append(" ON DUPLICATE KEY UPDATE val = VALUES(val)"); + } else if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" ON CONFLICT (thing_code, attr_key, ts) DO UPDATE SET val = EXCLUDED.val"); + } + sql.append(";"); + return sql.toString(); + } + + public static String sqlAmQueryMin(String thingCode, String attrKey, long stTs, long enTs, long interval,DatabaseType dataType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE).append(", ").append(ATTR_KEY).append(", "); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(AggType.MIN).append("(CAST(").append(VAL).append(" AS numeric ))AS value , "); + } else { + sql.append(AggType.MIN).append("(CAST(").append(VAL).append(" AS double )) AS value , "); + } + sql.append(" FLOOR((").append(TS + " - ").append(stTs).append(") / ").append(interval).append(" ) * ").append(interval).append(" + ").append(stTs).append(" AS time "); + sql.append(" FROM "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" WHERE ").append(THING_CODE).append(" = '").append(thingCode).append("' ").append("AND ").append(ATTR_KEY).append(" = '").append(attrKey).append("' ").append("AND ").append(TS).append(" >= ").append(stTs).append(" AND ").append(TS).append(" <= ").append(enTs).append(" GROUP BY ").append(THING_CODE).append(", ").append(ATTR_KEY).append(", ").append(" time ORDER BY time DESC;"); + return sql.toString(); + } + + public static String AggQuery(String thingCode, List keys, long stTs, long enTs, long interval, DatabaseType dataType, AggType aggType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE).append(", ").append(ATTR_KEY).append(", "); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + if (AggType.AVG.equals(aggType) || AggType.SUM.equals(aggType)) { + sql.append("ROUND(").append(aggType).append("(CAST(").append(VAL).append(" AS numeric )),4) AS value ,"); + } else { + sql.append(aggType).append("(CAST(").append(VAL).append(" AS numeric ))AS value , "); + } + } else { + if (AggType.AVG.equals(aggType) || AggType.SUM.equals(aggType)) { + sql.append("ROUND(").append(aggType).append("(CAST(").append(VAL).append(" AS double )),4) AS value ,"); + } else { + sql.append(aggType).append("(CAST(").append(VAL).append(" AS double )) AS value , "); + } + } + sql.append(" FLOOR((").append(TS + " - ").append(stTs).append(") / ").append(interval).append(" ) * ").append(interval).append(" + ").append(stTs).append(" AS time "); + sql.append(" FROM "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" WHERE ").append(THING_CODE).append(" = '").append(thingCode).append("' ").append("AND ").append(TS).append(" >= ").append(stTs).append(" AND ").append(TS).append(" <= ").append(enTs).append(" AND ").append(ATTR_KEY); + if (keys.size() > 1) { + sql.append(" IN ("); + for (int i = 0; i < keys.size(); i++) { + sql.append("'"); + if (i == keys.size() - 1) { + sql.append(keys.get(i)).append("') "); + } else { + sql.append(keys.get(i)).append("',"); + } + } + } else { + sql.append(" = '").append(keys.get(0)).append("' "); + } + sql.append(" GROUP BY ").append(THING_CODE).append(", ").append(ATTR_KEY).append(",time").append(" ORDER BY thing_code, attr_key, time DESC;"); + return sql.toString(); + } + + + /** + * 根据聚合间隔时长,物编号,属性编号分组求每个间隔时间内的最大值,用于计算每个AM + * + * @param thingCode 物编号 + * @param attrKey 属性编号 + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param interval 聚合间隔时长(单位:毫秒) + * @return + */ + public static String amQueryMax(String thingCode, String attrKey, long stTs, long enTs, long interval, DatabaseType dataType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE).append(", ").append(ATTR_KEY).append(", "); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(AggType.MAX).append("(CAST(").append(VAL).append(" AS numeric ))AS value , "); + } else { + sql.append(AggType.MAX).append("(CAST(").append(VAL).append(" AS double )) AS value , "); + } + sql.append(enTs).append(" - (FLOOR((").append(enTs).append(" - ").append(TS).append(" ) / ").append(interval).append(") * ").append(interval).append(") AS time ").append("FROM "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" WHERE ").append(THING_CODE).append(" = '").append(thingCode).append("' ").append("AND ").append(ATTR_KEY).append(" = '").append(attrKey).append("' ").append("AND ").append(TS).append(" > ").append(stTs).append(" AND ").append(TS).append(" <= ").append(enTs).append(" GROUP BY ").append(THING_CODE).append(", ").append(ATTR_KEY).append(",").append("time").append(" ORDER BY thing_code, attr_key, time DESC;"); + return sql.toString(); + } + + + /** + * 拼接 查询物属性时间范围内的数据集合 SQL + * + * @param things 物和属性集合 + * @param stTs 大于等于当前时间戳 + * @param enTs 小于当前时间戳 + * @param dataType 数据库类型 + * @return 返回sql 语句 + */ + public static String thingsAndKeysSql(Map> things, long stTs, long enTs, DatabaseType dataType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE + ", ").append(ATTR_KEY + ", ").append(TS + ","); + sql.append(" CAST(").append(VAL); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" AS numeric ) AS value "); + } else { + sql.append(" AS double ) AS value "); + } + sql.append(" FROM "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" WHERE ").append(TS).append(" >= ").append(stTs).append(" AND ").append(TS).append(" < ").append(enTs).append(" AND "); + List thingCodes = things.keySet().stream().toList(); + if (thingCodes.size() == 1) { + sql.append(THING_CODE + " = '").append(thingCodes.get(0)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = things.get(thingCodes.get(0)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("' "); + } else { + sql.append(" IN ("); + for (int i = 0; i < attrKey.size(); i++) { + sql.append("'"); + if (i == attrKey.size() - 1) { + sql.append(attrKey.get(i)).append("') "); + } else { + sql.append(attrKey.get(i)).append("',"); + } + } + } + } else if (thingCodes.size() > 1) { + sql.append("("); + for (int i = 0; i < thingCodes.size(); i++) { + sql.append("(" + THING_CODE + " = '").append(thingCodes.get(i)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = things.get(thingCodes.get(i)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("'"); + } else { + sql.append(" IN ("); + for (int j = 0; j < attrKey.size(); j++) { + sql.append("'"); + if (j == attrKey.size() - 1) { + sql.append(attrKey.get(j)).append("')"); + } else { + sql.append(attrKey.get(j)).append("',"); + } + } + } + if (i != thingCodes.size() - 1) { + sql.append(") OR "); + } else { + sql.append("))"); + } + } + } + sql.append(" ORDER BY thing_code, attr_key, ts DESC;"); + return sql.toString(); + } + + /** + * 根据物编号,属性编号,开始和结束时间 分组汇总求和,将根据数量判断是否用IN + * + * @param setMap 物对应属性编号map + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param dataType 数据库类型 + * @return + */ + public static String thingsAndKeysSum(Map> setMap, long stTs, long enTs, DatabaseType dataType) { + StringBuilder sql = new StringBuilder("SELECT ").append(THING_CODE + ", ").append(ATTR_KEY + ", "); + sql.append(" ROUND(SUM(CAST(").append(VAL); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" AS numeric )),4) as value "); + } else { + sql.append(" AS double )),4) as value "); + } + sql.append(" from "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" where ").append(TS).append(" >= ").append(stTs).append(" AND ").append(TS).append(" < ").append(enTs).append(" AND "); + List thingCodes = setMap.keySet().stream().toList(); + if (thingCodes.size() == 1) { + sql.append(THING_CODE + " = '").append(thingCodes.get(0)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = setMap.get(thingCodes.get(0)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("' "); + } else { + sql.append(" IN ("); + for (int i = 0; i < attrKey.size(); i++) { + sql.append("'"); + if (i == attrKey.size() - 1) { + sql.append(attrKey.get(i)).append("') "); + } else { + sql.append(attrKey.get(i)).append("',"); + } + } + } + } else if (thingCodes.size() > 1) { + sql.append("("); + for (int i = 0; i < thingCodes.size(); i++) { + sql.append("(" + THING_CODE + " = '").append(thingCodes.get(i)).append("' AND "); + sql.append(ATTR_KEY); + List attrKey = setMap.get(thingCodes.get(i)).stream().toList(); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("'"); + } else { + sql.append(" IN ("); + for (int j = 0; j < attrKey.size(); j++) { + sql.append("'"); + if (j == attrKey.size() - 1) { + sql.append(attrKey.get(j)).append("')"); + } else { + sql.append(attrKey.get(j)).append("',"); + } + } + } + if (i != thingCodes.size() - 1) { + sql.append(") OR "); + } else { + sql.append("))"); + } + } + } + sql.append(" group by ").append(THING_CODE + ", ").append(ATTR_KEY + ";"); + return sql.toString(); + } + + /** + * 拼接 每小时数据汇总的SQL语句,注意 MsySql 的数据类型 + * + * @param thingCodes 物编号列表 + * @param attrKey 属性列表 + * @param stTs 开始时间 + * @param enTs 结束时间 + * @param interval 时间间隔(单位:分),默认60分 + * @param dataType 数据库类型,不同数据库sql函数不同 + * @return + */ + public static String hhQuerySum(List thingCodes, List attrKey, long stTs, long enTs, Integer interval, DatabaseType dataType) { + int inter = interval * 60 * 1000; + StringBuilder sql = new StringBuilder("SELECT ") + .append(THING_CODE).append(", ") + .append(ATTR_KEY).append(", ") + .append(" (FLOOR(( ").append(TS).append(" - ").append(stTs).append(") / ").append(inter).append(") * ").append(inter).append(" + ").append(stTs).append(" ) AS time, ROUND(SUM(CAST( ").append(VAL); + if (DatabaseType.PG.equals(dataType) || DatabaseType.Timescale.equals(dataType)) { + sql.append(" AS numeric )),4) as value "); + } else { + sql.append(" AS double )),4) as value "); + } + sql.append(" from "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" where ").append(THING_CODE); + if (thingCodes.size() == 1) { + sql.append(" = '").append(thingCodes.get(0)).append("' and "); + } else { + sql.append(" in ("); + for (int i = 0; i < thingCodes.size(); i++) { + sql.append("'"); + if (i == thingCodes.size() - 1) { + sql.append(thingCodes.get(i)).append("' ) and "); + } else { + sql.append(thingCodes.get(i)).append("',"); + } + } + } + sql.append(ATTR_KEY); + if (attrKey.size() == 1) { + sql.append(" = '").append(attrKey.get(0)).append("' and "); + } else { + sql.append(" in ("); + for (int i = 0; i < attrKey.size(); i++) { + sql.append("'"); + if (i == attrKey.size() - 1) { + sql.append(attrKey.get(i)).append("' ) and "); + } else { + sql.append(attrKey.get(i)).append("',"); + } + } + } + sql.append(TS).append(" >= ").append(stTs).append(" and ").append(TS).append(" < ").append(enTs).append(" GROUP BY thing_code, attr_key, time ").append(" order by time DESC;"); + return sql.toString(); + } + + /** + * 获取列表中 年/月/日的开始时间 + * + * @param list 传入数据集合 + * @param timeType 开始时间的类型 如: dd/mm/yy + * @return + */ + public static Map>> getStartTime(List list, String timeType) { + Map>> ddStart = new HashMap<>(); + list.forEach(dataProto -> { + String key = dataProto.getTskvProto().getKey(); + String thingCode = dataProto.getTskvProto().getThingCode(); + DateTime stDD = DaXiaUtils.getHhDdMmYyStartTs(dataProto.getTskvProto().getTs(), timeType, null, null, null, null); + if (ddStart.get(stDD.getMillis()) == null) { + Map> things = new HashMap<>(); + Set keys = new HashSet<>(); + keys.add(key); + things.put(thingCode, keys); + ddStart.put(stDD.getMillis(), things); + } else { + if (ddStart.get(stDD.getMillis()).get(thingCode) == null) { + Set keys = new HashSet<>(); + keys.add(key); + ddStart.get(stDD.getMillis()).put(thingCode, keys); + } else { + ddStart.get(stDD.getMillis()).get(thingCode).add(key); + } + } + }); + return ddStart; + } + + public static String limitQuery(String thingCode, String key, long stTs, long enTs, Integer limit, DatabaseType dataType, String desc) { + StringBuilder sql = new StringBuilder(); + sql.append("SELECT ").append(THING_CODE + ",").append(ATTR_KEY + ",").append(TS + ",").append(VAL); + sql.append(" FROM "); + if (DatabaseType.CK.equals(dataType)) { + sql.append(TS_KV + " FINAL "); + } else { + sql.append(TS_KV); + } + sql.append(" WHERE ").append(THING_CODE + " = '").append(thingCode).append("'").append(" AND ").append(ATTR_KEY + " = '").append(key).append("'").append(" AND ").append(TS + " >= ").append(stTs).append(" AND ").append(TS + " < ").append(enTs).append(" ORDER BY ts ").append(desc).append(" LIMIT ").append(limit).append(";"); + return sql.toString(); + } + + /** + * am 区间计算,用区间结束值减去开始值,用于实时计算,如有断点会向后查询 进行计算; + * + * @param protoList 递增数据集合 + * @param offset 断点向后查询 多少小时 + * @param interval 聚合间隔多少分钟 + * @param dataType 数据库类型 + * @param eventPublisher 数据推送事件 + */ + public static void amSumV1(List protoList, int offset, int interval, DatabaseType dataType, EventPublisher eventPublisher) { + List amList = new ArrayList<>(); + protoList.forEach(dataProto -> { + long inter = (long) interval * 60 * 1000; + String thingCode = dataProto.getTskvProto().getThingCode(); + String key = dataProto.getTskvProto().getKey(); + long ts = dataProto.getTskvProto().getTs(); + String value = dataProto.getTskvProto().getVal(); + DateTime stTs = DaXiaUtils.getAmStartTime(ts, interval); + DateTime enTs = DaXiaUtils.getAmEndTime(ts, interval); + //判断当前消息时间为统计结算点 + if ((ts - inter) == stTs.getMillis()) { + //查询前后两个区间的最小值 + String sql = sqlAmQueryMin(thingCode, key, stTs.getMillis(), enTs.getMillis(), inter, dataType); + List rowList = new ArrayList<>(); + try { + rowList = Db.selectListBySql(sql); +// log.info("-------am计算---st-{}--ts-{}-en-{}-->{}", stTs.toString("yyyy-MM-dd HH:mm:ss"), new DateTime(ts).toString("yyyy-MM-dd HH:mm:ss"), enTs.toString("yyyy-MM-dd HH:mm:ss"), sql); + } catch (Exception e) { + log.error("--am计算查询失败!---------sql-->{}---------{}", sql, e.getMessage()); + throw new RuntimeException(e); + } + if (rowList.size() > 1) { + for (int i = 1; i < rowList.size(); i++) { + long l = rowList.get(i - 1).getLong("time") - inter; + String v = DaXiaUtils.minus(rowList.get(i).get("value"), rowList.get(i - 1).get("value")).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(l).setVal(v).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } else if (rowList.size() == 1) { + //向后查询 + String oldQuery = limitQuery(thingCode, key, new DateTime(ts).minusHours(offset).getMillis(), ts, 1, dataType, "DESC"); + Row oldRow = Db.selectOneBySql(oldQuery); + if (oldRow != null) { + String v = DaXiaUtils.minus(oldRow.get("val"), value).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(stTs.getMillis()).setVal(v).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); +// log.error("--am断点计算----向后查询----st-{}----en-{}-->{}", new DateTime(stTs).minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(ts).toString("yyyy-MM-dd HH:mm:ss"), oldQuery); + } else { + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(stTs.getMillis()).setVal("0").build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + log.error("--am断点计算----向后查询失败!----结果置为0-----st-{}----en-{}-->{}", new DateTime(stTs).minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(ts).toString("yyyy-MM-dd HH:mm:ss"), oldQuery); + + } + //如当前系统时间大于,当前消息的结算时间点(说明当前消息数据为历史数据),则向前查询第一个值 + if (new DateTime().getMillis() > enTs.getMillis()) { + String newQuery = limitQuery(thingCode, key, ts + 1, new DateTime(ts).plusHours(offset).getMillis(), 1, dataType, "ASC"); + Row newRow = Db.selectOneBySql(newQuery); + String v = DaXiaUtils.minus(value, newRow.get("val")).toPlainString(); + long newTs = DaXiaUtils.getAmStartTime(newRow.getLong("ts"), interval).getMillis(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(newTs).setVal(v).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } else { + log.error("----tsKv---存储延迟---am计算已接收数据,数据库查询失败!----->{}", dataProto); + } + } else { //当前消息时间非结算点,向后多查一个区间 + String sql = sqlAmQueryMin(thingCode, key, stTs.minusMinutes(interval).getMillis(), enTs.getMillis(), inter, dataType); +// log.info("-------am计算---st-{}--ts-{}-en-{}-->{}", stTs.minusMinutes(interval).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(ts).toString("yyyy-MM-dd HH:mm:ss"), enTs.toString("yyyy-MM-dd HH:mm:ss"), sql); + List rowList = new ArrayList<>(); + try { + rowList = Db.selectListBySql(sql); + } catch (Exception e) { + log.error("----am计算数据查询失败!!!---------sql-->{}---------{}", sql, e.getMessage()); + throw new RuntimeException(e); + } + if (rowList.size() > 0) { + //当前消息值减去第一个最小值大于等于0 + String v = DaXiaUtils.minus(rowList.get(0).get("value"), value).toPlainString(); + if (new BigDecimal(v).compareTo(BigDecimal.ZERO) >= 0) { + if (stTs.getMillis() == rowList.get(0).getLong("time")) { + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(stTs.getMillis()).setVal(v).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } + if (rowList.size() > 1) { + for (int i = 1; i < rowList.size(); i++) { + long l = rowList.get(i - 1).getLong("time") - inter; + String v1 = DaXiaUtils.minus(rowList.get(i).get("value"), rowList.get(i - 1).get("value")).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(l).setVal(v1).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } else if (rowList.size() == 1) { + //查询2个区间的最小值,如果只有一条记录,并且当前消息值减去第一个最小值如果等于0,判断为断点计算,向后查询 + if (new BigDecimal(v).compareTo(BigDecimal.ZERO) == 0) { + String oldQuery = limitQuery(thingCode, key, stTs.minusHours(offset).getMillis(), stTs.getMillis(), 1, dataType, "DESC"); + Row row = Db.selectOneBySql(oldQuery); + if (row != null) { + String v1 = DaXiaUtils.minus(row.get("val"), value).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(stTs.minusMinutes(interval).getMillis()).setVal(v1).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); +// log.error("--am断点计算----向后查询----st-{}----en-{}-->{}", new DateTime(ts).minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(ts).toString("yyyy-MM-dd HH:mm:ss"), oldQuery); + } else { + log.error("--am断点计算----向后查询失败!----结果置为0-----st-{}----en-{}-->{}", stTs.minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), stTs.toString("yyyy-MM-dd HH:mm:ss"), oldQuery); + } + } + //如当前系统时间大于,当前消息的结算时间点(说明当前消息数据为历史数据),则向前查询第一个值 + if (new DateTime().getMillis() >= enTs.plusMinutes(interval).getMillis()) { + String newQuery = limitQuery(thingCode, key, enTs.getMillis(), enTs.plusHours(offset).getMillis(), 1, dataType, "ASC"); + Row newRow = Db.selectOneBySql(newQuery); + if (newRow != null) { + String v2 = DaXiaUtils.minus(rowList.get(0).get("value"), newRow.get("val")).toPlainString(); + long newTs = DaXiaUtils.getAmStartTime(newRow.getLong("ts"), interval).minusMinutes(interval).getMillis(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(key + "am").setTs(newTs).setVal(v2).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } + } + } else { + log.error("----tsKv---存储延迟---am计算已接收数据,数据库查询失败!----->{}", dataProto); + } + } + }); + if (!amList.isEmpty()) { + //存储入库 + saveProtoTsKv(amList, dataType); + String top = Topics.V1_TSKV_LATEST.getValue() + eventPublisher.getRequestId(); + eventPublisher.publish(new QueueConsumerEvent(top, amList)); + } +// log.info("---------am计算结果------>{}", TsKvDTO.protoToJson(amList)); + } + + /** + * am 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算的API接口 + * + * @param thingCode 物编号 + * @param keys 属性列表 + * @param stTs 开始时间戳 + * @param enTs 结算时间戳 + * @param interval 区间计算 间隔(单位:分) + * @param dataType 数据库类型 + * @param eventPublisher 消息推送事件 + */ + public static void amSum(String thingCode, List keys, long stTs, long enTs, int interval, DatabaseType dataType, EventPublisher eventPublisher) { + long val = (long) interval * 60 * 1000; + String sql = AggQuery(thingCode, keys, stTs, enTs, val, dataType, AggType.MIN); + List rowList; + try { + rowList = Db.selectListBySql(sql); + } catch (Exception e) { + log.error("----AM重新计算查询失败!------->{}----{}", sql, e.getMessage()); + throw new RuntimeException(e); + } + List amList = new ArrayList<>(); + if (rowList.size() > 1) { + Map> listMap = rowList.stream().collect(Collectors.groupingBy(row -> row.get("attr_key").toString())); + listMap.keySet().forEach(key -> { + List rows = listMap.get(key); + String attrKey = key + "am"; + for (int i = 1; i < rows.size(); i++) { + long time = rows.get(i - 1).getLong("time") - val; + String value = DaXiaUtils.minus(rows.get(i).get("value"), rows.get(i - 1).get("value")).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(attrKey).setTs(time).setVal(value).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + }); + } + if (!amList.isEmpty()) { + //存储入库 + saveProtoTsKv(amList, dataType); + String top = Topics.V1_TSKV_LATEST.getValue() + eventPublisher.getRequestId(); + eventPublisher.publish(new QueueConsumerEvent(top, amList)); + } + } + + /** + * am 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算 + * + * @param thingCode 物编号 + * @param key 物属性 + * @param stTs 开始时间 + * @param enTs 结束时间 + */ + public static void amSum(String thingCode, String key, long stTs, long enTs, int offset, int interval, DatabaseType dataType, EventPublisher eventPublisher) { + long val = (long) interval * 60 * 1000; + String attrKey = key + "am"; + String sql = amQueryMax(thingCode, key, stTs, enTs, val, dataType); + List rowList = Db.selectListBySql(sql); + List amList = new ArrayList<>(); + if (rowList.size() == 1) { + long time = rowList.get(0).getLong("time"); + String query = limitQuery(thingCode, key, new DateTime(stTs).minusHours(offset).getMillis(), time, 1, dataType, "DESC"); + Row row = Db.selectOneBySql(query); + if (row != null) { + time = time - val; + String value = DaXiaUtils.minus(row.get("val"), rowList.get(0).get("value")).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(attrKey).setTs(time).setVal(value).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + log.info("-------AM断点计算---st-{}----en-{}-->{}", new DateTime(stTs).minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(time).toString("yyyy-MM-dd HH:mm:ss"), query); + } else { + log.error("--------{}----am计算断点查询失败-----{}---->{}----->{}", thingCode, new DateTime(stTs).minusHours(offset).toString("yyyy-MM-dd HH:mm:ss"), new DateTime(stTs).toString("yyyy-MM-dd HH:mm:ss"), query); + } + } else if (rowList.size() > 1) { + for (int i = 1; i < rowList.size(); i++) { + long time = rowList.get(i - 1).getLong("time") - val; + String value = DaXiaUtils.minus(rowList.get(i).get("value"), rowList.get(i - 1).get("value")).toPlainString(); + QueueProto.TsKvProto tsKvProto = QueueProto.TsKvProto.newBuilder().setThingCode(thingCode).setKey(attrKey).setTs(time).setVal(value).build(); + amList.add(DataProto.newBuilder().setTskvProto(tsKvProto).build()); + } + } + if (!amList.isEmpty()) { + //存储入库 + saveProtoTsKv(amList, dataType); + String top = Topics.V1_TSKV_LATEST.getValue() + eventPublisher.getRequestId(); + eventPublisher.publish(new QueueConsumerEvent(top, amList)); + } + } + + /** + * 每刻数据sum求和,统计出每小时和天总数据并直接存储入库 + * + * @param protoList 刻数据列表 + * @param dataType 数据库类型 + * @param eventPublisher 事件推送服务 + */ + public static void hhDdSum(List protoList, DatabaseType dataType, EventPublisher eventPublisher) { + Map>> ddStart = getStartTime(protoList, "dd"); + List ddProto = new ArrayList<>(); + List protos = new ArrayList<>(); + //统计每小时和天用量 + ddStart.keySet().forEach(ddTs -> { + Map> things = ddStart.get(ddTs); + things.keySet().forEach(code -> { + String sql = hhQuerySum(List.of(code), things.get(code).stream().toList(), new DateTime(ddTs).getMillis(), new DateTime(ddTs).plusDays(1).getMillis(), 60, dataType); + //获取每小时用量 + List rows = Db.selectListBySql(sql); + List hhProto = new ArrayList<>(); + rows.forEach(row -> { + hhProto.add(DataProto.newBuilder().setTskvProto(QueueProto.TsKvProto.newBuilder().setThingCode(row.getString(THING_CODE)).setKey(DaXiaUtils.removeLastKey(row.getString(ATTR_KEY), "am") + "hh").setVal(DaXiaUtils.removeZero(row.getString("value"))).setTs(row.getLong("time")).build()).build()); + }); + //分组求每日合计 + Map hh = hhProto.stream().collect(Collectors.groupingBy(dataProto -> dataProto.getTskvProto().getKey(), Collectors.mapping(proto -> new BigDecimal(proto.getTskvProto().getVal()), Collectors.reducing(BigDecimal.ZERO, BigDecimal::add)))); + //属性替换 + hh.keySet().forEach(key -> { + ddProto.add(DataProto.newBuilder().setTskvProto(QueueProto.TsKvProto.newBuilder().setThingCode(code).setTs(ddTs).setVal(hh.get(key).toPlainString()).setKey(DaXiaUtils.removeLastKey(key, "hh") + "dd").build()).build()); + }); + protos.addAll(hhProto); + }); + }); + protos.addAll(ddProto); + // 先将dd存储入库 + saveProtoTsKv(protos, dataType); + mmSum(ddProto, protos, dataType, eventPublisher); + } + + + /** + * 每天数据sum求和,统计出月总数据直接存储入库 + * + * @param ddProto 天的数据列表 + * @param dataType 数据库类型 + * @param eventPublisher 事件推送服务 + */ + public static void mmSum(List ddProto, List protos, DatabaseType dataType, EventPublisher eventPublisher) { + Map>> mmStart = getStartTime(ddProto, "mm"); + List mmProto = new ArrayList<>(); + mmStart.keySet().forEach(mmTs -> { + DateTime stTs = new DateTime(mmTs); + DateTime enTs = stTs.plusMonths(1); + Map> things = mmStart.get(mmTs); + String sql = thingsAndKeysSum(things, stTs.getMillis(), enTs.getMillis(), dataType); + List mmRow = Db.selectListBySql(sql); + mmRow.forEach(row -> { + mmProto.add(DataProto.newBuilder().setTskvProto(QueueProto.TsKvProto.newBuilder().setThingCode(row.getString(THING_CODE)).setKey(DaXiaUtils.removeLastKey(row.getString(ATTR_KEY), "dd") + "mm").setVal(DaXiaUtils.removeZero(row.getString("value"))).setTs(mmTs).build()).build()); + }); + if (mmRow.isEmpty()) { + log.error("--------DD统计未查到数据-----SQL------------>{}", sql); + } + }); + saveProtoTsKv(mmProto, dataType); + protos.addAll(mmProto); + yySum(mmProto, protos, dataType, eventPublisher); + } + + /** + * 每月数据sum求和,统计出年总数据直接存储入库 + * + * @param mmProto 月的列表 + * @param dataType 数据库类型 + * @param eventPublisher 事件推送服务 + */ + public static void yySum(List mmProto, List protos, DatabaseType dataType, EventPublisher eventPublisher) { + Map>> yyStart = getStartTime(mmProto, "yy"); + List yyProto = new ArrayList<>(); + yyStart.keySet().forEach(yyTs -> { + DateTime stTs = new DateTime(yyTs); + DateTime enTs = stTs.plusYears(1); + Map> things = yyStart.get(yyTs); + String sql = thingsAndKeysSum(things, stTs.getMillis(), enTs.getMillis(), dataType); + List yyRow = Db.selectListBySql(sql); + yyRow.forEach(row -> { + yyProto.add(DataProto.newBuilder().setTskvProto(QueueProto.TsKvProto.newBuilder().setThingCode(row.getString(THING_CODE)).setKey(DaXiaUtils.removeLastKey(row.getString(ATTR_KEY), "mm") + "yy").setVal(DaXiaUtils.removeZero(row.getString("value"))).setTs(yyTs).build()).build()); + }); + }); + saveProtoTsKv(yyProto, dataType); +// log.info("----------------存储yy------------------>{}", TsKvDTO.protoToJson(yyProto)); + protos.addAll(yyProto); + //统一将HH/DD/MM/YY推送出去,主题 不存储 + String top = Topics.V1_TSKV_LATEST.getValue() + eventPublisher.getRequestId(); + eventPublisher.publish(new QueueConsumerEvent(top, protos)); + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvService.java new file mode 100644 index 0000000..578b040 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvService.java @@ -0,0 +1,385 @@ +package com.thing.common.tskv.service; + + +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto.DataProto; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface TsKvService { + + /** + * 存储历史和最新数据 + * + * @param dataProtos 数据集合 + */ + void saveProtoTsKvAndLatest(List dataProtos); + + /** + * 存储历史和最新数据 + * + * @param tsKvDTOList 数据集合 + */ + void saveDTOTsKvAndLatest(List tsKvDTOList); + + /** + * 存储历史数据 + * + * @param tsKvDTOList 数据集合 + * @return + */ + Integer saveDTOTsKv(List tsKvDTOList); + + /** + * 存储历史数据,按表后缀动态生成插入sql + * @param tsKvDTOList + * @return + */ + Integer saveDTOTsKvByTableSuffix(List tsKvDTOList,String tableSuffix); + + /** + * 存储历史数据 + * + * @param dataProtos 数据集合 + * @return + */ + Integer saveProtoTsKv(List dataProtos); + + /** + * 存储最新数据 + * + * @param tsKvDTOList 数据集合 + * @return + */ + Integer saveDTOLatest(List tsKvDTOList); + + /** + * 存储最新数据 + * + * @param dataProtos 数据集合 + * @return + */ + Integer saveProtoLatest(List dataProtos); + + /** + * 递增属性:区间统计计算am + * + * @param dataProtos 数据对象(递增属性) + */ + void amSumProtos(List dataProtos); + + /** + * 递增属性:区间统计计算am + * + * @param tsKvDTOList 数据对象集合(递增属性) + */ + void amSumDTOList(List tsKvDTOList); + + /** + * 递增属性:区间统计计算am,用于手动计算或前端重新计算 + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + void amSumMaps(Map> maps, long stTs, long enTs); + + /** + * 每小时/天/月/年 数据汇总求和 + * + * @param dataProtos 数据对象(属性带有am的区间属性) + */ + void hhDdMmYySum(List dataProtos); + + /** + * 单设备 单属性 最新值 + * + * @param code 物编码 + * @param attrKey 物属性列表 + * @return list + */ + TsKvDTO findLatestByCodeAndAttr(String code, String attrKey); + + + /** + * 单设备 多属性 最新值 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc); + + + /** + * 多设备 多属性 最新值 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc); + + /** + * 多设备 多属性 最新值 分页 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit); + + + /** + * 多设备 多属性 最新值 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findLatestByMultiMap(Map> multiMap, Boolean isAsc); + + + List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备 多属性 最新值 分页 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit); + + /** + * 单设备 单属性 历史数据查询 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param isAsc 默认降序 + * @return list + */ + List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc); + + + /** + * 单设备 单属性 历史聚合数据查询 + * 聚合 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param agg 聚合函数 + * @return list + */ + TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg); + + + /** + * 单设备 单属性 历史数据分页查询 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param isAsc 默认降序 + * @return list + */ + PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 单设备 多属性 历史数据查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc); + + /** + * 单设备 多属性 历史数据聚合查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 单设备 多属性 历史数据分页查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 单设备 多属性 历史聚合数据 分页查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + /** + * 多设备 多属性 历史数据查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc); + + + /** + * 多设备 多属性 历史数据查询 按表名动态拼接sql + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc,String tableSuffix); + + + /** + * 多设备 多属性 历史数据聚合查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 多设备 多属性 历史数据分页查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史聚合数据 分页查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + + /** + * 多设备 多属性 历史数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + + + /** + * 多设备 多属性 历史聚合数据 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 多设备 多属性 历史 数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史聚合数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param code 物编码 + * @param attr 属性 + * @return list + */ + List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param code 物编码 + * @param attr 属性 + * @return list + */ + PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param codeList 物编码集合 + * @param attrList 属性集合 + * @return list + */ + List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param codeList 物编码集合 + * @param attrList 属性集合 + * @return list + */ + PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 多时间查询 + * + * @param codeList key -- 物编码 + * attrList -- 属性集合 + * timeList -- 时间集合 + * @return list + */ + List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList,Boolean isAsc); + + Integer deleteTskv(TsKvDTO kvDTO ); + + Integer deleteLastTskv(TsKvDTO kvDTO ); + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvServiceImpl.java new file mode 100644 index 0000000..96ff303 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/TsKvServiceImpl.java @@ -0,0 +1,279 @@ +package com.thing.common.tskv.service; + +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.latest.LatestBaseService; +import com.thing.common.tskv.service.tskv.TsKvBaseService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.PreDestroy; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class TsKvServiceImpl implements TsKvService { + + private final HhExecutor hhExecutor; + private final AmExecutor amExecutor; + private final DBExecutor dbExecutor; + private final TsKvBaseService tsKvBaseService; + private final LatestBaseService latestBaseService; + + public TsKvServiceImpl(DBExecutor dbExecutor, TsKvBaseService tsKvBaseService, LatestBaseService latestBaseService, AmExecutor amExecutor, HhExecutor hhExecutor) { + this.dbExecutor = dbExecutor; + this.tsKvBaseService = tsKvBaseService; + this.latestBaseService = latestBaseService; + this.amExecutor = amExecutor; + this.hhExecutor = hhExecutor; + } + + @Override + public void saveProtoTsKvAndLatest(List dataProtos) { + if (!dataProtos.isEmpty()) { + saveProtoTsKv(dataProtos); + saveProtoLatest(dataProtos); + } + } + + @Override + public void saveDTOTsKvAndLatest(List tsKvDTOList) { + if (!tsKvDTOList.isEmpty()) { + saveDTOTsKv(tsKvDTOList); + saveDTOLatest(tsKvDTOList); + } + } + + @Override + public Integer saveDTOTsKv(List tsKvDTOList) { + return tsKvBaseService.saveDTOTsKv(tsKvDTOList,null); + } + + @Override + public Integer saveDTOTsKvByTableSuffix(List tsKvDTOList,String tableSuffix) { + return tsKvBaseService.saveDTOTsKv(tsKvDTOList,tableSuffix); + } + + @Override + public Integer saveProtoTsKv(List dataProtos) { + return tsKvBaseService.saveProtoTsKv(dataProtos); + } + + @Override + public Integer saveDTOLatest(List list) { + return latestBaseService.saveDTOLatest(list); + } + + @Override + public Integer saveProtoLatest(List dataProtos) { + dbExecutor.submit(() -> { + latestBaseService.saveProtoLatest(dataProtos); + }); + return dataProtos.size(); + } + + @Override + public void amSumProtos(List dataProtos) { + amExecutor.submit(() -> { + tsKvBaseService.amSumProtos(dataProtos); + }); + } + + @Override + public void amSumDTOList(List tsKvDTOList) { + tsKvBaseService.amSumDTOList(tsKvDTOList); + } + + /** + * 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算的API接口 + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + @Override + public void amSumMaps(Map> maps, long stTs, long enTs) { + amExecutor.submit(() -> { + tsKvBaseService.amSumMaps(maps, stTs, enTs); + }); + } + + @Override + public void hhDdMmYySum(List dataProtos) { + tsKvBaseService.hhDdMmYySum(dataProtos); + } + + @PreDestroy + void stopActorSystem() { + hhExecutor.destroy(); + amExecutor.destroy(); + dbExecutor.destroy(); + } + @Override + public TsKvDTO findLatestByCodeAndAttr(String code, String attrKey) { + return latestBaseService.findLatestByCodeAndAttr(code, attrKey); + } + + @Override + public List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc) { + return latestBaseService.findLatestByCodeAndAttrs(code, attrList, isAsc); + } + + @Override + public List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc) { + return latestBaseService.findLatestByCodesAndAttrs(codeList, attrList, isAsc); + } + + @Override + public PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit) { + return latestBaseService.findPageLatestByCodesAndAttrs(codeList, attrList, isAsc, page, limit); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Boolean isAsc) { + return latestBaseService.findLatestByMultiMap(multiMap, isAsc); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + return latestBaseService.findLatestByMultiMap(multiMap, startTime, endTime, isAsc); + } + + @Override + public PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit) { + return latestBaseService.findPageLatestByMultiMap(multiMap, isAsc, page, limit); + } + + @Override + public List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc) { + return tsKvBaseService.findTsKvByCodeAndAttr(code, attrKey, startTime, endTime, isAsc); + } + + @Override + public TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg) { + return tsKvBaseService.findTsKvAggByCodeAndAttr(code, attrKey, startTime, endTime, agg); + } + + @Override + public PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvByCodeAndAttr(code, attrKey, startTime, endTime, isAsc, page, limit); + } + + @Override + public List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + return tsKvBaseService.findTsKvByCodeAndAttrs(code, attrList, startTime, endTime, isAsc); + } + + @Override + public List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return tsKvBaseService.findTsKvAggByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvAggByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + return tsKvBaseService.findTsKvByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc); + } + + @Override + public List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, String tableSuffix) { + return tsKvBaseService.findTsKvByCodesAndAttrsWithTableSuffix(codeList, attrList, startTime, endTime, isAsc,tableSuffix); + } + + @Override + public List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return tsKvBaseService.findTsKvAggByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvAggByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + return tsKvBaseService.findTsKvByMultiMap(multiMap, startTime, endTime, isAsc); + } + + @Override + public List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return tsKvBaseService.findTsKvAggByMultiMap(multiMap, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findPageTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findTsKvPageByMultiMap(multiMap, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvAggByMultiMap(multiMap, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return tsKvBaseService.findTsKvIntervalAggByCodeAndAttr(code, attr, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvIntervalAggByCodeAndAttr(code, attr, startTime, endTime, agg, interval, timeType, isAsc,page, limit); + } + + @Override + public List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return tsKvBaseService.findTsKvIntervalAggByCodesAndAttrs(codeList, attrList, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvIntervalAggByCodesAndAttrs(codeList, attrList, startTime, endTime, agg, interval, timeType, isAsc,page, limit); + } + + @Override + public List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return tsKvBaseService.findTsKvIntervalAggByMultiMap(multiMap, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return tsKvBaseService.findPageTsKvIntervalAggByMultiMap(multiMap, startTime, endTime, agg, interval, timeType, isAsc,page, limit); + } + + @Override + public List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList, Boolean isAsc) { + return tsKvBaseService.findTsKvByCodesAndKeys(codeList, attrList, timeList, isAsc); + } + + @Override + public Integer deleteTskv(TsKvDTO kvDTO) { + return tsKvBaseService.deleteTskv(kvDTO); + } + + @Override + public Integer deleteLastTskv(TsKvDTO kvDTO) { + return tsKvBaseService.deleteLastTskv(kvDTO); + + } + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestBaseService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestBaseService.java new file mode 100644 index 0000000..af5756e --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestBaseService.java @@ -0,0 +1,75 @@ +package com.thing.common.tskv.service.latest; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface LatestBaseService { + + Integer saveDTOLatest(List kvDTOList); + + Integer saveProtoLatest(List protoList); + + /** + * 单设备 单属性 最新值 + * + * @param code 物编码 + * @param attrKey 物属性列表 + * @return list + */ + TsKvDTO findLatestByCodeAndAttr(String code, String attrKey); + + /** + * 单设备 多属性 最新值 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc); + + + /** + * 多设备 多属性 最新值 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc); + + /** + * 多设备 多属性 最新值 分页 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit); + + + /** + * 多设备 多属性 最新值 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findLatestByMultiMap(Map> multiMap, Boolean isAsc); + + List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + /** + * 多设备 多属性 最新值 分页 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit); + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkService.java new file mode 100644 index 0000000..cef8023 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkService.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.service.IService; +import com.thing.common.tskv.entity.TsKvLatestCk; + +@UseDataSource("ck") +public interface LatestCkService extends IService, LatestBaseService { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkServiceImpl.java new file mode 100644 index 0000000..0bafee6 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestCkServiceImpl.java @@ -0,0 +1,90 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.core.row.Db; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.TsKvLatestCk; +import com.thing.common.tskv.mapper.TsKvLatestCkMapper; +import com.thing.common.tskv.mapper.ViewTsKvLatestCkMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.LatestNativeSQL; +import com.thing.common.tskv.service.tskv.ViewTsKvCkService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +@ConditionalOnExpression("'${database.latest.type}'=='clickhouse'") +@AllArgsConstructor +public class LatestCkServiceImpl extends ServiceImpl implements LatestCkService { + + private final ViewLatestCkService viewLatestCkService; + private final ViewTsKvCkService viewTsKvCkService; + + @Override + public Integer saveDTOLatest(List kvDTOList) { + if (kvDTOList.isEmpty()) { + return 0; + } + List list = TsKvDTO.removeDuplicates(kvDTOList); + String sql = LatestNativeSQL.sqlSaveDTOLatest(list, DatabaseType.CK); + try { + Db.insertBySql(sql); + } catch (Exception exception) { + log.error("------TsKvLatestCK----数据存储失败------->{}----->{}", sql, exception.getMessage()); + throw new RuntimeException(exception); + } +// log.info("---------时序数据存储耗时----->{}", new DateTime().getMillis() - timeST.getMillis()); + return kvDTOList.size(); + } + + @Override + public Integer saveProtoLatest(List protoList) { + return saveDTOLatest(TsKvDTO.toTsKvDTOs(protoList)); + } + + @Override + public TsKvDTO findLatestByCodeAndAttr(String code, String attrKey) { + return viewLatestCkService.findLatestByCodeAndAttr(code, attrKey); + } + + @Override + public List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc) { + return viewLatestCkService.findLatestByCodeAndAttrs(code, attrList, isAsc); + } + + @Override + public List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc) { + return viewLatestCkService.findLatestByCodesAndAttrs(codeList, attrList, isAsc); + } + + @Override + public PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit) { + return viewLatestCkService.findPageLatestByCodesAndAttrs(codeList, attrList, isAsc, page, limit); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Boolean isAsc) { + return viewLatestCkService.findLatestByMultiMap(multiMap, isAsc); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + return viewLatestCkService.findLatestByMultiMap(multiMap,startTime,endTime, isAsc); + } + + @Override + public PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit) { + return viewLatestCkService.findPageLatestByMultiMap(multiMap, isAsc, page, limit); + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyService.java new file mode 100644 index 0000000..cefc60f --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyService.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.service.IService; +import com.thing.common.tskv.entity.TsKvLatestMy; + +@UseDataSource("my") +public interface LatestMyService extends IService, LatestBaseService { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyServiceImpl.java new file mode 100644 index 0000000..655906a --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestMyServiceImpl.java @@ -0,0 +1,128 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.TsKvLatestMy; +import com.thing.common.tskv.mapper.TsKvLatestMyMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.LatestNativeSQL; +import com.thing.common.tskv.service.latest.LatestMyService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.thing.common.tskv.entity.table.TsKvLatestMyTableDef.TS_KV_LATEST_MY; + + +@Slf4j +@Service +@ConditionalOnExpression("'${database.latest.type}'=='mysql' || '${database.latest.type}'=='tidb'") +public class LatestMyServiceImpl extends ServiceImpl implements LatestMyService { + + + private QueryWrapper getQueryWrapper(String code, Collection codeList, String attrKey, Collection attrList, Boolean isAsc) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(TsKvLatestMy::getThingCode, code, StringUtils::isNotBlank) + .eq(TsKvLatestMy::getAttrKey, attrKey, StringUtils::isNotBlank) + .in(TsKvLatestMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvLatestMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .orderBy(TsKvLatestMy::getTs, isAsc); + return queryWrapper; + } + + @Override + public Integer saveDTOLatest(List kvDTOList) { + if (kvDTOList.isEmpty()) { + return 0; + } + List list = TsKvDTO.removeDuplicates(kvDTOList); + String sql = LatestNativeSQL.sqlSaveDTOLatest(list, DatabaseType.MYSQL); + try { + Db.insertBySql(sql); + } catch (Exception exception) { + log.error("------TsKvLatestMy----数据存储失败------->{}----->{}", sql, exception.getMessage()); + throw new RuntimeException(exception); + } + return list.size(); + } + + @Override + public Integer saveProtoLatest(List protoList) { + return saveDTOLatest(TsKvDTO.toTsKvDTOs(protoList)); + } + + @Override + public TsKvDTO findLatestByCodeAndAttr(String code, String attrKey) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, attrKey, null, false); + return this.getOneAs(queryWrapper, TsKvDTO.class); + } + + + @Override + public List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, null, attrList, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, isAsc); + Page tsKvDTOPage = mapper.paginateAs(page, limit, queryWrapper, TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(), tsKvDTOPage.getTotalRow()); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Boolean isAsc) { + QueryChain queryChain = getTsKvLatestMyQueryChain(multiMap); + queryChain.orderBy(TsKvLatestMy::getTs, isAsc); + return mapper.selectListByQueryAs(queryChain.toQueryWrapper(), TsKvDTO.class); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = getTsKvLatestMyQueryChain(multiMap); + queryChain.between(TsKvLatestMy::getTs, startTime, endTime); + queryChain.orderBy(TsKvLatestMy::getTs, isAsc); + return mapper.selectListByQueryAs(queryChain.toQueryWrapper(), TsKvDTO.class); + } + + @Override + public PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit) { + QueryChain queryChain = getTsKvLatestMyQueryChain(multiMap); + queryChain.orderBy(TsKvLatestMy::getTs, isAsc); + Page tsKvDTOPage = mapper.paginateAs(page, limit, queryChain.toQueryWrapper(), TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(), tsKvDTOPage.getTotalRow()); + } + + private QueryChain getTsKvLatestMyQueryChain(Map> multiMap) { + QueryChain queryChain = queryChain(); + for (Map.Entry> entry : multiMap.entrySet()) { + String code = entry.getKey(); + Collection attrList = entry.getValue(); + queryChain.or(queryWrapper -> { + queryWrapper.eq(TsKvLatestMy::getThingCode, code).in(TsKvLatestMy::getAttrKey, attrList); + }); + } + return queryChain; + } + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgService.java new file mode 100644 index 0000000..24c9cae --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgService.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.service.IService; +import com.thing.common.tskv.entity.TsKvLatestPg; + +@UseDataSource("postgresql") +public interface LatestPgService extends IService, LatestBaseService { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgServiceImpl.java new file mode 100644 index 0000000..74029bf --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/LatestPgServiceImpl.java @@ -0,0 +1,126 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.TsKvLatestMy; +import com.thing.common.tskv.entity.TsKvLatestPg; +import com.thing.common.tskv.mapper.TsKvLatestPgMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.LatestNativeSQL; +import com.thing.common.tskv.service.latest.LatestPgService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; + +import static com.thing.common.tskv.entity.table.TsKvLatestPgTableDef.TS_KV_LATEST_PG; + + +@Slf4j +@Service +@ConditionalOnExpression("'${database.latest.type}'=='timescale' || '${database.latest.type}'=='postgresql'") +public class LatestPgServiceImpl extends ServiceImpl implements LatestPgService { + + @Override + public Integer saveDTOLatest(List kvDTOList) { + if (kvDTOList.isEmpty()) { + return 0; + } + List list = TsKvDTO.removeDuplicates(kvDTOList); + String sql = LatestNativeSQL.sqlSaveDTOLatest(list, DatabaseType.PG); + try { + Db.insertBySql(sql); + } catch (Exception exception) { + log.error("------TsKvLatestPG----数据存储失败------->{}----->{}", sql, exception.getMessage()); + throw new RuntimeException(exception); + } + return list.size(); + } + + @Override + public Integer saveProtoLatest(List protoList) { + return saveDTOLatest(TsKvDTO.toTsKvDTOs(protoList)); + } + + private QueryWrapper getQueryWrapper(String code,Collection codeList, String attrKey,Collection attrList,Boolean isAsc) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(TsKvLatestMy::getThingCode, code, StringUtils::isNotBlank) + .eq(TsKvLatestMy::getAttrKey, attrKey, StringUtils::isNotBlank) + .in(TsKvLatestMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvLatestMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .orderBy(TsKvLatestMy::getTs, isAsc); + return queryWrapper; + } + + @Override + public TsKvDTO findLatestByCodeAndAttr(String code, String attrKey) { + QueryWrapper queryWrapper = getQueryWrapper(code, null,attrKey,null,false); + return this.getOneAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(code, null,null,attrList,isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(null,codeList, null,attrList,isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(null,codeList, null,attrList,isAsc); + Page tsKvDTOPage = mapper.paginateAs(page, limit, queryWrapper, TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(),tsKvDTOPage.getTotalRow()); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Boolean isAsc) { + QueryChain queryChain = getTsKvLatestPgQueryChain(multiMap); + queryChain.orderBy(TsKvLatestPg::getTs,isAsc); + return mapper.selectListByQueryAs(queryChain.toQueryWrapper(),TsKvDTO.class); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = getTsKvLatestPgQueryChain(multiMap); + queryChain.between(TsKvLatestPg::getTs,startTime,endTime); + queryChain.orderBy(TsKvLatestPg::getTs,isAsc); + return mapper.selectListByQueryAs(queryChain.toQueryWrapper(),TsKvDTO.class); + } + + @Override + public PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit) { + QueryChain queryChain = getTsKvLatestPgQueryChain(multiMap); + queryChain.orderBy(TsKvLatestMy::getTs,isAsc); + Page tsKvDTOPage = mapper.paginateAs(page, limit, queryChain.toQueryWrapper(), TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(),tsKvDTOPage.getTotalRow()); + } + + private QueryChain getTsKvLatestPgQueryChain(Map> multiMap) { + QueryChain queryChain = queryChain(); + for (Map.Entry> entry : multiMap.entrySet()) { + String code = entry.getKey(); + Collection attrList = entry.getValue(); + queryChain.or(queryWrapper -> { + queryWrapper.eq(TsKvLatestPg::getThingCode, code).in(TsKvLatestPg::getAttrKey,attrList); + }); + } + return queryChain; + } +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkService.java new file mode 100644 index 0000000..6077db2 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkService.java @@ -0,0 +1,31 @@ +package com.thing.common.tskv.service.latest; + +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.service.IService; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.ViewTsKvLatest; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +@UseDataSource("ck") +public interface ViewLatestCkService extends IService { + + + TsKvDTO findLatestByCodeAndAttr(String code, String attrKey); + + List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc); + + List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc); + + PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit); + + List findLatestByMultiMap(Map> multiMap, Boolean isAsc); + + List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + + PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit); + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkServiceImpl.java new file mode 100644 index 0000000..df1cd08 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/latest/ViewLatestCkServiceImpl.java @@ -0,0 +1,102 @@ +package com.thing.common.tskv.service.latest; + +import com.google.common.collect.Lists; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.Row; +import com.mybatisflex.core.row.RowUtil; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.NativeSQLTool; +import com.thing.common.tskv.entity.ViewTsKvLatest; +import com.thing.common.tskv.mapper.ViewTsKvLatestCkMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Slf4j +@Service +@ConditionalOnExpression("'${database.latest.type}'=='clickhouse'") +public class ViewLatestCkServiceImpl extends ServiceImpl implements ViewLatestCkService { + + @Override + public TsKvDTO findLatestByCodeAndAttr(String code, String attrKey) { + String sql = " SELECT thing_code,attr_key,ts,val FROM view_latest WHERE thing_code = '" + + code + "' AND attr_key='" + attrKey + "'"; + Row row = Db.selectOneBySql(sql); + if(Objects.isNull(row)){ + return null; + } + return row.toEntity(TsKvDTO.class); + } + + @Override + public List findLatestByCodeAndAttrs(String code, Collection attrList, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),attrList,sql,null,null); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + List rows = Db.selectListBySql(sql.toString()); + return RowUtil.toEntityList(rows,TsKvDTO.class); + } + + @Override + public List findLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,null,null); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public PageData findPageLatestByCodesAndAttrs(Collection codeList, Collection attrList, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,null,null); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest where "); + NativeSQLTool.spliceMapSql(multiMap,sql,null,null); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public List findLatestByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest where "); + NativeSQLTool.spliceMapSql(multiMap,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public PageData findPageLatestByMultiMap(Map> multiMap, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_latest where "); + NativeSQLTool.spliceMapSql(multiMap,sql,null,null); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvBaseService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvBaseService.java new file mode 100644 index 0000000..eb92863 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvBaseService.java @@ -0,0 +1,278 @@ +package com.thing.common.tskv.service.tskv; + +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface TsKvBaseService { + + /** + * 时序数据存储 + * + * @param kvDTOList 数据集合 + * @return 保存数量 + */ + Integer saveDTOTsKv(List kvDTOList,String tableSuffix); + + /** + * 存储最新数据 + * + * @param list 数据集合 + * @return + */ + Integer saveProtoTsKv(List list); + + /** + * 递增属性:区间统计计算am + * + * @param dataProtos 数据对象(递增属性) + */ + void amSumProtos(List dataProtos); + + /** + * 递增属性:区间统计计算am + * + * @param tsKvDTOList 数据对象集合(递增属性) + */ + void amSumDTOList(List tsKvDTOList); + + /** + * 递增属性:区间统计计算am + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + void amSumMaps(Map> maps, long stTs, long enTs); + + /** + * 每小时/天/月/年 数据汇总求和 + * + * @param protoList 数据对象(属性带有am的区间属性) + */ + void hhDdMmYySum(List protoList); + + /** + * 单设备 单属性 历史数据查询 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param isAsc 默认降序 + * @return list + */ + List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc); + + /** + * 单设备 单属性 历史聚合数据查询 + * 聚合 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param agg 聚合函数 + * @return list + */ + TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg); + + + /** + * 单设备 单属性 历史数据分页查询 + * + * @param code 物编码 + * @param attrKey 物属性 + * @param isAsc 默认降序 + * @return list + */ + PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + + /** + * 单设备 多属性 历史数据查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc); + + /** + * 单设备 多属性 历史数据聚合查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 单设备 多属性 历史数据分页查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 单设备 多属性 历史聚合数据 分页查询 + * + * @param code 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + + /** + * 多设备 多属性 历史数据查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备 多属性 历史数据聚合查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 多设备 多属性 历史数据分页查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史聚合数据 分页查询 + * + * @param codeList 物编码 + * @param attrList 物属性列表 + * @return list + */ + PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + + /** + * 多设备 多属性 历史数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备 多属性 历史聚合数据 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + + /** + * 多设备 多属性 历史 数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findTsKvPageByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史聚合数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param code 物编码 + * @param attr 属性 + * @return list + */ + List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param code 物编码 + * @param attr 属性 + * @return list + */ + PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param codeList 物编码集合 + * @param attrList 属性集合 + * @return list + */ + List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param codeList 物编码集合 + * @param attrList 属性集合 + * @return list + */ + PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + + /** + * 多设备 多属性 历史区间聚合数据 查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + /** + * 多设备 多属性 历史区间聚合数据 分页查询 + * + * @param multiMap key -- 物编码 + * value -- 属性集合 + * @return list + */ + PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + /** + * 多设备 多属性 多时间查询 + * + * @param codeList key -- 物编码 + * attrList -- 属性集合 + * timeList -- 时间集合 + * @return list + */ + List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList,Boolean isAsc); + + + Integer deleteTskv(TsKvDTO kvDTO); + + Integer deleteLastTskv(TsKvDTO kvDTO); + + List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, String tableSuffix); +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkService.java new file mode 100644 index 0000000..584fa77 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkService.java @@ -0,0 +1,112 @@ +package com.thing.common.tskv.service.tskv; + +import com.mybatisflex.core.service.IService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.TsKvCk; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +public interface TsKvCkService extends IService,TsKvBaseService{ + + /** + * 时序数据存储 + * + * @param kvDTOList 数据集合 + * @return 保存数量 + */ +// Integer saveTsKv(List kvDTOList); +// +// /** +// * 递增属性:区间统计计算am +// * +// * @param dataProto 数据对象(递增属性) +// */ +// void amSumProto(QueueProto.DataProto dataProto); +// +// /** +// * 递增属性:区间统计计算am +// * +// * @param tsKvDTO 数据对象(递增属性) +// */ +// void amSumDTO(TsKvDTO tsKvDTO); +// +// /** +// * 递增属性:区间统计计算am +// * +// * @param dataProtos 数据对象(递增属性) +// */ +// void amSumProtos(List dataProtos); +// +// /** +// * 递增属性:区间统计计算am +// * +// * @param tsKvDTOList 数据对象集合(递增属性) +// */ +// void amSumDTOList(List tsKvDTOList); +// +// /** +// * 递增属性:区间统计计算am +// * +// * @param maps 物编号对应属性集合 +// * @param stTs 开始时间 +// * @param enTs 结束属性 +// */ +// void amSumMaps(Map> maps, long stTs, long enTs); +// +// /** +// * 每小时/天/月/年 数据汇总求和 +// * +// * @param protoList 数据对象(属性带有am的区间属性) +// */ +// void hhDdMmYySum(List protoList); +// +// List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc); +// +// TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg); +// +// PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); +// +// List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc); +// +// List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); +// +// PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); +// +// PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); +// +// List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc); +// +// List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); +// +// PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); +// +// PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); +// +// List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); +// +// List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg); +// +// PageData findPageTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); +// +// PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); +// +// List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); +// +// PageData findPageTsKvIntervalAggByCodeAndAttr(String code,String attr,Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); +// +// List findTsKvIntervalAggByCodesAndAttrs(Collection codeList,Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); +// +// PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList,Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); +// +// List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); +// +// PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkServiceImpl.java new file mode 100644 index 0000000..d315abc --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvCkServiceImpl.java @@ -0,0 +1,241 @@ +package com.thing.common.tskv.service.tskv; + +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.event.EventPublisher; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.NativeSQLTool; +import com.thing.common.tskv.entity.TsKvCk; +import com.thing.common.tskv.entity.TsKvMy; +import com.thing.common.tskv.mapper.TsKvCkMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.TsKvNativeSQL; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.thing.common.util.time.DaXiaUtils.getAmEndTime; +import static com.thing.common.util.time.DaXiaUtils.getAmStartTime; + + +@Slf4j +@Service +@UseDataSource("ck") +@ConditionalOnExpression("'${database.ts_kv.type}'=='clickhouse'") +public class TsKvCkServiceImpl extends ServiceImpl implements TsKvCkService { + @Value("${calculate.am_total.offset:24}") + private int offset; + @Value("${calculate.am_total.interval:15}") + private int interval; + private final EventPublisher eventPublisher; + private final ViewTsKvCkService viewTsKvCkService; + + + public TsKvCkServiceImpl(EventPublisher eventPublisher, ViewTsKvCkService viewTsKvCkService) { + this.eventPublisher = eventPublisher; + this.viewTsKvCkService = viewTsKvCkService; + } + + @Override + public Integer saveDTOTsKv(List kvDTOList,String tableSuffix) { + return TsKvNativeSQL.saveDTOTsKv(kvDTOList, DatabaseType.CK,tableSuffix); + } + + @Override + public Integer saveProtoTsKv(List dataProtos) { + return TsKvNativeSQL.saveProtoTsKv(dataProtos, DatabaseType.CK); + } + + + @Override + public void amSumProtos(List dataProtos) { + TsKvNativeSQL.amSumV1(dataProtos, offset, interval, DatabaseType.CK, eventPublisher); + } + + @Override + public void amSumDTOList(List tsKvDTOList) { + amSumProtos(TsKvDTO.toDataProtoList(tsKvDTOList)); + } + + /** + * 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算的API接口 + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + @Override + public void amSumMaps(Map> maps, long stTs, long enTs) { + DateTime st = getAmStartTime(stTs, this.interval); + DateTime en = getAmEndTime(enTs, this.interval); + maps.keySet().forEach(thingCode -> { + if (!maps.get(thingCode).isEmpty()) { + TsKvNativeSQL.amSum(thingCode, maps.get(thingCode), st.getMillis(), en.getMillis(), interval, DatabaseType.CK, eventPublisher); + } + }); + } + + @Override + public void hhDdMmYySum(List protoList) { + TsKvNativeSQL.hhDdSum(protoList, DatabaseType.CK, eventPublisher); + } + + @Override + public List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc) { + return viewTsKvCkService.findTsKvByCodeAndAttr(code, attrKey, startTime, endTime, isAsc); + } + + @Override + public TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg) { + return viewTsKvCkService.findTsKvAggByCodeAndAttr(code, attrKey, startTime, endTime, agg); + } + + @Override + public PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvByCodeAndAttr(code, attrKey, startTime, endTime, isAsc, page, limit); + } + + @Override + public List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + return viewTsKvCkService.findTsKvByCodeAndAttrs(code, attrList, startTime, endTime, isAsc); + } + + @Override + public List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return viewTsKvCkService.findTsKvAggByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvAggByCodeAndAttrs(code, attrList, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + return viewTsKvCkService.findTsKvByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc,""); + } + + @Override + public List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return viewTsKvCkService.findTsKvAggByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvAggByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + return viewTsKvCkService.findTsKvByMultiMap(multiMap, startTime, endTime, isAsc); + } + + @Override + public List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + return viewTsKvCkService.findTsKvAggByMultiMap(multiMap, startTime, endTime, isAsc, agg); + } + + @Override + public PageData findTsKvPageByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvByMultiMap(multiMap, startTime, endTime, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvAggByMultiMap(multiMap, startTime, endTime, isAsc, agg, page, limit); + } + + @Override + public List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return viewTsKvCkService.findTsKvIntervalAggByCodeAndAttr(code, attr, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvIntervalAggByCodeAndAttr(code, attr, startTime, endTime, agg, interval, timeType, isAsc, page, limit); + } + + @Override + public List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return viewTsKvCkService.findTsKvIntervalAggByCodesAndAttrs(codeList, attrList, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvIntervalAggByCodesAndAttrs(codeList, attrList, startTime, endTime, agg, interval, timeType, isAsc, page, limit); + } + + @Override + public List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + return viewTsKvCkService.findTsKvIntervalAggByMultiMap(multiMap, startTime, endTime, agg, interval, timeType, isAsc); + } + + @Override + public PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + return viewTsKvCkService.findPageTsKvIntervalAggByMultiMap(multiMap, startTime, endTime, agg, interval, timeType, isAsc, page, limit); + } + + @Override + public List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList, Boolean isAsc) { + QueryWrapper queryWrapper = QueryWrapper.create(); + queryWrapper.in(TsKvMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .in(TsKvMy::getTs,timeList,CollectionUtils.isNotEmpty(timeList)); + queryWrapper.orderBy(TsKvMy::getTs, isAsc); + return mapper.selectListByQueryAs(queryWrapper,TsKvDTO.class); + } + + @Override + public Integer deleteTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteTskvSql(kvDTO); + return Db.deleteBySql(delSql); + } + + @Override + public Integer deleteLastTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteLastTskvSql(kvDTO); + return Db.deleteBySql(delSql); } + + @Override + public List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, String tableSuffix) { + return viewTsKvCkService.findTsKvByCodesAndAttrs(codeList, attrList, startTime, endTime, isAsc,tableSuffix); + } + + private QueryChain getTsKvCKQueryChain(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = queryChain(); + queryChain.between(TsKvCk::getTs, startTime, endTime); + queryChain.and(" ( 1!=1 "); + multiMap.forEach((code, attrList) -> queryChain.or(queryWrapper -> { + queryWrapper.eq(TsKvCk::getThingCode, code) + .in(TsKvCk::getAttrKey, attrList); + })); + queryChain.or(" 1!=1 ) "); + queryChain.orderBy(TsKvCk::getTs, isAsc); + return queryChain; + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyService.java new file mode 100644 index 0000000..a0e470a --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyService.java @@ -0,0 +1,9 @@ +package com.thing.common.tskv.service.tskv; + +import com.mybatisflex.core.service.IService; +import com.thing.common.tskv.entity.TsKvMy; + + +public interface TsKvMyService extends IService, TsKvBaseService { + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyServiceImpl.java new file mode 100644 index 0000000..e09ec26 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvMyServiceImpl.java @@ -0,0 +1,422 @@ +package com.thing.common.tskv.service.tskv; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.Row; +import com.mybatisflex.core.row.RowUtil; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.DataBaseType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.event.EventPublisher; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.NativeSQLTool; +import com.thing.common.tskv.entity.TsKvMy; +import com.thing.common.tskv.mapper.TsKvMyMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.TsKvNativeSQL; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; + +import static com.thing.common.tskv.NativeSQLTool.*; +import static com.thing.common.util.time.DaXiaUtils.getAmEndTime; +import static com.thing.common.util.time.DaXiaUtils.getAmStartTime; + + +@Slf4j +@Service +@UseDataSource("my") +@ConditionalOnExpression("'${database.ts_kv.type}'=='mysql' || '${database.ts_kv.type}'=='tidb'") +public class TsKvMyServiceImpl extends ServiceImpl implements TsKvMyService { + + @Value("${calculate.am_total.offset:24}") + private int offset; + @Value("${calculate.am_total.interval:15}") + private int interval; + private final EventPublisher eventPublisher; + + public TsKvMyServiceImpl(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + // private final Map intervalMap = Maps.newHashMap(); + + @Override + public Integer saveDTOTsKv(List kvDTOList,String tableSuffix) { + return TsKvNativeSQL.saveDTOTsKv(kvDTOList, DatabaseType.MYSQL,tableSuffix); + } + + @Override + public Integer saveProtoTsKv(List dataProtos) { + return TsKvNativeSQL.saveProtoTsKv(dataProtos, DatabaseType.MYSQL); + } + + + @Override + public void amSumProtos(List dataProtos) { + TsKvNativeSQL.amSumV1(dataProtos, offset, interval, DatabaseType.MYSQL, eventPublisher); + } + + @Override + public void amSumDTOList(List tsKvDTOList) { + amSumProtos(TsKvDTO.toDataProtoList(tsKvDTOList)); + } + + /** + * 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算的API接口 + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + @Override + public void amSumMaps(Map> maps, long stTs, long enTs) { + DateTime st = getAmStartTime(stTs, this.interval); + DateTime en = getAmEndTime(enTs, this.interval); + maps.keySet().forEach(thingCode -> { + if (!maps.get(thingCode).isEmpty()) { + TsKvNativeSQL.amSum(thingCode, maps.get(thingCode), st.getMillis(), en.getMillis(), interval, DatabaseType.MYSQL, eventPublisher); + } + }); + } + + /** + * 每小时/日/月/年的数据统计计算 + * + * @param protoList 数据对象(带有am的区间属性) + */ + @Override + public void hhDdMmYySum(List protoList) { + TsKvNativeSQL.hhDdSum(protoList, DatabaseType.MYSQL, eventPublisher); + } + +// @PostConstruct +// private void initMap() { +// intervalMap.put(TimeType.DAY, " (FLOOR((ts - startTime ) / (86400000 * r)) * 86400000 * r + startTime) as time,"); +// intervalMap.put(TimeType.HOUR, " (FLOOR((ts - startTime) / (3600000 * r )) * 3600000 * r + startTime) as time ,"); +// intervalMap.put(TimeType.MINUTE, " (FLOOR((ts - startTime) / (60000 * r)) * 60000 * r + startTime) as time ,"); +// } + + + private QueryWrapper getQueryWrapper(String code, Collection codeList, String attrKey, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = QueryWrapper.create(); + queryWrapper.eq(TsKvMy::getThingCode, code, StringUtils::isNotBlank) + .eq(TsKvMy::getAttrKey, attrKey, StringUtils::isNotBlank) + .in(TsKvMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .between(TsKvMy::getTs, startTime, endTime, ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)); + queryWrapper.orderBy(TsKvMy::getTs, isAsc); + return queryWrapper; + } + + + private QueryChain getTsKvMyQueryChain(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = queryChain(); + queryChain.between(TsKvMy::getTs, startTime, endTime); + queryChain.and(" ( 1!=1 "); + multiMap.forEach((code, attrList) -> queryChain.or(queryWrapper -> { + queryWrapper.eq(TsKvMy::getThingCode, code) + .in(TsKvMy::getAttrKey, attrList); + })); + queryChain.or(" 1!=1 ) "); + queryChain.orderBy(TsKvMy::getTs, isAsc); + return queryChain; + } + + @Override + public List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(TsKvMy::getThingCode, code).eq(TsKvMy::getAttrKey, attrKey) + .between(TsKvMy::getTs, startTime, endTime).orderBy(TsKvMy::getTs, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrapper", Collection.class, Collection.class, Long.class, Long.class, AggType.class, DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), Lists.newArrayList(attrKey), startTime, endTime, agg, DataBaseType.MYSQL); + Row rows = Db.selectOneBySql(sql); + return rows.toEntity(TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrapper"), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, attrKey, null, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, null, attrList, startTime, endTime, isAsc); + return mapper.selectListByQueryAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.MYSQL); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, null, attrList, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.MYSQL); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, startTime, endTime, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.MYSQL); + sql = sql + " ORDER BY ts "; + if (!isAsc) { + sql = sql + " DESC"; + } + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.MYSQL); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = getTsKvMyQueryChain(multiMap, startTime, endTime, isAsc); + return this.listAs(queryChain.toQueryWrapper(), TsKvDTO.class); + } + + @Override + public List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "MapWrapper", Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.MYSQL); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "MapWrapper"), e); + } + } + + @Override + public PageData findTsKvPageByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryChain queryChain = getTsKvMyQueryChain(multiMap, startTime, endTime, isAsc); + Page tsKvDTOPage = this.pageAs(new Page<>(page, limit), queryChain.toQueryWrapper(), TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(), tsKvDTOPage.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "MapWrapper", Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.MYSQL); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "MapWrapper"), e); + } + } + + @Override + public List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(Lists.newArrayList(code), Lists.newArrayList(attr), null, startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + //校验时间范围:不能超过时间,属性是否匹配 +// TimeType.matchTimeRange(timeType, startTime, endTime, interval); +// ThingAttrType.matchAttr(attr, timeType); + StringBuilder sql = new StringBuilder(); + intervalSql(Lists.newArrayList(code), Lists.newArrayList(attr), null, startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(codeList, attrList, null, startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(); + intervalSql(codeList, attrList, null, startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(null, null, multiMap, startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(); + intervalSql(null, null, multiMap, startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList, Boolean isAsc) { + QueryWrapper queryWrapper = QueryWrapper.create(); + queryWrapper.in(TsKvMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .in(TsKvMy::getTs,timeList,CollectionUtils.isNotEmpty(timeList)); + queryWrapper.orderBy(TsKvMy::getTs, isAsc); + return mapper.selectListByQueryAs(queryWrapper,TsKvDTO.class); + } + + @Override + public Integer deleteTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteTskvSql(kvDTO); + return Db.deleteBySql(delSql); + } + + @Override + public Integer deleteLastTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteLastTskvSql(kvDTO); + return Db.deleteBySql(delSql); + } + + @Override + public List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, String tableSuffix) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM thing_ts_kv"); + sql.append(tableSuffix); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + private PageData getTsKvDTOPageIntervalData(Integer page, Integer limit, long count, String sql) { + if (ObjectUtil.equals(count, 0L)) { + return new PageData<>(Lists.newArrayList(), count); + } + return new PageData<>(RowUtil.toEntityList(Db.selectListBySql(sql + " LIMIT " + limit + " OFFSET " + (page - 1) * limit), TsKvDTO.class), count); + } + + private void intervalSql(Collection codeList, Collection attrList, Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, StringBuilder sql) { + if (TimeType.YEAR.equals(timeType) || TimeType.MONTH.equals(timeType)) { + sql.append("WITH RECURSIVE date_list AS ( SELECT FROM_UNIXTIME( ").append(startTime).append(" / 1000, '%Y-%m-%d %H:%i:%s') as date ") + .append(" UNION ALL ") + .append(" SELECT DATE_ADD(date, INTERVAL ").append(interval).append(" ").append(timeType).append(" ) FROM date_list WHERE date < FROM_UNIXTIME( "); + sql.append(endTime).append(" / 1000, '%Y-%m-%d %H:%i:%s' )) "); + sql.append(" SELECT CAST(UNIX_TIMESTAMP(date_list.date) * 1000 AS SIGNED) AS ts, thing_code,attr_key, ") + .append(agg) + .append("(CAST(val AS SIGNED)) as val FROM date_list "); + sql.append(" LEFT JOIN ").append(TS_KV).append(" ON ts >= UNIX_TIMESTAMP(date)*1000 AND ts <= UNIX_TIMESTAMP(DATE_ADD(date, INTERVAL ") + .append(interval).append(" ").append(timeType).append(" ))* 1000 "); + if (MapUtils.isNotEmpty(multiMap)) { + sql.append(" WHERE "); + spliceMapSql(multiMap, sql, startTime, endTime); + } else { + spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + } + sql.append(" GROUP BY thing_code,attr_key ,date "); + sql.append(" ORDER BY date "); + } else { + sql.append(" SELECT thing_code, attr_key, time as ts,val from (") + .append(" SELECT thing_code, attr_key, ") + .append(" (FLOOR((ts - ").append(startTime).append(" ) / ( "); + + if (TimeType.DAY.equals(timeType)) { + sql.append(" 86400000 * ").append(interval).append(")) * 86400000 * ").append(interval).append(" + ").append(startTime); + } + if (TimeType.HOUR.equals(timeType)) { + sql.append(" 3600000 * ").append(interval).append(")) * 3600000 * ").append(interval).append(" + ").append(startTime); + } + if (TimeType.MINUTE.equals(timeType)) { + sql.append(" 60000 * ").append(interval).append(")) * 60000 * ").append(interval).append(" + ").append(startTime); + } + sql.append(" ) as time, ").append(agg).append("(CAST(val AS SIGNED)) AS val FROM ").append(TS_KV); + if (MapUtils.isNotEmpty(multiMap)) { + sql.append(" WHERE "); + spliceMapSql(multiMap, sql, startTime, endTime); + } else { + spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + } + sql.append(" GROUP BY thing_code, attr_key,time) as g ") + .append(" ORDER BY thing_code, attr_key,time "); + } + if (!isAsc) { + sql.append(" DESC "); + } + } + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgService.java new file mode 100644 index 0000000..f1d8641 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgService.java @@ -0,0 +1,10 @@ +package com.thing.common.tskv.service.tskv; + +import com.mybatisflex.core.service.IService; +import com.thing.common.tskv.entity.TsKvPg; + + +public interface TsKvPgService extends IService, TsKvBaseService { + + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgServiceImpl.java new file mode 100644 index 0000000..96a7cfe --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/TsKvPgServiceImpl.java @@ -0,0 +1,424 @@ +package com.thing.common.tskv.service.tskv; + +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.Row; +import com.mybatisflex.core.row.RowUtil; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.DataBaseType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.event.EventPublisher; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.NativeSQLTool; +import com.thing.common.tskv.entity.TsKvMy; +import com.thing.common.tskv.entity.TsKvPg; +import com.thing.common.tskv.mapper.TsKvPgMapper; +import com.thing.common.tskv.service.DatabaseType; +import com.thing.common.tskv.service.TsKvNativeSQL; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.joda.time.DateTime; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.thing.common.tskv.NativeSQLTool.*; +import static com.thing.common.util.time.DaXiaUtils.getAmEndTime; +import static com.thing.common.util.time.DaXiaUtils.getAmStartTime; + + +@Slf4j +@Service +@UseDataSource("postgresql") +@ConditionalOnExpression("'${database.ts_kv.type}'=='timescale' || '${database.ts_kv.type}'=='postgresql'") +public class TsKvPgServiceImpl extends ServiceImpl implements TsKvPgService { + @Value("${calculate.am_total.offset:24}") + private int offset; + @Value("${calculate.am_total.interval:15}") + private int interval; + private final EventPublisher eventPublisher; + + public TsKvPgServiceImpl(EventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + @Override + public Integer saveDTOTsKv(List kvDTOList,String tableSuffix) { + return TsKvNativeSQL.saveDTOTsKv(kvDTOList, DatabaseType.PG,tableSuffix); + } + + @Override + public Integer saveProtoTsKv(List list) { + return TsKvNativeSQL.saveProtoTsKv(list, DatabaseType.PG); + } + + @Override + public void amSumProtos(List dataProtos) { + TsKvNativeSQL.amSumV1(dataProtos, offset, interval, DatabaseType.PG, eventPublisher); + } + + @Override + public void amSumDTOList(List tsKvDTOList) { + amSumProtos(TsKvDTO.toDataProtoList(tsKvDTOList)); + } + + /** + * 区间计算,用区间结束值减去开始值,用于数据重新计算或手动计算的API接口 + * + * @param maps 物编号对应属性集合 + * @param stTs 开始时间 + * @param enTs 结束属性 + */ + @Override + public void amSumMaps(Map> maps, long stTs, long enTs) { + DateTime st = getAmStartTime(stTs, this.interval); + DateTime en = getAmEndTime(enTs, this.interval); + maps.keySet().forEach(thingCode -> { + if (!maps.get(thingCode).isEmpty()) { + TsKvNativeSQL.amSum(thingCode, maps.get(thingCode), st.getMillis(), en.getMillis(), offset, DatabaseType.PG, eventPublisher); + } + }); + } + + /** + * 每小时/日/月/年的数据统计计算 + * + * @param protoList 数据对象(带有am的区间属性) + */ + @Override + public void hhDdMmYySum(List protoList) { + TsKvNativeSQL.hhDdSum(protoList, DatabaseType.PG, eventPublisher); + } + + private QueryWrapper getQueryWrapper(String code, Collection codeList, String attrKey, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = QueryWrapper.create(); + queryWrapper.eq(TsKvPg::getThingCode, code, StringUtils::isNotBlank) + .eq(TsKvPg::getAttrKey, attrKey, StringUtils::isNotBlank) + .in(TsKvPg::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvPg::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .between(TsKvPg::getTs, startTime, endTime, ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)); + queryWrapper.orderBy(TsKvPg::getTs, isAsc); + return queryWrapper; + } + + private QueryChain getTsKvPgQueryChain(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = queryChain(); + queryChain.between(TsKvPg::getTs, startTime, endTime); + queryChain.and(" ( 1!=1 "); + multiMap.forEach((code, attrList) -> queryChain.or(queryWrapper -> { + queryWrapper.eq(TsKvPg::getThingCode, code) + .in(TsKvPg::getAttrKey, attrList); + })); + queryChain.or(" 1!=1 ) "); + queryChain.orderBy(TsKvPg::getTs, isAsc); + return queryChain; + } + + @Override + public List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(TsKvMy::getThingCode, code).eq(TsKvMy::getAttrKey, attrKey) + .between(TsKvMy::getTs, startTime, endTime).orderBy(TsKvMy::getTs, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","Wrapper", agg), Collection.class, Collection.class, Long.class, Long.class, AggType.class, DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), Lists.newArrayList(attrKey), startTime, endTime, agg,DataBaseType.POSTGRES); + Row rows = Db.selectOneBySql(sql); + if(ObjectUtil.isEmpty(rows)){ + return new TsKvDTO(); + } + return rows.toEntity(TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("%s调用%s方法异常,请检查代码", DataBaseType.POSTGRES,AggType.getMethodName("get","Wrappers", agg)), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, attrKey, null, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, null, attrList, startTime, endTime, isAsc); + return mapper.selectListByQueryAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","Wrappers", agg), Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.POSTGRES); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","Wrappers", agg)), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(code, null, null, attrList, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","Wrappers", agg), Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.POSTGRES); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","Wrappers", agg)), e); + } + } + + @Override + public List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, startTime, endTime, isAsc); + return this.listAs(queryWrapper, TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","Wrappers", agg), Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.POSTGRES); + sql = sql + " ORDER BY ts "; + if (!isAsc) { + sql = sql + " DESC"; + } + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","Wrappers", agg)), e); + } + } + + @Override + public PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryWrapper queryWrapper = getQueryWrapper(null, codeList, null, attrList, startTime, endTime, isAsc); + Page paginate = this.pageAs(new Page<>(page, limit), queryWrapper, TsKvDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","Wrappers", agg), Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.POSTGRES); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","Wrappers", agg)), e); + } + } + + @Override + public List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + QueryChain queryChain = getTsKvPgQueryChain(multiMap, startTime, endTime, isAsc); + return this.listAs(queryChain.toQueryWrapper(), TsKvDTO.class); + } + + @Override + public List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","MapWrapper", agg), Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.POSTGRES); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","MapWrapper", agg)), e); + } + } + + @Override + public PageData findTsKvPageByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + QueryChain queryChain = getTsKvPgQueryChain(multiMap, startTime, endTime, isAsc); + Page tsKvDTOPage = this.pageAs(new Page<>(page, limit), queryChain.toQueryWrapper(), TsKvDTO.class); + return new PageData<>(tsKvDTOPage.getRecords(), tsKvDTOPage.getTotalRow()); + } + + @Override + public PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod(AggType.getMethodName("get","MapWrapper", agg), Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.POSTGRES); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s方法异常,请检查代码", AggType.getMethodName("get","MapWrapper", agg)), e); + } + } + + @Override + public List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(Lists.newArrayList(code), Lists.newArrayList(attr), null,startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + //校验时间范围:不能超过时间,属性是否匹配 +// TimeType.matchTimeRange(timeType, startTime, endTime, interval); +// ThingAttrType.matchAttr(attr, timeType); + StringBuilder sql = new StringBuilder(); + intervalSql(Lists.newArrayList(code), Lists.newArrayList(attr),null, startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(codeList, attrList, null,startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(); + intervalSql(codeList, attrList, null,startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(); + intervalSql(null, null,multiMap, startTime, endTime, agg, interval, timeType, isAsc, sql); + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(); + intervalSql(null, null,multiMap,startTime, endTime, agg, interval, timeType, isAsc, sql); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return getTsKvDTOPageIntervalData(page, limit, count, sql.toString()); + } + + @Override + public List findTsKvByCodesAndKeys(Collection codeList, Collection attrList, Collection timeList, Boolean isAsc) { + QueryWrapper queryWrapper = QueryWrapper.create(); + queryWrapper.in(TsKvMy::getAttrKey, attrList, CollectionUtils.isNotEmpty(attrList)) + .in(TsKvMy::getThingCode, codeList, CollectionUtils.isNotEmpty(codeList)) + .in(TsKvMy::getTs,timeList,CollectionUtils.isNotEmpty(timeList)); + queryWrapper.orderBy(TsKvMy::getTs, isAsc); + return mapper.selectListByQueryAs(queryWrapper,TsKvDTO.class); + } + + @Override + public Integer deleteTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteTskvSql(kvDTO); + return Db.deleteBySql(delSql); + } + + @Override + public Integer deleteLastTskv(TsKvDTO kvDTO) { + String delSql = NativeSQLTool.deleteLastTskvSql(kvDTO); + return Db.deleteBySql(delSql); + } + + @Override + public List findTsKvByCodesAndAttrsWithTableSuffix(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, String tableSuffix) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM thing_ts_kv"); + sql.append(tableSuffix); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + private void intervalSql(Collection codeList, Collection attrList, Map> multiMap,Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, StringBuilder sql) { + if (TimeType.YEAR.equals(timeType) || TimeType.MONTH.equals(timeType) ) { + sql.append(" WITH date_list AS ( ").append(" SELECT EXTRACT ( epoch FROM T ) * 1000 AS ts FROM ") + .append(" generate_series ( TO_TIMESTAMP( ").append(startTime).append(" / 1000 ), ") + .append(" TO_TIMESTAMP( ").append(endTime).append("/ 1000 ), INTERVAL '") + .append(interval).append(" ").append(timeType).append("' ) AS T )"); + sql.append(" SELECT thing_code,attr_key,date_list.ts,ROUND(").append(agg).append("(CAST(val AS DECIMAL)), 2) as val FROM thing_ts_kv as tskv ") + .append(" JOIN date_list ON "); + sql.append(" EXTRACT ( epoch FROM date_trunc( '").append(timeType).append("', ( to_timestamp( tskv.ts / 1000 ) AT TIME ZONE'UTC+0' ) AT TIME ZONE'UTC+8' ) ) * 1000 "); + sql.append("= date_list.ts GROUP BY thing_code,attr_key,date_list.ts ORDER BY date_list.ts "); + }else{ + sql.append(" SELECT thing_code, attr_key, time as ts,val from (") + .append(" SELECT thing_code, attr_key, ") + .append(" (FLOOR((ts - ").append(startTime).append(" ) / ( "); + + if (TimeType.DAY.equals(timeType)) { + sql.append(" 86400000 * ").append(interval).append(")) * 86400000 * ").append(interval).append(" + ").append(startTime); + } + if (TimeType.HOUR.equals(timeType)) { + sql.append(" 3600000 * ").append(interval).append(")) * 3600000 * ").append(interval).append(" + ").append(startTime); + } + if (TimeType.MINUTE.equals(timeType)) { + sql.append(" 60000 * ").append(interval).append(")) * 60000 * ").append(interval).append(" + ").append(startTime); + } + sql.append(" ) as time, ROUND(").append(agg).append("(CAST(val AS DECIMAL)), 2) AS val FROM ").append(TS_KV); + if (MapUtils.isNotEmpty(multiMap)) { + sql.append(" WHERE "); + spliceMapSql(multiMap, sql, startTime, endTime); + } else { + spliceCodesAndAttrsSql(codeList, attrList, sql, startTime, endTime); + } + sql.append(" GROUP BY thing_code, attr_key,time) as g ") + .append(" ORDER BY time "); + } +// else if(TimeType.MONTH.equals(timeType) ) { +// sql.append(" EXTRACT(epoch from date_trunc('month', to_timestamp(tskv.ts / 1000) at time zone 'UTC+0' at time zone 'UTC+8')) * 1000 "); +// } + +// else if(TimeType.WEEK.equals(timeType) || TimeType.DAY.equals(timeType) ) { +// sql.append(" date_part('epoch', date_trunc('").append(timeType).append("', to_timestamp(tskv.ts / 1000) at time zone 'UTC+0' at time zone 'UTC+8'))::bigint * 1000 "); +// } +// else if(TimeType.DAY.equals(timeType) ) { +// sql.append(" date_part('epoch', date_trunc('week', to_timestamp(tskv.ts / 1000) at time zone 'UTC+0' at time zone 'UTC+8'))::bigint * 1000 "); +// } +// else if(TimeType.HOUR.equals(timeType) ) { +// sql.append(" tskv.ts - (tskv.ts % 3600000) "); +// }else if(TimeType.MINUTE.equals(timeType) ) { +// sql.append(" tskv.ts - (tskv.ts % 60000) "); +// } + if (!isAsc) { + sql.append(" DESC "); + } + } + + + private PageData getTsKvDTOPageIntervalData(Integer page, Integer limit, long count, String sql) { + if (ObjectUtil.equals(count, 0L)) { + return new PageData<>( Lists.newArrayList(), count); + } + return new PageData<>(RowUtil.toEntityList(Db.selectListBySql(sql + " LIMIT " + limit + " OFFSET " + (page - 1) * limit), TsKvDTO.class), count); + } + +} + + + + diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkService.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkService.java new file mode 100644 index 0000000..2899a82 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkService.java @@ -0,0 +1,58 @@ +package com.thing.common.tskv.service.tskv; + +import com.mybatisflex.core.service.IService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.entity.ViewTsKv; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface ViewTsKvCkService extends IService { + + List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc); + + TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg); + + PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc); + + List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc,String tableSuffix); + + List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc); + + List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg); + + PageData findPageTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit); + + PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit); + + List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + PageData findPageTsKvIntervalAggByCodeAndAttr(String code,String attr,Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + List findTsKvIntervalAggByCodesAndAttrs(Collection codeList,Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList,Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + + List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc); + + PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit); + +} diff --git a/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkServiceImpl.java b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkServiceImpl.java new file mode 100644 index 0000000..788b6b7 --- /dev/null +++ b/common/tskv/src/main/java/com/thing/common/tskv/service/tskv/ViewTsKvCkServiceImpl.java @@ -0,0 +1,292 @@ +package com.thing.common.tskv.service.tskv; + +import com.google.common.collect.Lists; +import com.mybatisflex.annotation.UseDataSource; +import com.mybatisflex.core.row.Db; +import com.mybatisflex.core.row.Row; +import com.mybatisflex.core.row.RowUtil; +import com.mybatisflex.spring.service.impl.ServiceImpl; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.DataBaseType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.NativeSQLTool; +import com.thing.common.tskv.entity.ViewTsKv; +import com.thing.common.tskv.mapper.ViewTsKvCkMapper; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.stereotype.Service; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.thing.common.tskv.NativeSQLTool.TS_KV; + +@Slf4j +@Service +@UseDataSource("ck") +@ConditionalOnExpression("'${database.ts_kv.type}'=='clickhouse'") +public class ViewTsKvCkServiceImpl extends ServiceImpl implements ViewTsKvCkService { + + @Override + public List findTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc) { + StringBuilder sql = new StringBuilder("SELECT thing_code,attr_key,ts,val FROM view_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),Lists.newArrayList(attrKey),sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public TsKvDTO findTsKvAggByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrapper", Collection.class, Collection.class, Long.class, Long.class, AggType.class, DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), Lists.newArrayList(attrKey), startTime, endTime, agg,DataBaseType.CLICKHOUSE); + Row rows = Db.selectOneBySql(sql); + if(Objects.isNull(rows)){ + return null; + } + return rows.toEntity(TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("CLICKHOUSE调用%s%s%s方法异常,请检查代码", "get", agg, "Wrapper"), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttr(String code, String attrKey, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),Lists.newArrayList(attrKey),sql,startTime,endTime); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public List findTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),attrList,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class, DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("CLICKHOUSE调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public PageData findPageTsKvByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),attrList,sql,startTime,endTime); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodeAndAttrs(String code, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class, DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, Lists.newArrayList(code), attrList, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public List findTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc,String tableSuffix) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv"); + sql.append(tableSuffix); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public List findTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + sql = sql + " ORDER BY ts "; + if (!isAsc) { + sql = sql + " DESC"; + } + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public PageData findPageTsKvByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "Wrappers", Collection.class, Collection.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, codeList, attrList, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "Wrappers"), e); + } + } + + @Override + public List findTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM ").append(TS_KV).append(" where "); + NativeSQLTool.spliceMapSql(multiMap,sql,startTime,endTime); + sql.append(" ORDER BY ts "); + if(!isAsc){ + sql.append(" DESC "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()),TsKvDTO.class); + } + + @Override + public List findTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "MapWrapper", Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + return RowUtil.toEntityList(Db.selectListBySql(sql), TsKvDTO.class); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "MapWrapper"), e); + } + } + + @Override + public PageData findPageTsKvByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT thing_code,attr_key,ts,val FROM view_ts_kv where "); + NativeSQLTool.spliceMapSql(multiMap,sql,startTime,endTime); + String countSql = NativeSQLTool.countSql(sql.toString(), true); + long count = Db.selectCount(countSql); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public PageData findPageTsKvAggByMultiMap(Map> multiMap, Long startTime, Long endTime, Boolean isAsc, AggType agg, Integer page, Integer limit) { + try { + Method method = NativeSQLTool.class.getMethod("get" + agg + "MapWrapper", Map.class, Long.class, Long.class, AggType.class,DataBaseType.class); + String sql = (String) method.invoke(NativeSQLTool.class, multiMap, startTime, endTime, agg,DataBaseType.CLICKHOUSE); + long count = Db.selectCount(NativeSQLTool.countSql(sql, true)); + return NativeSQLTool.getTsKvDTOPageData(sql, count, isAsc, page, limit); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new SysException(String.format("调用%s%s%s方法异常,请检查代码", "get", agg, "MapWrapper"), e); + } + } + + @Override + public List findTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),Lists.newArrayList(attr),sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + sql.append(" order by ts "); + if(!isAsc){ + sql.append(" desc "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodeAndAttr(String code, String attr, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk "); + NativeSQLTool.spliceCodesAndAttrsSql(Lists.newArrayList(code),Lists.newArrayList(attr),sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public List findTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + sql.append(" order by ts "); + if(!isAsc){ + sql.append(" desc "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByCodesAndAttrs(Collection codeList, Collection attrList, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk "); + NativeSQLTool.spliceCodesAndAttrsSql(codeList,attrList,sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } + + @Override + public List findTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk where "); + NativeSQLTool.spliceMapSql(multiMap,sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + sql.append(" order by ts "); + if(!isAsc){ + sql.append(" desc "); + } + return RowUtil.toEntityList(Db.selectListBySql(sql.toString()), TsKvDTO.class); + } + + @Override + public PageData findPageTsKvIntervalAggByMultiMap(Map> multiMap, Long startTime, Long endTime, AggType agg, Integer interval, TimeType timeType, Boolean isAsc, Integer page, Integer limit) { + StringBuilder sql = new StringBuilder(" SELECT vtk.thing_code,vtk.attr_key,round(").append(agg).append("(toFloat32OrZero(vtk.val)),2) AS val,"); + // 聚合函数 + String result = TimeType.matchIsDaySql(timeType,interval); + sql.append(result); + sql.append(" from view_ts_kv as vtk where "); + NativeSQLTool.spliceMapSql(multiMap,sql,startTime,endTime); + sql.append(" group by vtk.thing_code,vtk.attr_key,ts "); + long count = Db.selectCount(NativeSQLTool.countSql(sql.toString(), true)); + return NativeSQLTool.getTsKvDTOPageData(sql.toString(), count, isAsc, page, limit); + } +} diff --git a/common/util/pom.xml b/common/util/pom.xml new file mode 100644 index 0000000..02ee393 --- /dev/null +++ b/common/util/pom.xml @@ -0,0 +1,79 @@ + + + 4.0.0 + + com.thing + common + 5.1 + + com.thing.common + util + jar + ThingBI Server Common Utils + + UTF-8 + ${basedir}/../.. + + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + com.google.guava + guava + provided + + + javax.annotation + javax.annotation-api + + + org.projectlombok + lombok + provided + + + jakarta.validation + jakarta.validation-api + 3.0.2 + + + + org.slf4j + slf4j-api + + + + com.google.code.gson + gson + + + com.googlecode.aviator + aviator + + + org.apache.commons + commons-lang3 + + + joda-time + joda-time + + + + + diff --git a/common/util/src/main/java/com/thing/common/util/AfterStartUp.java b/common/util/src/main/java/com/thing/common/util/AfterStartUp.java new file mode 100644 index 0000000..b059b30 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/AfterStartUp.java @@ -0,0 +1,51 @@ +/** + * Copyright © 2016-2023 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.thing.common.util; + + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.event.EventListener; +import org.springframework.core.annotation.AliasFor; +import org.springframework.core.annotation.Order; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 项目启动 事件侦听 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +@EventListener(ApplicationReadyEvent.class) +@Order +public @interface AfterStartUp { + + int QUEUE_INFO_INITIALIZATION = 1; + int DISCOVERY_SERVICE = 2; + + int STARTUP_SERVICE = 8; + int ACTOR_SYSTEM = 9; + int REGULAR_SERVICE = 10; + + int BEFORE_TRANSPORT_SERVICE = Integer.MAX_VALUE - 1001; + int TRANSPORT_SERVICE = Integer.MAX_VALUE - 1000; + int AFTER_TRANSPORT_SERVICE = Integer.MAX_VALUE - 999; + + @AliasFor(annotation = Order.class, attribute = "value") + int order(); +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/AbstractListeningExecutor.java b/common/util/src/main/java/com/thing/common/util/thread/AbstractListeningExecutor.java new file mode 100644 index 0000000..a9e1475 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/AbstractListeningExecutor.java @@ -0,0 +1,96 @@ +package com.thing.common.util.thread; + +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.common.util.concurrent.MoreExecutors; + +import jakarta.validation.constraints.NotNull; + +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.Callable; +import java.util.function.BiFunction; +import java.util.function.Function; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +/** 抽象监听执行器 */ +@Data +public abstract class AbstractListeningExecutor implements ListeningExecutor { + + private String taskName; + + private ListeningExecutorService service; + + @PostConstruct + public void init() { + this.service = + MoreExecutors.listeningDecorator( + ThingExecutors.newWorkStealingPool( + getThreadPollSize(), + Optional.ofNullable(getTaskName()) + .orElse(getClass().getSimpleName()))); + } + + @PreDestroy + public void destroy() { + if (this.service != null) { + this.service.shutdown(); + } + } + + @Override + public ListenableFuture executeAsync(Callable task) { + return service.submit(task); + } + + @Override + public List loopListToExecute(List list, Function> taskFunction) { + List>> futures = new ArrayList<>(); + List result = new ArrayList<>(); + list.forEach(e -> futures.add(executeAsync(() -> taskFunction.apply(e)))); + futures.forEach( + f -> { + try { + result.addAll(f.get()); + } catch (Exception ignore) { + } + }); + return result; + } + + @Override + public List loopMapToExecute(Map map, BiFunction> taskFunction) { + List>> futures = new ArrayList<>(); + List result = new ArrayList<>(); + map.forEach((k, v) -> futures.add(executeAsync(() -> taskFunction.apply(k, v)))); + futures.forEach( + f -> { + try { + result.addAll(f.get()); + } catch (Exception ignore) { + } + }); + return result; + } + + public ListenableFuture executeAsync(Runnable task) { + return service.submit(task); + } + + @Override + public void execute(@NotNull Runnable command) { + service.execute(command); + } + + public ListeningExecutorService executor() { + return service; + } + + protected abstract int getThreadPollSize(); +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/CallbackService.java b/common/util/src/main/java/com/thing/common/util/thread/CallbackService.java new file mode 100644 index 0000000..8fdc7d9 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/CallbackService.java @@ -0,0 +1,67 @@ +package com.thing.common.util.thread; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.google.common.util.concurrent.MoreExecutors; + +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** 携带回调的异步执行服务:Copy from thingsboard DonAsynchron */ +@SuppressWarnings("unused") +public class CallbackService { + + public static void withCallback( + ListenableFuture future, Consumer onSuccess, Consumer onFailure) { + withCallback(future, onSuccess, onFailure, null); + } + + public static void withCallback( + ListenableFuture future, + Consumer onSuccess, + Consumer onFailure, + Executor executor) { + FutureCallback callback = + new FutureCallback<>() { + @Override + public void onSuccess(T result) { + try { + onSuccess.accept(result); + } catch (Throwable th) { + onFailure(th); + } + } + + @Override + public void onFailure(Throwable t) { + onFailure.accept(t); + } + }; + Futures.addCallback( + future, + callback, + Objects.requireNonNullElseGet(executor, MoreExecutors::directExecutor)); + } + + public static ListenableFuture submit( + Callable task, + Consumer onSuccess, + Consumer onFailure, + Executor executor) { + return submit(task, onSuccess, onFailure, executor, null); + } + + public static ListenableFuture submit( + Callable task, + Consumer onSuccess, + Consumer onFailure, + Executor executor, + Executor callbackExecutor) { + ListenableFuture future = Futures.submit(task, executor); + withCallback(future, onSuccess, onFailure, callbackExecutor); + return future; + } +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/ForkJoinWorkerThreadFactory.java b/common/util/src/main/java/com/thing/common/util/thread/ForkJoinWorkerThreadFactory.java new file mode 100644 index 0000000..0223568 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/ForkJoinWorkerThreadFactory.java @@ -0,0 +1,27 @@ + +package com.thing.common.util.thread; + +import lombok.NonNull; +import lombok.ToString; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.atomic.AtomicLong; + +@ToString +public class ForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + private final String namePrefix; + private final AtomicLong threadNumber = new AtomicLong(1); + + public ForkJoinWorkerThreadFactory(@NonNull String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setContextClassLoader(this.getClass().getClassLoader()); + thread.setName(namePrefix +"-"+thread.getPoolIndex()+"-"+threadNumber.getAndIncrement()); + return thread; + } +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/ListeningExecutor.java b/common/util/src/main/java/com/thing/common/util/thread/ListeningExecutor.java new file mode 100644 index 0000000..2240349 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/ListeningExecutor.java @@ -0,0 +1,40 @@ +package com.thing.common.util.thread; + +import com.google.common.util.concurrent.ListenableFuture; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.Executor; +import java.util.function.BiFunction; +import java.util.function.Function; + +public interface ListeningExecutor extends Executor { + + ListenableFuture executeAsync(Callable task); + + /** + * 适用于每次执行 taskFunction 都会返回一个 list 的情形 + */ + List loopListToExecute(List list, Function> taskFunction); + + /** + * 适用于每次执行 taskFunction 都会返回一个 list 的情形 + */ + List loopMapToExecute(Map map, BiFunction> taskFunction); + + default ListenableFuture executeAsync(Runnable task) { + return executeAsync(() -> { + task.run(); + return null; + }); + } + + default ListenableFuture submit(Callable task) { + return executeAsync(task); + } + + default ListenableFuture submit(Runnable task) { + return executeAsync(task); + } +} \ No newline at end of file diff --git a/common/util/src/main/java/com/thing/common/util/thread/TBExecutors.java b/common/util/src/main/java/com/thing/common/util/thread/TBExecutors.java new file mode 100644 index 0000000..7534947 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/TBExecutors.java @@ -0,0 +1,37 @@ + +package com.thing.common.util.thread; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; + +public class TBExecutors { + + /** + *从ExecutorService分叉以提供线程轮询名称的方法 + * + * 创建一个线程池,保持足够的线程来支持 + * 给定的并行性级别,并且可能使用多个队列来 + * 减少争用。并行度级别对应于 + * 最大数量的线程积极参与,或可 + * 参与,任务处理。线程的实际数量可能 + * 动态增长和收缩。偷工作的人不会 + * 保证提交任务的顺序 + * 执行。 + * + * @param parallelism the targeted parallelism level + * @param namePrefix used to define thread name + * @return the newly created thread pool + * @throws IllegalArgumentException if {@code parallelism <= 0} + * @since 1.8 + */ + public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) { + return new ForkJoinPool(parallelism, + new ForkJoinWorkerThreadFactory(namePrefix), + null, true); + } + + public static ExecutorService newWorkStealingPool(int parallelism, Class clazz) { + return newWorkStealingPool(parallelism, clazz.getSimpleName()); + } + +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/ThingExecutors.java b/common/util/src/main/java/com/thing/common/util/thread/ThingExecutors.java new file mode 100644 index 0000000..9ca45c5 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/ThingExecutors.java @@ -0,0 +1,29 @@ +package com.thing.common.util.thread; + +import lombok.extern.slf4j.Slf4j; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ForkJoinPool; + +/** + * @author zhenghh. 2022-06-14 + */ +@Slf4j +public class ThingExecutors { + + public static ExecutorService newWorkStealingPool(int parallelism, String namePrefix) { + return new ForkJoinPool( + parallelism, + new ThingForkJoinWorkerThreadFactory(namePrefix), + defaultHandler(), + true); + } + + public static ExecutorService newWorkStealingPool(int parallelism, Class clazz) { + return newWorkStealingPool(parallelism, clazz.getSimpleName()); + } + + private static Thread.UncaughtExceptionHandler defaultHandler() { + return (t, e) -> log.warn("thread pool execute error on {}, message: ", t.getName(), e); + } +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/ThingForkJoinWorkerThreadFactory.java b/common/util/src/main/java/com/thing/common/util/thread/ThingForkJoinWorkerThreadFactory.java new file mode 100644 index 0000000..f453732 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/ThingForkJoinWorkerThreadFactory.java @@ -0,0 +1,29 @@ +package com.thing.common.util.thread; + +import lombok.NonNull; + +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinWorkerThread; +import java.util.concurrent.atomic.AtomicLong; + +/** + * @author zhenghh. 2022-06-14 + **/ +public class ThingForkJoinWorkerThreadFactory implements ForkJoinPool.ForkJoinWorkerThreadFactory { + + private final String namePrefix; + private final AtomicLong threadNumber = new AtomicLong(1L); + + public ThingForkJoinWorkerThreadFactory(@NonNull String namePrefix) { + this.namePrefix = namePrefix; + } + + @Override + public final ForkJoinWorkerThread newThread(ForkJoinPool pool) { + ForkJoinWorkerThread thread = ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool); + thread.setContextClassLoader(this.getClass().getClassLoader()); + String namePrefix = this.namePrefix; + thread.setName(namePrefix + "-" + thread.getPoolIndex() + "-" + this.threadNumber.getAndIncrement()); + return thread; + } +} diff --git a/common/util/src/main/java/com/thing/common/util/thread/ThingThreadFactory.java b/common/util/src/main/java/com/thing/common/util/thread/ThingThreadFactory.java new file mode 100644 index 0000000..3b653f2 --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/thread/ThingThreadFactory.java @@ -0,0 +1,37 @@ +package com.thing.common.util.thread; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author zhenghh. 2022-06-14 + **/ +public class ThingThreadFactory implements ThreadFactory { + private static final AtomicInteger POOL_NUMBER = new AtomicInteger(1); + private final ThreadGroup group; + private final AtomicInteger threadNumber = new AtomicInteger(1); + private final String namePrefix; + + public static ThingThreadFactory forName(String name) { + return new ThingThreadFactory(name); + } + + private ThingThreadFactory(String name) { + this.group = Thread.currentThread().getThreadGroup(); + this.namePrefix = name + "-" + POOL_NUMBER.getAndIncrement() + "-thread-"; + } + + @Override + public Thread newThread(Runnable runnable) { + Thread t = new Thread(this.group, runnable, this.namePrefix + this.threadNumber.getAndIncrement(), 0L); + if (t.isDaemon()) { + t.setDaemon(false); + } + + if (t.getPriority() != 5) { + t.setPriority(5); + } + + return t; + } +} diff --git a/common/util/src/main/java/com/thing/common/util/time/DaXiaUtils.java b/common/util/src/main/java/com/thing/common/util/time/DaXiaUtils.java new file mode 100644 index 0000000..605c80d --- /dev/null +++ b/common/util/src/main/java/com/thing/common/util/time/DaXiaUtils.java @@ -0,0 +1,179 @@ +package com.thing.common.util.time; + +import org.joda.time.DateTime; +import lombok.extern.slf4j.Slf4j; +import com.google.gson.JsonElement; +import com.googlecode.aviator.Expression; +import org.springframework.util.StringUtils; +import com.googlecode.aviator.AviatorEvaluator; +import org.apache.commons.lang3.math.NumberUtils; + +import java.util.Map; +import java.math.BigDecimal; + +@Slf4j +public class DaXiaUtils { + + public static final String METADATA_VARIABLE_TEMPLATE = "${%s}"; + public static final String TIME_PATTERN = "yyyy-MM-dd HH:mm:ss.SSS"; + + public static String processVars(String pattern, String key, Object[] array) { + String varPattern = String.format(METADATA_VARIABLE_TEMPLATE, key); + return pattern.replace(varPattern, StringUtils.arrayToCommaDelimitedString(array)); + } + + public static String processVar(String pattern, String key, String val) { + String varPattern = String.format(METADATA_VARIABLE_TEMPLATE, key); + return pattern.replace(varPattern, val); + } + + + /** + * Json 类型判断 + * + * @param json json对象 + * @return 返回对应类型 + */ + public static String jsonType(JsonElement json) { + //属性和值都不为 空 + if (json != null || !json.isJsonNull()) { + if (json.isJsonObject()) { + return "Json"; + } else if (json.isJsonArray()) { + return "Array"; + } else if (json.isJsonPrimitive()) { + if (json.getAsJsonPrimitive().isBoolean()) { + return "Boolean"; + } else if (json.getAsJsonPrimitive().isNumber()) { + return "Number"; + } else if (json.getAsJsonPrimitive().isString()) { + return "String"; + } + } + } + return null; + } + + /** + * 去掉数值后面无效的 0 + * + * @param value 数值 + * @return + */ + public static String removeZero(String value) { + if (NumberUtils.isCreatable(value)) { + if (value.indexOf(".") > 0) { + //正则表达 + value = value.replaceAll("0+?$", "");//去掉后面无用的零 + value = value.replaceAll("[.]$", "");//如小数点后面全是零则去掉小数点 + } + return value; + } else { + log.error("----非数值类型,转换失败------------>{}", value); + } + return BigDecimal.ZERO.toString(); + } + + /** + * 获取区间计算的开始时间 + * + * @param ts 消息时间戳 + * @param interval 消息统计间隔(单位:分) + * @return 时间对象 + */ + public static DateTime getAmStartTime(long ts, int interval) { + DateTime dateTime = new DateTime(ts); + int minute = dateTime.getMinuteOfHour(); + // 求商 + int j = (int) (minute / interval); + DateTime start = new DateTime(dateTime.getYear(), dateTime.getMonthOfYear(), dateTime.getDayOfMonth(), dateTime.getHourOfDay(), j * interval); + if (start.getMinuteOfHour() == dateTime.getMinuteOfHour()) { + return start.minusMinutes(interval); + } + return start; + } + + /** + * 获取区间计算的结束时间 + * + * @param ts 消息时间戳 + * @param interval 消息统计间隔(单位:分) + * @return 时间对象 + */ + public static DateTime getAmEndTime(long ts, int interval) { + DateTime dateTime = new DateTime(ts); + int minute = dateTime.getMinuteOfHour(); + // 求商 + int j = (int) (minute / interval); + DateTime end = new DateTime(dateTime.getYear(), dateTime.getMonthOfYear(), dateTime.getDayOfMonth(), dateTime.getHourOfDay(), j * interval); + return end.plusMinutes(interval); + } + + public static BigDecimal minus(Object stValue, Object enValue) { + if (!NumberUtils.isCreatable(stValue.toString()) || !NumberUtils.isCreatable(enValue.toString())) { + return BigDecimal.ZERO; + } + BigDecimal ns = new BigDecimal(stValue.toString()).stripTrailingZeros(); + BigDecimal ne = new BigDecimal(enValue.toString()).stripTrailingZeros(); + // 减数或被减数 小于等于0时,结果置零; + if (ns.compareTo(BigDecimal.ZERO) < 1 || ne.compareTo(BigDecimal.ZERO) < 1) { + return BigDecimal.ZERO; + } + BigDecimal vs = ne.subtract(ns).stripTrailingZeros(); + // 结果小于0时,结果置零; + if (vs.compareTo(BigDecimal.ZERO) < 0) { + return BigDecimal.ZERO; + } + return vs; + } + + /** + * 根据某个时间戳获取小时/天/月/年的开始统计时间 + * + * @param ts 13位 时间戳 + * @param type hh/dd/mm/yy + */ + public static DateTime getHhDdMmYyStartTs(Long ts, String type, Integer start_hh, Integer start_dd, Integer start_mm, Integer start_yy) { + DateTime time = new DateTime(ts); + // 如果消息时间的分小于开始时间的分,开始时间延1小时 + if (start_hh != null && time.getMinuteOfHour() < start_hh) { + time = time.minusHours(1); + } + // 如果消息时间的小时小于开始时间的小时,开始时间延1天 + if (start_dd != null && time.getHourOfDay() < start_dd) { + time = time.minusDays(1); + } + // 如果消息时间的天小于开始时间的天,开始时间延1个月 + if (start_mm != null && time.getDayOfMonth() < start_mm) { + time = time.minusMonths(1); + } + // 如果消息时间的月小于开始时间的月,开始时间延1年 + if (start_yy != null && time.getMonthOfYear() < start_yy) { + time = time.minusYears(1); + } + if ("hh".equals(type)) { + return new DateTime(time.getYear(), time.getMonthOfYear(), time.getDayOfMonth(), time.getHourOfDay(), start_hh == null ? 0 : start_hh); + } else if ("dd".equals(type)) { + return new DateTime(time.getYear(), time.getMonthOfYear(), time.getDayOfMonth(), start_dd == null ? 0 : start_dd, start_hh == null ? 0 : start_hh); + } else if ("mm".equals(type)) { + return new DateTime(time.getYear(), time.getMonthOfYear(), start_mm == null ? 1 : start_mm, start_dd == null ? 0 : start_dd, start_hh == null ? 0 : start_hh); + } else if ("yy".equals(type)) { + return new DateTime(time.getYear(), start_yy == null ? 1 : start_yy, start_mm == null ? 1 : start_mm, start_dd == null ? 0 : start_dd, start_hh == null ? 0 : start_hh); + } + return time; + } + + /** + * 删除最后的字符 + * + * @param str 原始字符 + * @param key 要删除的字符 + * @return + */ + public static String removeLastKey(String str, String key) { + int lastIndex = str.lastIndexOf(key); + return str.substring(0, lastIndex) + str.substring(lastIndex + key.length()); + } + + +} diff --git a/modules/alarm/pom.xml b/modules/alarm/pom.xml new file mode 100644 index 0000000..ebbd469 --- /dev/null +++ b/modules/alarm/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + alarm + jar + ThingBI Server Modules alarm + + UTF-8 + + + + com.thing.common + script + + + com.thing.modules + thing + + + com.thing.modules + msg + + + \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmConfigController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmConfigController.java new file mode 100644 index 0000000..5c29123 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmConfigController.java @@ -0,0 +1,127 @@ +package com.thing.alarm.alarm.controller; + +import cn.hutool.core.collection.CollectionUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmConfigDTO; +import com.thing.alarm.alarm.dto.AlarmConfigDeleteParam; +import com.thing.alarm.alarm.entity.AlarmConfigEntity; +import com.thing.alarm.alarm.service.AlarmConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.annotation.Resource; + +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** +* 尖峰谷平,夜间用能告警配置表 +* +* @author xc +* @since 3.0 2023-12-05 +*/ +@RestController +@RequestMapping("alarm/alarmconfig") +@Tag(name="尖峰谷平,夜间用能告警配置表") +public class AlarmConfigController { + + @Resource + private AlarmConfigService alarmConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description ="排序字段") , + @Parameter(name = Constant.ORDER, description ="排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = alarmConfigService.getPageData(params,AlarmConfigDTO.class); + + return new Result>().ok(page); + } + + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description ="排序字段") , + @Parameter(name = Constant.ORDER, description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "thingId", description ="物的id") , + @Parameter(name = "type", description ="1-峰谷指标告警配置 , 2-夜间用能告警配置") , + }) + public Result> list( @RequestParam Map params){ + List list = alarmConfigService.listAs(params,AlarmConfigDTO.class); + if(CollectionUtil.isNotEmpty(list)){ + for (AlarmConfigDTO alarmConfigDTO : list) { + alarmConfigService.getThing(alarmConfigDTO); + } + } + return new Result>().ok(list); + } + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + AlarmConfigDTO alarmConfigDTO = alarmConfigService.getByIdAs(id,AlarmConfigDTO.class); + alarmConfigService.getThing(alarmConfigDTO); + return new Result().ok(alarmConfigDTO); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody List dto){ + alarmConfigService.saveList(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody List dto){ + alarmConfigService.updateList(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除(峰谷)") + @LogOperation("删除(峰谷)") + public Result delete(@RequestBody Long[] ids){ + alarmConfigService + .getMapper() + .deleteByQuery( + QueryWrapper.create() + .in(AlarmConfigEntity::getThingId, List.of(ids)) + .eq(AlarmConfigEntity::getType, "1")); + return new Result<>(); + } + + @DeleteMapping("id") + @Operation(summary="删除(夜间)") + @LogOperation("删除(夜间)") + public Result deleteBatch(@RequestBody Long[] ids){ + alarmConfigService + .getMapper() + .deleteByQuery( + QueryWrapper.create() + .in(AlarmConfigEntity::getThingId, List.of(ids)) + .eq(AlarmConfigEntity::getType, "2")); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDictController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDictController.java new file mode 100644 index 0000000..f85fe5f --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDictController.java @@ -0,0 +1,117 @@ +package com.thing.alarm.alarm.controller; + +import com.thing.alarm.alarm.dto.AlarmDictDTO; +import com.thing.alarm.alarm.entity.AlarmDictType; +import com.thing.alarm.alarm.service.AlarmDictService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 告警字典管理模块 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-15 +*/ +@RestController +@RequestMapping("alarm/dict") +@Tag(name="告警字典管理模块") +public class AlarmDictController { + @Autowired + private AlarmDictService alarmDictService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description ="排序字段") , + @Parameter(name = Constant.ORDER, description ="排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = alarmDictService.getPageData(params,AlarmDictDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + AlarmDictDTO data = alarmDictService.getByIdAs(id,AlarmDictDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmDictDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmDictService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmDictDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + alarmDictService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + alarmDictService.deleteAlarmDict(ids); + + return new Result(); + } + + @GetMapping("tree") + @Operation(summary="获取告警字典树形列表") + @Parameters({ + @Parameter(name = "dictName", description ="字典名称") , + @Parameter(name = "dictType", description ="字典类型") + }) + public Result> treeAlarmDict( @RequestParam Map params){ + List list= alarmDictService.treeAlarmDict(params); + return new Result>().ok(list); + } + + @GetMapping("all") + @Operation(summary="获取所有字典数据") + public Result> all(){ + List list = alarmDictService.getAllList(); + + return new Result>().ok(list); + } + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDisposeLogController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDisposeLogController.java new file mode 100644 index 0000000..d892756 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmDisposeLogController.java @@ -0,0 +1,107 @@ +package com.thing.alarm.alarm.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.thing.alarm.alarm.dto.AlarmDisposeLogDTO; +import com.thing.alarm.alarm.service.AlarmDisposeLogService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.oss.cloud.OSSFactory; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 处理记录管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-17 + */ +@RestController +@RequestMapping("alarm/dispose/log") +@Tag(name = "处理记录管理模块") +public class AlarmDisposeLogController { + @Autowired + private AlarmDisposeLogService alarmDisposeLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description ="排序字段"), + @Parameter(name = Constant.ORDER, description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "alarmRuleLogId", description ="告警记录id") + }) + public Result> page( @RequestParam Map params) { + PageData page = alarmDisposeLogService.pageAlarmDisposeLog(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + AlarmDisposeLogDTO data = alarmDisposeLogService.getByIdAs(id,AlarmDisposeLogDTO.class); + List images = JSON.parseArray(data.getImages(), String.class); + if(CollectionUtil.isNotEmpty(images)) { + data.setImages(JSON.toJSONString(images.stream().map(OSSFactory::splice).collect(Collectors.toList()))); + } + return new Result().ok(data); + } + + @GetMapping("list/{id}") + @Operation(summary="列表") + public Result> listAlarmDisposeLog(@PathVariable("id") Long id) { + List data = alarmDisposeLogService.listAlarmDisposeLog(id); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmDisposeLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmDisposeLogService.addAlarmDisposeLog(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmDisposeLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + alarmDisposeLogService.updateAlarmDisposeLog(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + alarmDisposeLogService.batchDelete(ids); + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleActionController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleActionController.java new file mode 100644 index 0000000..e1b3d50 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleActionController.java @@ -0,0 +1,101 @@ +package com.thing.alarm.alarm.controller; + +import com.thing.alarm.alarm.dto.AlarmRuleActionDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionDetailDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionPageDTO; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + + +/** + * 告警动作关联表 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@RestController +@RequestMapping("alarm/rule/action") +@Tag(name = "告警动作关联表") +public class AlarmRuleActionController { + @Autowired + private AlarmRuleActionService alarmRuleActionService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description ="排序字段"), + @Parameter(name = Constant.ORDER, description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "ruleName", description ="告警名称"), + @Parameter(name = "pushName", description ="推送名称"), + @Parameter(name = "ruleType", description ="告警类型"), + @Parameter(name = "ruleLevel", description ="严重程度") + }) + public Result> page( @RequestParam Map params) { + PageData page = alarmRuleActionService.alarmRuleActionPage(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + AlarmRuleActionDetailDTO data = alarmRuleActionService.getAlarmRuleActionDetailDTO(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmRuleActionDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmRuleActionService.insertAlarmRuleAction(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmRuleActionDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + alarmRuleActionService.updateAlarmRuleActionDTO(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + alarmRuleActionService.deleteAlarmRuleActionDTO(ids); + + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleController.java new file mode 100644 index 0000000..7f62d5c --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleController.java @@ -0,0 +1,111 @@ +package com.thing.alarm.alarm.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.alarm.alarm.dto.AlarmRuleDTO; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + + +/** + * 告警规则 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@RestController +@RequestMapping("alarm/rule") +@Tag(name = "告警规则") +public class AlarmRuleController { + @Autowired + private AlarmRuleService alarmRuleService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "告警名称"), + @Parameter(name = "thingName", description = "告警对象"), + @Parameter(name = "type", description = "告警类型(字典 alarm_type)"), + @Parameter(name = "level", description = "严重程度(字典 alarm_level)") + }) + public Result> page( @RequestParam Map params) { + PageData page = alarmRuleService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + AlarmRuleDTO data = alarmRuleService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmRuleDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (CollectionUtil.isEmpty(dto.getEntityList())) { + throw new SysException("请选择告警物"); + } + dto.getEntityList().forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + + alarmRuleService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmRuleDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + if (CollectionUtil.isEmpty(dto.getEntityList())) { + throw new SysException("请选择告警物"); + } + dto.getEntityList().forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + alarmRuleService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + alarmRuleService.delete(ids); + + return new Result(); + } + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleEntityController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleEntityController.java new file mode 100644 index 0000000..b7fabbb --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleEntityController.java @@ -0,0 +1,95 @@ +package com.thing.alarm.alarm.controller; + +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + + +/** + * 告警实体 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@RestController +@RequestMapping("alarm/rule/entity") +@Tag(name = "告警实体") +public class AlarmRuleEntityController { + @Autowired + private AlarmRuleEntityService alarmRuleEntityService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params) { + PageData page = alarmRuleEntityService.getPageData(params,AlarmRuleEntityDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + AlarmRuleEntityDTO data = alarmRuleEntityService.getByIdAs(id,AlarmRuleEntityDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmRuleEntityDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmRuleEntityService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmRuleEntityDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + alarmRuleEntityService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + alarmRuleEntityService.batchDelete(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleLogController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleLogController.java new file mode 100644 index 0000000..49954b5 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleLogController.java @@ -0,0 +1,118 @@ +package com.thing.alarm.alarm.controller; + +import com.thing.alarm.alarm.dto.AlarmRuleLogDTO; +import com.thing.alarm.alarm.service.AlarmRuleLogService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +@RestController +@RequestMapping("alarm/rule/log") +@Tag(name = "告警记录") +public class AlarmRuleLogController { + @Autowired + private AlarmRuleLogService alarmRuleLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "status", description = "告警状态"), + @Parameter(name = "ruleLevel", description = "严重程度"), + @Parameter(name = "ruleName", description = "告警名称"), + @Parameter(name = "alarmType", description = "告警类型。 1: 通用告警,2: 发电告警"), + @Parameter(name = "plantIds", description = "光伏场站id列表"), + }) + public Result> page( @RequestParam Map params) { + PageData page = alarmRuleLogService.page(params); + return new Result>().ok(page); + } + + + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "status", description = "告警状态"), + @Parameter(name = "ruleLevel", description = "严重程度"), + @Parameter(name = "ruleName", description = "告警名称"), + @Parameter(name = "key", description = "物编码物名称") + }) + public Result> list( @RequestParam Map params) { + List list = alarmRuleLogService.listInfo(params); + + return new Result>().ok(list); + } + + + + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + AlarmRuleLogDTO data = alarmRuleLogService.getByIdAs(id,AlarmRuleLogDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody AlarmRuleLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmRuleLogService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody AlarmRuleLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + alarmRuleLogService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + alarmRuleLogService.deleteAlarmRuleLog(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleSettingController.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleSettingController.java new file mode 100644 index 0000000..a1d8a0b --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/controller/AlarmRuleSettingController.java @@ -0,0 +1,103 @@ +package com.thing.alarm.alarm.controller; + +import com.thing.alarm.alarm.dto.AlarmRuleSettingDTO; +import com.thing.alarm.alarm.dto.AlarmRuleSettingPageDTO; +import com.thing.alarm.alarm.service.AlarmRuleSettingService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + + +/** +* 告警设置 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-15 +*/ +@RestController +@RequestMapping("alarm/alarmrulesetting") +@Tag(name="告警设置") +public class AlarmRuleSettingController { + @Autowired + private AlarmRuleSettingService alarmRuleSettingService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "pushId", description = "推送设置主键") , + @Parameter(name = "receiver", description = "接收方") + }) + //@RequiresPermissions("alarm:alarmrulesetting:page") + public Result> page( @RequestParam Map params){ + PageData page = alarmRuleSettingService.getAlarmRuleSettingPage(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("alarm:alarmrulesetting:info") + public Result get(@PathVariable("id") Long id){ + AlarmRuleSettingDTO data = alarmRuleSettingService.getByIdAs(id,AlarmRuleSettingDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("alarm:alarmrulesetting:save") + public Result save(@RequestBody AlarmRuleSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + alarmRuleSettingService.insertAlarmRuleSetting(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + // @RequiresPermissions("alarm:alarmrulesetting:update") + public Result update(@RequestBody AlarmRuleSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + alarmRuleSettingService.updateAlarmRuleSetting(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + // @RequiresPermissions("alarm:alarmrulesetting:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + alarmRuleSettingService.deleteAlarmRuleSetting(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDTO.java new file mode 100644 index 0000000..cc006dc --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDTO.java @@ -0,0 +1,60 @@ +package com.thing.alarm.alarm.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 尖峰谷平,夜间用能告警配置表 +* +* @author xc +* @since 3.0 2023-12-05 +*/ +@Data +@Schema( name= "尖峰谷平,夜间用能告警配置表") +public class AlarmConfigDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "物实体id") + private Long thingId; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物名称") + private String thingName; + @Schema(description = "属性id") + private Long attrId; + @Schema(description = "属性编码") + private String attrCode; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "告警条件 > < >= <= =") + private String alarmRule; + @Schema(description = "告警值") + private String alarmVal; + @Schema(description = "优值条件 > < >= <= =") + private String fineRule; + @Schema(description = "优值值") + private String fineVal; + @Schema(description = "类型 1峰平谷尖 2夜间用能") + private String type; + @Schema(description = "开始时间") + private String beginTime; + @Schema(description = "结束时间") + private String endTime; + @Schema(description = "备注") + private String remark; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建人名称") + private String createName; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "1峰段用量2尖段用量3谷段用量4.平段用量") + private Long sort; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDeleteParam.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDeleteParam.java new file mode 100644 index 0000000..921f270 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmConfigDeleteParam.java @@ -0,0 +1,18 @@ +package com.thing.alarm.alarm.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/7/9 14:47 + * @description + */ +@Data +@Schema(description = "峰谷指标、夜间用能配置删除参数") +public class AlarmConfigDeleteParam { + private List thingIds; + private String type; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDictDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDictDTO.java new file mode 100644 index 0000000..dc7fe71 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDictDTO.java @@ -0,0 +1,45 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.core.utils.params.TreeNode; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 告警字典管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +@Data +@Schema( name= "告警字典管理模块") +public class AlarmDictDTO extends TreeNode implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "父级字典id") + private Long pid; + @Schema(description = "字典类型") + private String dictType; + @Schema(description = "字典名称") + private String dictName; + @Schema(description = "字典值") + private String dictValue; + @Schema(description = "排序") + private Integer sort; + @Schema(description = "备注") + private String remark; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDisposeLogDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDisposeLogDTO.java new file mode 100644 index 0000000..c47ff78 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmDisposeLogDTO.java @@ -0,0 +1,43 @@ +package com.thing.alarm.alarm.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 处理记录管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-17 + */ +@Data +@Schema( name= "处理记录管理模块") +public class AlarmDisposeLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "告警记录id") + private Long alarmLogId; + @Schema(description = "问题描述") + private String describe; + @Schema(description = "处理状态") + private String status; + @Schema(description = "图片列表") + private String images; + @Schema(description = "处理结果") + private String disposeResult; + @Schema(description = "处理人") + private String disposeUserName; + private Long creator; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long createDate; + private Long updater; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmJson.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmJson.java new file mode 100644 index 0000000..1077f15 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmJson.java @@ -0,0 +1,83 @@ +package com.thing.alarm.alarm.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author zhenghh. 2022-07-20 + **/ +@Data +public class AlarmJson { + + /** + * 规则主键 + */ + private String ruleId; + /** + * 规则名称 + */ + private String name; + /** + * 触发规则 + */ + private String condition; + /** + * 告警内容 + */ + private String content; + /** + * 是否重复告警 + */ + private Boolean repeat; + /** + * 处理状态 + */ + private String status; + /** + * 当前计算结果 + */ + private Boolean evaluate; + /** + * 告警物 + */ + private List list; + + @Data + public static class AlarmEntity { + + /** + * AA/AB + */ + private String label; + /** + * 物编码 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 物名称 + */ + private String thingName; + /** + * 物属性编码 + */ + private String thingAttr; + /** + * 物属性名称 + */ + private String thingAttrName; + /** + * 时间 + */ + private Long time; + /** + * 值 + */ + private BigDecimal value; + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmProcessingUserDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmProcessingUserDTO.java new file mode 100644 index 0000000..0c5d4e6 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmProcessingUserDTO.java @@ -0,0 +1,16 @@ +package com.thing.alarm.alarm.dto; + +import lombok.Data; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 16:47 2022/8/17 + */ +@Data +public class AlarmProcessingUserDTO { + + private Long id; + + private String realName; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCache.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCache.java new file mode 100644 index 0000000..a41c787 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCache.java @@ -0,0 +1,15 @@ +package com.thing.alarm.alarm.dto; + +import lombok.Data; + +/** + * @author zhenghh. 2022-08-31 + **/ +@Data +public class AlarmRuleActionCache { + + private Long ruleId; + private String actionType; + private Long actionId; + private String actionConfigure; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCacheDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCacheDTO.java new file mode 100644 index 0000000..26b5d80 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionCacheDTO.java @@ -0,0 +1,21 @@ +package com.thing.alarm.alarm.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +public class AlarmRuleActionCacheDTO implements Serializable { + + private static final long serialVersionUID = -9189414799401666675L; + /** + * 缓存键(id) + */ + private Long key; + + /** + * 规则主键 + */ + private List actionList; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDTO.java new file mode 100644 index 0000000..2c8a9f5 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDTO.java @@ -0,0 +1,45 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serializable; + +/** + * 告警动作关联表 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Data +@Schema( name= "告警动作关联表") +public class AlarmRuleActionDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "告警名称") + @NotNull(message = "告警名称不能为空!", groups = DefaultGroup.class) + private Long ruleId; + @Schema(description = "推送方式(消息、控制...)") + @NotBlank(message="推送方式不能为空", groups = DefaultGroup.class) + private String actionType; + @Schema(description = "推送名称(消息设置主键...)") + @NotNull(message = "推送名称不能为空!", groups = DefaultGroup.class) + private Long actionId; + @Schema(description = "动作额外配置") + private String actionConfigure; + @Schema(description = "推送设置id") + private Long settingId; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDetailDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDetailDTO.java new file mode 100644 index 0000000..7f1ed01 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionDetailDTO.java @@ -0,0 +1,40 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.orm.dto.BaseDTO; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class AlarmRuleActionDetailDTO extends BaseDTO { + + @Schema(description = "主键") + private Long id; + + @Schema(description = "告警id") + private Long ruleId; + + @Schema(description = "推送id") + private Long actionId; + + @Schema(description = "告警名称") + private String ruleName; + + @Schema(description = "告警类型") + private String ruleType; + + @Schema(description = "推送名称") + private String pushName; + + @Schema(description = "推送方式") + private String pushMethod; + + @Schema(description = "接收方") + private String receivers; + + @Schema(description = "模板名称") + private String templateName; + + @Schema(description = "告警内容") + private String ruleContent; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionPageDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionPageDTO.java new file mode 100644 index 0000000..772a7a8 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleActionPageDTO.java @@ -0,0 +1,44 @@ +package com.thing.alarm.alarm.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +public class AlarmRuleActionPageDTO extends BaseDTO { + + @Schema(description = "主键") + private Long id; + @Schema(description = "告警名称") + private String ruleName; + @Schema(description = "告警类型") + private String ruleType; + @Schema(description = "严重程度") + private String ruleLevel; + @Schema(description = "发送方") + private String sender; + @Schema(description = "接收方") + private String receivers; + @Schema(description = "推送名称") + private String pushName; + @Schema(description = "动作类型(消息、控制...)") + private String actionType; + @Schema(description = "告警内容") + private String ruleContent; + @Schema(description = "创建者") + private String creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updateDate; + + + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleDTO.java new file mode 100644 index 0000000..bff27a3 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleDTO.java @@ -0,0 +1,73 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 告警规则 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-19 +*/ +@Data +@Schema( name= "告警规则") +public class AlarmRuleDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "告警名称") + @NotBlank(message="告警名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema(description = "告警类型") + @NotBlank(message="告警类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "严重程度") + @NotBlank(message="严重程度不能为空", groups = DefaultGroup.class) + private String level; + @Schema(description = "是否可用") + private Boolean enable; + @Schema(description = "是否重复告警") + @NotNull(message="是否重复告警不能为空", groups = DefaultGroup.class) + private Boolean repeat; + @Schema(description = "触发规则") + @NotBlank(message="触发规则不能为空", groups = DefaultGroup.class) + private String condition; + @Schema(description = "规则中文") + @NotBlank(message="规则中文不能为空", groups = DefaultGroup.class) + private String conditionName; + @Schema(description = "告警物") + private String thingName; + @Schema(description = "告警内容") + private String content; + @Schema(description = "备注") + private String remark; + @Schema(description = "触发类型(job、socket)") + @NotBlank(message="触发类型不能为空", groups = DefaultGroup.class) + private String triggerType; + @Schema(description = "最新值") + private String elementInfo; + @Schema(description = "告警物列表") + @NotNull(message="告警物不能为空", groups = DefaultGroup.class) + private List entityList; + @Schema(description = "处理状态") + private String status; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建人") + private String createName; + @Schema(description = "创建时间") + private Long createDate; +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleEntityDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleEntityDTO.java new file mode 100644 index 0000000..ffd39a8 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleEntityDTO.java @@ -0,0 +1,53 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** +* 告警实体 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-19 +*/ +@Data +@Schema( name= "告警实体") +public class AlarmRuleEntityDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + + @Schema(description = "规则主键") + private Long ruleId; + + @Schema(description = "物实体主键") + @NotNull(message="物实体主键不能为空", groups = DefaultGroup.class) + private Long thingId; + + @Schema(description = "物编码") + @NotBlank(message="物编码不能为空", groups = DefaultGroup.class) + private String thingCode; + + @Schema(description = "设备ID") + private Long entityId; + + @Schema(description = "物名称") + private String thingName; + + @Schema(description = "物属性") + @NotBlank(message="物属性不能为空", groups = DefaultGroup.class) + private String thingAttr; + + @Schema(description = "属性名称") + private String thingAttrName; + + @Schema(description = "AA/AB/AC") + @NotBlank(message="标签不能为空", groups = DefaultGroup.class) + private String label; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleLogDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleLogDTO.java new file mode 100644 index 0000000..9107696 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleLogDTO.java @@ -0,0 +1,62 @@ +package com.thing.alarm.alarm.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +@Data +@Schema( name= "告警记录") +public class AlarmRuleLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "告警规则主键") + private Long ruleId; + @Schema(description = "推送设置id") + private Long actionId; + @Schema(description = "告警内容") + private String content; + @Schema(description = "告警状态") + private String status; + @Schema(description = "告警名称") + private String ruleName; + @Schema(description = "告警类型") + private String ruleType; + @Schema(description = "严重程度") + private String ruleLevel; + @Schema(description = "告警对象") + private String thingName; + @Schema(description = "告警时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date alarmTime; + private Long creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long createDate; + private Long updater; + private Long updateDate; + + @Schema(description = "处理人") + private Long disposeUserId; + private String disposeUserName; + @Schema(description = "处理完成时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date disposeTime; + @Schema(description = "持续时间") + private String existTime; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleReportDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleReportDTO.java new file mode 100644 index 0000000..21bb58c --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleReportDTO.java @@ -0,0 +1,46 @@ +package com.thing.alarm.alarm.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +@Data +@Schema( name= "告警记录") +public class AlarmRuleReportDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键(日志表)") + private Long id; + @Schema(description = "告警规则主键") + private Long ruleId; + @Schema(description = "告警实体主键") + private Long thingId; + @Schema(description = "告警实体名称") + private String thingCode; + @Schema(description = "属性编码(能源品种)") + private String thingAttr; + @Schema(description = "告警时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private String alarmAime; + @Schema(description = "告警状态") + private String status; + @Schema(description = "告警名称") + private String ruleName; + @Schema(description = "告警类型") + private String ruleType; + @Schema(description = "条件") + private String condition; + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingDTO.java new file mode 100644 index 0000000..4e2d77d --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingDTO.java @@ -0,0 +1,48 @@ +package com.thing.alarm.alarm.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + +/** +* 告警设置 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-15 +*/ +@Data +@Schema( name= "告警设置") +public class AlarmRuleSettingDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "告警选择类型") + @NotBlank(message = "告警选择类型不能为空", groups = DefaultGroup.class) + private String settingType; + @Schema(description = "告警id集合") + @NotBlank(message = "告警id集合不能为空", groups = DefaultGroup.class) + private String settingIds; + @Schema(description = "告警流向(消息、控制...)") + @NotBlank(message = "告警流向不能为空", groups = DefaultGroup.class) + private String actionType; + @Schema(description = "推送主键(消息设置主键...)") + @NotNull(message = "推送名称不能为空", groups = DefaultGroup.class) + private Long actionId; + @Schema(description = "所属租户") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + private Long creator; + private Long createDate; + private Long updater; + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingPageDTO.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingPageDTO.java new file mode 100644 index 0000000..9b78313 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/dto/AlarmRuleSettingPageDTO.java @@ -0,0 +1,36 @@ +package com.thing.alarm.alarm.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.dto.BaseDTO; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +@Data +public class AlarmRuleSettingPageDTO extends BaseDTO { + + @Schema(description = "主键") + private Long id; + + @Schema(description = "告警流向(消息、控制...)") + private String actionType; + + @Schema(description = "推送对象") + private String receivers; + + @Schema(description = "推送名称") + private String pushName; + + @Schema(description = "创建者") + private String creator; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmConfigEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmConfigEntity.java new file mode 100644 index 0000000..9cb82e8 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmConfigEntity.java @@ -0,0 +1,85 @@ +package com.thing.alarm.alarm.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; + +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 尖峰谷平,夜间用能告警配置表 + * + * @author xc + * @since 3.0 2023-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_config") +public class AlarmConfigEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 物实体id + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 属性id + */ + private Long attrId; + /** + * 属性编码 + */ + private String attrCode; + /** + * 告警条件 > < >= <= = + */ + private String alarmRule; + /** + * 告警值 + */ + private String alarmVal; + /** + * 优值条件 > < >= <= = + */ + private String fineRule; + /** + * 优值值 + */ + private String fineVal; + /** + * 类型 1峰平谷尖 2夜间用能 + */ + private String type; + /** + * 开始时间 + */ + private String beginTime; + /** + * 结束时间 + */ + private String endTime; + /** + * 备注 + */ + private String remark; + + /** + * 1峰段用量2尖段用量3谷段用量4.平段用量 + */ + private Long sort; + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictData.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictData.java new file mode 100644 index 0000000..f39f51a --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictData.java @@ -0,0 +1,21 @@ + + +package com.thing.alarm.alarm.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * 字典数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@AllArgsConstructor +public class AlarmDictData { + @JsonIgnore + private Long dictTypeId; + private String dictLabel; + private String dictValue; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictEntity.java new file mode 100644 index 0000000..99436b4 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictEntity.java @@ -0,0 +1,51 @@ +package com.thing.alarm.alarm.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 告警字典管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_dict") +public class AlarmDictEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 父级字典id + */ + private Long pid; + /** + * 字典类型 + */ + private String dictType; + /** + * 字典名称 + */ + private String dictName; + /** + * 字典值 + */ + private String dictValue; + /** + * 排序 + */ + private Integer sort; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictType.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictType.java new file mode 100644 index 0000000..cbe779e --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDictType.java @@ -0,0 +1,22 @@ + + +package com.thing.alarm.alarm.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class AlarmDictType { + @JsonIgnore + private Long id; + private String dictType; + private List dataList = new ArrayList<>(); +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDisposeLogEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDisposeLogEntity.java new file mode 100644 index 0000000..10ba31f --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmDisposeLogEntity.java @@ -0,0 +1,47 @@ +package com.thing.alarm.alarm.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 处理记录管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-17 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_dispose_log") +public class AlarmDisposeLogEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 告警记录 + */ + private Long alarmLogId; + /** + * 问题描述 + */ + private String describe; + /** + * 处理状态 + */ + private String status; + /** + * 图片列表 + */ + private String images; + /** + * 处理结果 + */ + private String disposeResult; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleActionEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleActionEntity.java new file mode 100644 index 0000000..74b6caf --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleActionEntity.java @@ -0,0 +1,46 @@ +package com.thing.alarm.alarm.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 告警动作关联表 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_rule_action") +public class AlarmRuleActionEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 规则主键 + */ + private Long ruleId; + /** + * 动作类型(消息、控制...) + */ + private String actionType; + /** + * 动作主键(消息设置主键...) + */ + private Long actionId; + /** + * 推送设置id + */ + private Long settingId; + /** + * 动作额外配置 + */ + private String actionConfigure; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntity.java new file mode 100644 index 0000000..3878812 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntity.java @@ -0,0 +1,75 @@ +package com.thing.alarm.alarm.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 告警规则 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_rule") +public class AlarmRuleEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 告警名称 + */ + private String name; + /** + * 告警类型 + */ + private String type; + /** + * 严重程度 + */ + private String level; + /** + * 是否可用 + */ + private Boolean enable; + /** + * 触发规则 + */ + private String condition; + /** + * 告警内容 + */ + private String content; + /** + * 备注 + */ + private String remark; + /** + * 触发类型(job、socket) + */ + private String triggerType; + /** + * 最新值 + */ + private String elementInfo; + /** + * 是否重复告警 + */ + private Boolean repeat; + /** + * 规则中文 + */ + private String conditionName; + /** + * 处理状态 + */ + private String status; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntityEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntityEntity.java new file mode 100644 index 0000000..6c81c8f --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleEntityEntity.java @@ -0,0 +1,48 @@ +package com.thing.alarm.alarm.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 告警实体 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_rule_entity") +public class AlarmRuleEntityEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 规则主键 + */ + private Long ruleId; + /** + * 物实体主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 设备ID + */ + private String entityId; + /** + * 物属性 + */ + private String thingAttr; + /** + * AA/AB/AC + */ + private String label; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleLogEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleLogEntity.java new file mode 100644 index 0000000..9e47f92 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleLogEntity.java @@ -0,0 +1,64 @@ +package com.thing.alarm.alarm.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_rule_log") +public class AlarmRuleLogEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 告警规则主键 + */ + private Long ruleId; + /** + * 告警内容 + */ + private String content; + /** + * 处理状态 + */ + private String status; + /** + * 推送设置id + */ + private Long actionId; + + /** + * 告警时间 + */ + private Date alarmTime; + + /** + * 处理人 + */ + private Long disposeUserId; + /** + * 处理完成时间 + */ + private Date disposeTime; + /** + * 告警类型 1通用告警,2发电告警 + */ + private String alarmType; + /** + * 告警物编码,发电告警校验回显用 + */ + private String alarmThingCode; + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleSettingEntity.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleSettingEntity.java new file mode 100644 index 0000000..2810baa --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/entity/AlarmRuleSettingEntity.java @@ -0,0 +1,39 @@ +package com.thing.alarm.alarm.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 告警设置 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("alarm_rule_setting") +public class AlarmRuleSettingEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 告警选择类型 + */ + private String settingType; + /** + * 告警id集合 + */ + private String settingIds; + /** + * 动作类型(消息、控制...) + */ + private String actionType; + /** + * 动作主键(消息设置主键...) + */ + private Long actionId; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmConfigMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmConfigMapper.java new file mode 100644 index 0000000..e2e5c0f --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.alarm.alarm.mapper; + +import com.thing.alarm.alarm.entity.AlarmConfigEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 尖峰谷平,夜间用能告警配置表 +* +* @author xc +* @since 3.0 2023-12-05 +*/ +@Mapper +public interface AlarmConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDictMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDictMapper.java new file mode 100644 index 0000000..2487f5d --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDictMapper.java @@ -0,0 +1,22 @@ +package com.thing.alarm.alarm.mapper; + + + +import com.thing.alarm.alarm.entity.AlarmDictEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 告警字典管理模块 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-15 +*/ +@Mapper +public interface AlarmDictMapper extends PowerBaseMapper { + + List findAllByDictType(String dictType); + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDisposeLogMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDisposeLogMapper.java new file mode 100644 index 0000000..98fa20b --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmDisposeLogMapper.java @@ -0,0 +1,17 @@ +package com.thing.alarm.alarm.mapper; + + +import com.thing.alarm.alarm.entity.AlarmDisposeLogEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 处理记录管理模块 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-17 +*/ +@Mapper +public interface AlarmDisposeLogMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleActionMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleActionMapper.java new file mode 100644 index 0000000..a79e3ba --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleActionMapper.java @@ -0,0 +1,27 @@ +package com.thing.alarm.alarm.mapper; + + +import com.thing.alarm.alarm.dto.AlarmRuleActionDetailDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionPageDTO; +import com.thing.alarm.alarm.entity.AlarmRuleActionEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 告警动作关联表 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-19 +*/ +@Mapper +public interface AlarmRuleActionMapper extends PowerBaseMapper { + + List getAlarmRuleActionList(Map params); + + AlarmRuleActionDetailDTO getAlarmRuleActionDetailDTO(Long id); + + String getConfigType(List pushIdList); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleEntityMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleEntityMapper.java new file mode 100644 index 0000000..20061fe --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleEntityMapper.java @@ -0,0 +1,27 @@ +package com.thing.alarm.alarm.mapper; + + +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.entity.AlarmRuleEntityEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 告警实体 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-19 +*/ +@Mapper +public interface AlarmRuleEntityMapper extends PowerBaseMapper { + + /** + * 物名称查询列表 + * @param params 查询 + * @return list + */ + List getThingNameList(Map params); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleLogMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleLogMapper.java new file mode 100644 index 0000000..b148990 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleLogMapper.java @@ -0,0 +1,38 @@ +package com.thing.alarm.alarm.mapper; + + +import com.thing.alarm.alarm.dto.AlarmProcessingUserDTO; +import com.thing.alarm.alarm.dto.AlarmRuleLogDTO; +import com.thing.alarm.alarm.dto.AlarmRuleReportDTO; +import com.thing.alarm.alarm.entity.AlarmRuleLogEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 告警记录 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-20 +*/ +@Mapper +public interface AlarmRuleLogMapper extends PowerBaseMapper { + List getIntervalPushTask(); + + long getCount(@Param("params") Map params); + + /** + * 分页查询 + * @param params 查询 + * @return list + */ + List getList(@Param("params") Map params); + + + List getProcessingUserList(List ids); + + List getAlarmReportList(Long alarmStTime, Long alarmEdTime, Long thingId, List thingAttrs); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleMapper.java new file mode 100644 index 0000000..b254ec8 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleMapper.java @@ -0,0 +1,17 @@ +package com.thing.alarm.alarm.mapper; + + +import com.thing.alarm.alarm.entity.AlarmRuleEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 告警规则 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-07-19 +*/ +@Mapper +public interface AlarmRuleMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleSettingMapper.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleSettingMapper.java new file mode 100644 index 0000000..039797b --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/mapper/AlarmRuleSettingMapper.java @@ -0,0 +1,24 @@ +package com.thing.alarm.alarm.mapper; + + + +import com.thing.alarm.alarm.dto.AlarmRuleSettingPageDTO; +import com.thing.alarm.alarm.entity.AlarmRuleSettingEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 告警设置 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-08-15 +*/ +@Mapper +public interface AlarmRuleSettingMapper extends PowerBaseMapper { + + List getAlarmRuleSettingPageList(Map params); + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmConfigService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmConfigService.java new file mode 100644 index 0000000..3ef5e42 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmConfigService.java @@ -0,0 +1,26 @@ +package com.thing.alarm.alarm.service; + +import com.thing.alarm.alarm.dto.AlarmConfigDTO; +import com.thing.alarm.alarm.entity.AlarmConfigEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; + +/** + * 尖峰谷平,夜间用能告警配置表 + * + * @author xc + * @since 3.0 2023-12-05 + */ +public interface AlarmConfigService extends IBaseService { + + + void saveList(List dto); + + void updateList(List dto); + + List findByThingId(Long thingId); + + void getThing(AlarmConfigDTO dto); + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDictService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDictService.java new file mode 100644 index 0000000..f2c14cb --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDictService.java @@ -0,0 +1,38 @@ +package com.thing.alarm.alarm.service; + +import com.thing.alarm.alarm.dto.AlarmDictDTO; +import com.thing.alarm.alarm.entity.AlarmDictEntity; +import com.thing.alarm.alarm.entity.AlarmDictType; +import com.thing.common.orm.service.IBaseService; + + +import java.util.List; +import java.util.Map; + + +/** + * 告警字典管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +public interface AlarmDictService extends IBaseService { + + /** + * 获取告警字典树形列表 + * @return + */ + List treeAlarmDict(Map params); + + /** + * 获取所有字典数据 + * @return + */ + List getAllList(); + + /** + * 删除字典数据及子集 + * @param ids + */ + void deleteAlarmDict(Long[] ids); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDisposeLogService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDisposeLogService.java new file mode 100644 index 0000000..0b3cd65 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmDisposeLogService.java @@ -0,0 +1,30 @@ +package com.thing.alarm.alarm.service; + + +import com.thing.alarm.alarm.dto.AlarmDisposeLogDTO; +import com.thing.alarm.alarm.entity.AlarmDisposeLogEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + + +/** + * 处理记录管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-17 + */ +public interface AlarmDisposeLogService extends IBaseService { + + List listAlarmDisposeLog(Long id); + + void addAlarmDisposeLog(AlarmDisposeLogDTO dto); + + void updateAlarmDisposeLog(AlarmDisposeLogDTO dto); + + void deleteAlarmDisposeLog(List alarmRuleLogIdList); + + PageData pageAlarmDisposeLog(Map params); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleActionService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleActionService.java new file mode 100644 index 0000000..518c438 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleActionService.java @@ -0,0 +1,54 @@ +package com.thing.alarm.alarm.service; + + +import com.thing.alarm.alarm.dto.AlarmRuleActionDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionDetailDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionPageDTO; +import com.thing.alarm.alarm.entity.AlarmRuleActionEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 告警动作关联表 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +public interface AlarmRuleActionService extends IBaseService { + + PageData alarmRuleActionPage(Map params); + + void insertAlarmRuleAction(AlarmRuleActionDTO alarmRuleActionDTO); + + void updateAlarmRuleActionDTO(AlarmRuleActionDTO alarmRuleActionDTO); + + void deleteAlarmRuleActionDTO(Long[] ids); + + AlarmRuleActionDetailDTO getAlarmRuleActionDetailDTO(Long id); + + /** + * 规则主键获取列表 + * @param ruleIds 规则主键集合 + * @return list + */ + List getListByRuleIds(List ruleIds); + + void insertAlarmRuleActionList(List alarmRuleActionDTOList); + + void updateAlarmRuleActionDTOList(List alarmRuleActionDTOList); + + void deleteAlarmRuleActionDTOList(List settingIdList); + + void operationActionByRule(Long ruleId, String type, String level); + + /** + * 设置保存列表 + * + * @param settingId 设置主键 + * @param ruleActionList 列表 + */ + void insertAlarmRuleAction(Long settingId, List ruleActionList); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleEntityService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleEntityService.java new file mode 100644 index 0000000..722e56a --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleEntityService.java @@ -0,0 +1,57 @@ +package com.thing.alarm.alarm.service; + +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.entity.AlarmRuleEntityEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 告警实体 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +public interface AlarmRuleEntityService extends IBaseService { + + /** + * 批量新增 + * + * @param ruleId 规则主键 + * @param entityList 告警物列表 + */ + void saveBatch(Long ruleId, List entityList); + + /** + * 删除 + * + * @param ruleIds 规则主键集合 + */ + void deleteByRuleIds(List ruleIds); + + /** + * 规则主键查询 + * + * @param ruleIds 规则主键集合 + * @return list + */ + List selectByRuleIds(List ruleIds); + + /** + * 列表 + * + * @param param 入参 + * @return list + */ + List getThingNameList(Map param); + + /** + * 列表 + * + * @param ruleIds 规则主键集合 + * @return list + */ + Map getThingNameMap(List ruleIds); + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleLogService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleLogService.java new file mode 100644 index 0000000..8d90adf --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleLogService.java @@ -0,0 +1,42 @@ +package com.thing.alarm.alarm.service; + +import com.thing.alarm.alarm.dto.AlarmRuleLogDTO; +import com.thing.alarm.alarm.entity.AlarmRuleLogEntity; +import com.thing.alarm.configuration.event.LogSaveActionEvent; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +public interface AlarmRuleLogService extends IBaseService { + + PageData page(Map params); + AlarmRuleLogDTO get(Long id); + List getIntervalPushTask(); + + /** + * 保存告警日志 + * @param actionEvent 事件 + */ + void save(LogSaveActionEvent actionEvent); + + /** + * 状态更新 + * @param id + * @param disposeUserId + * @param disposeTime + */ + Long updateStatus(Long id, String status , Long disposeUserId, long disposeTime); + + void deleteAlarmRuleLog(Long[] ids); + + List listInfo(Map params); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleService.java new file mode 100644 index 0000000..f396615 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleService.java @@ -0,0 +1,99 @@ +package com.thing.alarm.alarm.service; + + + +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleDTO; +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.entity.AlarmRuleEntity; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 告警规则 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +public interface AlarmRuleService extends IBaseService { + + /** + * 分页 + * + * @param params 查询条件 + * @return page + */ + PageData page(Map params); + + /** + * 详情 + * + * @param id 主键 + * @return dto + */ + AlarmRuleDTO get(Long id); + + /** + * 保存 + * + * @param dto 入参 + */ + void save(AlarmRuleDTO dto); + + /** + * 修改 + * + * @param dto 入参 + */ + void update(AlarmRuleDTO dto); + + /** + * 删除 + * + * @param ids 主键集合 + */ + void delete(Long[] ids); + + /** + * 获取所有启用规则 + * + * @param triggerType 触发方式 + * @return list + */ + List getEnableList(TriggerTypeEnum triggerType); + + /** + * 缓存 + * + * @param ids 主键集合 + * @return list + */ + List getAlarmJsonList(List ids); + + /** + * 主键查询 + * + * @param ids 主键集合 + * @return list + */ + List selectListByIds(List ids); + + /** + * 订阅物列表 + * + * @return list + */ + List getSubscribeList(); + + List getAlarmRuleDTO(List typeList, List levelList); + /** + * 处理完成 + * @param id 主键 + */ + void completed(Long id); + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleSettingService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleSettingService.java new file mode 100644 index 0000000..f7df079 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmRuleSettingService.java @@ -0,0 +1,31 @@ +package com.thing.alarm.alarm.service; + +import com.thing.alarm.alarm.dto.AlarmRuleSettingDTO; +import com.thing.alarm.alarm.dto.AlarmRuleSettingPageDTO; +import com.thing.alarm.alarm.entity.AlarmRuleSettingEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + + +import java.util.List; +import java.util.Map; + + +/** + * 告警设置 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +public interface AlarmRuleSettingService extends IBaseService { + + PageData getAlarmRuleSettingPage(Map params); + + void insertAlarmRuleSetting(AlarmRuleSettingDTO alarmRuleSettingDTO); + + void updateAlarmRuleSetting(AlarmRuleSettingDTO alarmRuleSettingDTO); + + void deleteAlarmRuleSetting(Long[] ids); + + List getAlarmRuleSettingDTOList(String type, String level); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmService.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmService.java new file mode 100644 index 0000000..ed094f6 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/AlarmService.java @@ -0,0 +1,27 @@ +package com.thing.alarm.alarm.service; + + +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleActionCache; + +import java.util.List; + +/** + * @author zhenghh. 2022-07-26 + **/ +public interface AlarmService { + + /** + * 计算 + * @param alarmJson 告警数据 + * @return boolean + */ + Boolean evaluate(AlarmJson alarmJson); + /** + * socket告警 + * + * @param alarmJsonList 告警信息 + * @param actionList 告警动作 + */ + void alarm(List alarmJsonList, List actionList); +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmConfigServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmConfigServiceImpl.java new file mode 100644 index 0000000..01c0cf7 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmConfigServiceImpl.java @@ -0,0 +1,148 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmConfigDTO; +import com.thing.alarm.alarm.entity.AlarmConfigEntity; +import com.thing.alarm.alarm.mapper.AlarmConfigMapper; +import com.thing.alarm.alarm.service.AlarmConfigService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.AlarmType; +import com.thing.common.core.enumeration.SignEnum; +import com.thing.common.core.enumeration.TemplateMark; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 尖峰谷平,夜间用能告警配置表 + * + * @author xc + * @since 3.0 2023-12-05 + */ +@Service +public class AlarmConfigServiceImpl extends BaseServiceImpl implements AlarmConfigService { + + @Resource + private ThingManageContextService thingManageContextService; + + @Resource + private SysUserMapper sysUserDao; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String code = (String) params.get("code"); + String type = (String) params.get("type"); + List thingIdStrList = MapUtil.get(params,"thingId",List.class); + List thingIds = + Objects.isNull(thingIdStrList) + ? null + : thingIdStrList.stream().map(Long::parseLong).collect(Collectors.toList()); + wrapper.in( AlarmConfigEntity::getThingId, thingIds,CollectionUtil.isNotEmpty(thingIds)) + .eq( AlarmConfigEntity::getType, type,StringUtils.isNotBlank(type)); + if(StringUtils.isNotBlank(code)){ + params.put("name",code); + params.remove("type"); + Optional> optionalList = thingManageContextService.findViewAllEntity(null, null, code, type, UserContext.getRealTenantCode(), + null, null, null, null, TemplateMark.NO.getValue()); + if(optionalList.isPresent()){ + wrapper.in( AlarmConfigEntity::getThingId, optionalList.get().stream().map( + s-> s.get(CacheNameEnum.EntityField.THING_ENTITY_ID.getField()).asLong() + ).toList()); + }else{ + wrapper.in( AlarmConfigEntity::getThingId, Lists.newArrayList(-1)); + } + } + return wrapper; + } + + @Override + public void saveList(List dto) { + validate(dto); + List alarmConfigEntities = ConvertUtils.sourceToTarget(dto, AlarmConfigEntity.class); + this.saveBatch(alarmConfigEntities); + } + + @Override + public void updateList(List dto) { + validate(dto); + List alarmConfigEntities = ConvertUtils.sourceToTarget(dto, AlarmConfigEntity.class); + + List collect_null = alarmConfigEntities.stream().filter(s -> ObjectUtil.isNull(s.getId())).collect(Collectors.toList()); + List collect_nonull = alarmConfigEntities.stream().filter(s -> !ObjectUtil.isNull(s.getId())).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(collect_null)){ + this.saveBatch(collect_null); + } + if(CollectionUtil.isNotEmpty(collect_nonull)){ + this.saveOrUpdateBatch(collect_nonull); + } + } + + @Override + public List findByThingId(Long thingId) { + List alarmConfigEntities = mapper.selectListByQuery(QueryWrapper.create().eq(AlarmConfigEntity::getThingId,thingId)); + return ConvertUtils.sourceToTarget(alarmConfigEntities,AlarmConfigDTO.class); + } + @Override + public void getThing(AlarmConfigDTO alarmConfigDTO){ + Optional> optional = thingManageContextService.findViewEntityAllByIds(Collections.singletonList(alarmConfigDTO.getThingId())); + if (optional.isEmpty() || CollectionUtils.isEmpty(optional.get())) { + return; + } + IotThingViewDTO thingView = optional.get().get(0); + if (Objects.isNull(thingView)) { + return; + } + alarmConfigDTO.setThingName(thingView.getEntityName()); + Optional dictRelationDTO = thingManageContextService.findDictRelationByEntityIdAndCode(thingView.getEntityId(), + alarmConfigDTO.getAttrCode()); + dictRelationDTO.ifPresent(iotThingDictRelationDTO -> alarmConfigDTO.setAttrName(iotThingDictRelationDTO.getName())); + if(Objects.nonNull(alarmConfigDTO.getCreator())){ + SysUserEntity sysUserEntity = sysUserDao.selectOneById(alarmConfigDTO.getCreator()); + alarmConfigDTO.setCreateName(sysUserEntity.getRealName()); + } + } + + private void validate(List configs) { + configs.forEach( + config -> { + //校验类型 + AlarmType.match(config.getType()); + // 优值条件或告警条件缺少一个则不需要校验 + if (StringUtils.isBlank(config.getFineRule()) + || StringUtils.isBlank(config.getAlarmRule())) { + return; + } + // 告警条件和优值条件合理性校验 + SignEnum alarmSign = SignEnum.match(config.getAlarmRule()); + SignEnum negationSign = alarmSign.getInverseSign(); + List subSigns = negationSign.getSubSignStr(); + if (!subSigns.contains(config.getFineRule())) { + throw new SysException( + "告警条件设置错误, " + config.getAlarmRule() + "对应的优值条件必须为:" + subSigns); + } + boolean valid = negationSign.compare(config.getFineVal(), config.getAlarmVal()); + if (!valid) { + throw new SysException("优值条件与告警条件不允许存在交集"); + } + }); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDictServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDictServiceImpl.java new file mode 100644 index 0000000..979d875 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDictServiceImpl.java @@ -0,0 +1,102 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmDictDTO; +import com.thing.alarm.alarm.entity.AlarmDictData; +import com.thing.alarm.alarm.entity.AlarmDictEntity; +import com.thing.alarm.alarm.entity.AlarmDictType; +import com.thing.alarm.alarm.mapper.AlarmDictMapper; +import com.thing.alarm.alarm.service.AlarmDictService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 告警字典管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +@Service +public class AlarmDictServiceImpl extends BaseServiceImpl implements AlarmDictService { + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + /** + * 获取告警字典树形列表 + * + * @return + */ + @Override + public List treeAlarmDict(Map params) { + String dictName = (String) params.get("dictName"); + String dictType = (String) params.get("dictType"); + List alarmDictEntities = mapper.selectListByQuery(QueryWrapper.create() + .like( AlarmDictEntity::getDictName, dictName,StringUtils.isNotBlank(dictName)) + .like(AlarmDictEntity::getDictType, dictType,StringUtils.isNotBlank(dictType)) + .orderBy(AlarmDictEntity::getSort,Boolean.FALSE)); + List alarmDictList = ConvertUtils.sourceToTarget(alarmDictEntities, AlarmDictDTO.class); + if (CollectionUtil.isEmpty(alarmDictList)) { + return alarmDictList; + } + List dictIdList = alarmDictList.stream().map(AlarmDictDTO::getId).collect(Collectors.toList()); + List entityList = mapper.selectListByQuery(QueryWrapper.create() + .in(AlarmDictEntity::getPid, dictIdList).notIn(AlarmDictEntity::getId, dictIdList).orderBy(AlarmDictEntity::getSort,Boolean.FALSE)); + if (CollectionUtil.isNotEmpty(entityList)) { + alarmDictList.addAll(ConvertUtils.sourceToTarget(entityList, AlarmDictDTO.class)); + } + return TreeUtils.build(alarmDictList, Constant.ALARM_DICT_TOOT); + } + + /** + * 获取所有字典数据 + * + * @return + */ + @Override + public List getAllList() { + List alarmDictList = mapper.selectListByQuery(QueryWrapper.create()); + return alarmDictList.parallelStream() + .filter(item -> Objects.equals(Constant.ALARM_DICT_TOOT, item.getPid())) + .map(item -> { + AlarmDictType type = new AlarmDictType(); + type.setId(item.getId()); + type.setDictType(item.getDictType()); + type.setDataList(alarmDictList.parallelStream() + .filter(alarm -> Objects.equals(item.getId(), alarm.getPid())) + .map(alarm -> new AlarmDictData(alarm.getPid(), alarm.getDictName(), alarm.getDictValue())) + .collect(Collectors.toList())); + return type; + }).collect(Collectors.toList()); + } + + /** + * 删除字典数据及子集 + * + * @param ids + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteAlarmDict(Long[] ids) { + Long selectCount = mapper.selectCountByQuery(QueryWrapper.create().in(AlarmDictEntity::getPid, Arrays.asList(ids))); + if(selectCount > 0) { + throw new SysException("请先删除子集"); + } + this.batchDelete(ids); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDisposeLogServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDisposeLogServiceImpl.java new file mode 100644 index 0000000..187e4aa --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmDisposeLogServiceImpl.java @@ -0,0 +1,143 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmDisposeLogDTO; +import com.thing.alarm.alarm.dto.AlarmProcessingUserDTO; +import com.thing.alarm.alarm.entity.AlarmDisposeLogEntity; +import com.thing.alarm.alarm.mapper.AlarmDisposeLogMapper; +import com.thing.alarm.alarm.mapper.AlarmRuleLogMapper; +import com.thing.alarm.alarm.service.AlarmDisposeLogService; +import com.thing.alarm.alarm.service.AlarmRuleLogService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.common.core.enumeration.AlarmStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.oss.cloud.OSSFactory; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 处理记录管理模块 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-17 + */ +@Service +public class AlarmDisposeLogServiceImpl extends BaseServiceImpl implements AlarmDisposeLogService { + + @Resource private AlarmRuleService alarmRuleService; + @Resource private AlarmRuleLogMapper alarmRuleLogDao; + @Resource @Lazy private AlarmRuleLogService alarmRuleLogService; + + @Override + public QueryWrapper getWrapper(Map params) { + Long alarmRuleLogId = StringUtils.isBlank((String) params.get("alarmRuleLogId")) ? null : Long.parseLong(String.valueOf(params.get("alarmRuleLogId"))); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( AlarmDisposeLogEntity::getAlarmLogId, alarmRuleLogId,!Objects.isNull(alarmRuleLogId)); + return wrapper; + } + + + @Override + public List listAlarmDisposeLog(Long id) { + List alarmDisposeLogEntities = mapper.selectListByQuery(QueryWrapper.create() + .eq(AlarmDisposeLogEntity::getAlarmLogId, id)); + List alarmDisposeLogList = ConvertUtils.sourceToTarget(alarmDisposeLogEntities, AlarmDisposeLogDTO.class); + buildAlarmDisposeLogDTO(alarmDisposeLogList); + return alarmDisposeLogList; + } + + @Override + @Transactional(rollbackFor = SysException.class) + public void addAlarmDisposeLog(AlarmDisposeLogDTO dto) { + convertUrl(dto); + saveDto(dto); + Long ruleId = alarmRuleLogService.updateStatus(dto.getAlarmLogId(), dto.getStatus(), dto.getCreator(), dto.getCreateDate()); + // 处理完成更新状态 + if (StringUtils.equals(dto.getStatus(), AlarmStatus.PROCESSED.getStatus())) { + alarmRuleService.completed(ruleId); + } + } + + @Override + @Transactional(rollbackFor = SysException.class) + public void updateAlarmDisposeLog(AlarmDisposeLogDTO dto) { + convertUrl(dto); + updateDto(dto); + Long ruleId = alarmRuleLogService.updateStatus(dto.getAlarmLogId(), dto.getStatus(), dto.getUpdater(), dto.getUpdateDate()); + // 处理完成更新状态 + if (StringUtils.equals(dto.getStatus(), AlarmStatus.PROCESSED.getStatus())) { + alarmRuleService.completed(ruleId); + } + } + + @Override + public void deleteAlarmDisposeLog(List alarmRuleLogIdList) { + List alarmDisposeLogEntities = mapper.selectListByQuery(QueryWrapper.create() + .in(AlarmDisposeLogEntity::getAlarmLogId, alarmRuleLogIdList)); + if (CollectionUtil.isEmpty(alarmDisposeLogEntities)) { + return; + } + removeByIds(alarmDisposeLogEntities); + } + + @Override + public PageData pageAlarmDisposeLog(Map params) { + PageData page =getPageData(params,AlarmDisposeLogDTO.class); + if (Objects.isNull(page)) { + return null; + } + List list = page.getList(); + buildAlarmDisposeLogDTO(list); + return page; + } + + + /** + * 构建返回值相关参数 + * + * @param list + */ + private void buildAlarmDisposeLogDTO(List list) { + if (CollectionUtil.isEmpty(list)) { + return; + } + List disposeIds = list.stream().map(AlarmDisposeLogDTO::getCreator).collect(Collectors.toList()); + List processingUserList = null; + if (CollectionUtil.isNotEmpty(disposeIds)) { + processingUserList = alarmRuleLogDao.getProcessingUserList(disposeIds); + } + if (CollectionUtil.isEmpty(processingUserList)) { + return; + } + for (AlarmDisposeLogDTO alarmDisposeLogDTO : list) { + AlarmProcessingUserDTO alarmProcessingUserDTO = processingUserList.stream().filter(item -> Objects.equals(item.getId(), alarmDisposeLogDTO.getCreator())).findFirst().orElseGet(null); + if (!Objects.isNull(alarmProcessingUserDTO)) { + alarmDisposeLogDTO.setDisposeUserName(alarmProcessingUserDTO.getRealName()); + } + } + } + + /** + * 截取地址 + * @param dto dto + */ + private void convertUrl(AlarmDisposeLogDTO dto) { + List images = JSON.parseArray(dto.getImages(), String.class); + if(CollectionUtil.isNotEmpty(images)) { + dto.setImages(JSON.toJSONString(images.stream().map(OSSFactory::cutOut).collect(Collectors.toList()))); + } + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleActionServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleActionServiceImpl.java new file mode 100644 index 0000000..2243909 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleActionServiceImpl.java @@ -0,0 +1,198 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmRuleActionDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionDetailDTO; +import com.thing.alarm.alarm.dto.AlarmRuleActionPageDTO; +import com.thing.alarm.alarm.dto.AlarmRuleSettingDTO; +import com.thing.alarm.alarm.entity.AlarmRuleActionEntity; +import com.thing.alarm.alarm.mapper.AlarmRuleActionMapper; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleSettingService; +import com.thing.common.core.enumeration.ActionType; +import com.thing.common.core.event.CacheAlarmActionEvent; +import com.thing.common.core.event.CacheAlarmEvent; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 告警动作关联表 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Service +public class AlarmRuleActionServiceImpl extends BaseServiceImpl implements AlarmRuleActionService { + + @Resource private ApplicationEventPublisher applicationEventPublisher; + @Resource @Lazy private AlarmRuleSettingService alarmRuleSettingService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public PageData alarmRuleActionPage(Map params) { + paramsToLike(params, "ruleName", "pushName"); + PageData iPage = getPageData(params); + List list = mapper.getAlarmRuleActionList(params); + return getPageData(list, iPage.getTotal(), AlarmRuleActionPageDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void insertAlarmRuleAction(AlarmRuleActionDTO alarmRuleActionDTO) { + AlarmRuleActionEntity alarmRuleActionEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(AlarmRuleActionEntity::getActionId, alarmRuleActionDTO.getActionId()) + .eq(AlarmRuleActionEntity::getRuleId, alarmRuleActionDTO.getRuleId()).limit(1)); + if (ObjectUtil.isNotNull(alarmRuleActionEntity)) throw new SysException("该告警推送已存在!"); + saveDto(alarmRuleActionDTO); + applicationEventPublisher.publishEvent(new CacheAlarmActionEvent(this, CollectionUtil.newArrayList(alarmRuleActionDTO.getRuleId()))); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAlarmRuleActionDTO(AlarmRuleActionDTO alarmRuleActionDTO) { + AlarmRuleActionEntity alarmRuleActionEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(AlarmRuleActionEntity::getActionId, alarmRuleActionDTO.getActionId()) + .eq(AlarmRuleActionEntity::getRuleId, alarmRuleActionDTO.getRuleId()).ne(AlarmRuleActionEntity::getId, alarmRuleActionDTO.getId()).limit(1)); + if (ObjectUtil.isNotNull(alarmRuleActionEntity)) throw new SysException("该告警推送已存在!"); + updateDto(alarmRuleActionDTO); + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, CollectionUtil.newArrayList(alarmRuleActionDTO.getRuleId()))); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAlarmRuleActionDTO(Long[] ids) { + List actionList = mapper.selectListByIds(Arrays.asList(ids)); + removeByIds(Arrays.asList(ids)); + List ruleIds = actionList.stream().map(AlarmRuleActionEntity::getRuleId).distinct().collect(Collectors.toList()); + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, ruleIds)); + } + + @Override + public AlarmRuleActionDetailDTO getAlarmRuleActionDetailDTO(Long id) { + AlarmRuleActionDTO alarmRuleActionDTO = getByIdAs(id,AlarmRuleActionDTO.class); + AlarmRuleActionDetailDTO alarmRuleActionDetailDTO = new AlarmRuleActionDetailDTO(); + if (ObjectUtil.isNull(alarmRuleActionDTO)) return alarmRuleActionDetailDTO; + if (CompareUtil.compare(alarmRuleActionDTO.getActionType(), ActionType.MSG.getValue()) == 0) { + alarmRuleActionDetailDTO = mapper.getAlarmRuleActionDetailDTO(id); + } + if (StringUtils.isNotBlank(alarmRuleActionDetailDTO.getPushMethod())) { + List pushIdList = Lists.newArrayList(alarmRuleActionDetailDTO.getPushMethod().split(",")).stream().map(Long::parseLong).collect(Collectors.toList()); + String pushMethod = mapper.getConfigType(pushIdList); + alarmRuleActionDetailDTO.setPushMethod(pushMethod); + } + return alarmRuleActionDetailDTO; + } + + /** + * 规则主键获取列表 + * + * @param ruleIds 规则主键集合 + * @return list + */ + @Override + public List getListByRuleIds(List ruleIds) { + if(CollectionUtil.isEmpty(ruleIds)) { + return new ArrayList<>(); + } + List alarmRuleActionList = mapper.selectListByQuery(QueryWrapper.create().in(AlarmRuleActionEntity::getRuleId, ruleIds)); + return ConvertUtils.sourceToTarget(alarmRuleActionList, AlarmRuleActionDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void insertAlarmRuleActionList(List alarmRuleActionDTOList) { + List alarmRuleActionEntityList = operationAlarmRuleAction(alarmRuleActionDTOList); + if (CollectionUtil.isNotEmpty(alarmRuleActionEntityList)){ + alarmRuleActionDTOList = alarmRuleActionDTOList.stream().filter(item -> alarmRuleActionEntityList.stream().noneMatch(alarmRuleActionEntity -> + CompareUtil.compare(alarmRuleActionEntity.getActionId(), item.getActionId()) == 0 && CompareUtil.compare(alarmRuleActionEntity.getRuleId(), item.getRuleId()) == 0)).collect(Collectors.toList()); + } + saveBatch(ConvertUtils.sourceToTarget(alarmRuleActionDTOList, AlarmRuleActionEntity.class)); + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, alarmRuleActionDTOList.stream().map(AlarmRuleActionDTO::getRuleId).distinct().collect(Collectors.toList()))); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAlarmRuleActionDTOList(List alarmRuleActionDTOList) { + deleteAlarmRuleActionDTOList(alarmRuleActionDTOList.stream().map(AlarmRuleActionDTO::getSettingId).collect(Collectors.toList())); + insertAlarmRuleActionList(alarmRuleActionDTOList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAlarmRuleActionDTOList(List settingIdList){ + List ruleActionList = mapper.selectListByQuery(QueryWrapper.create().in(AlarmRuleActionEntity::getSettingId, settingIdList)); + if(CollectionUtil.isEmpty(ruleActionList)) { + return; + } + mapper.deleteBatchByIds(ruleActionList.stream().map(AlarmRuleActionEntity::getId).collect(Collectors.toList())); + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, ruleActionList.stream().map(AlarmRuleActionEntity::getRuleId).distinct().collect(Collectors.toList()))); + } + + private List operationAlarmRuleAction(List alarmRuleActionDTOList){ + QueryWrapper queryWrapper =new QueryWrapper(); + QueryColumn action_id = column("action_id"); + QueryColumn rule_id = column("rule_id"); + alarmRuleActionDTOList.forEach(item -> { + queryWrapper.or(action_id.eq(item.getActionId()).and(rule_id.eq(item.getRuleId()))); + }); + return mapper.selectListByQuery(queryWrapper); + } + + @Override + public void operationActionByRule(Long ruleId, String type, String level){ + mapper.deleteByQuery(QueryWrapper.create().eq(AlarmRuleActionEntity::getRuleId, ruleId)); + List alarmRuleSettingDTOList = alarmRuleSettingService.getAlarmRuleSettingDTOList(type, level); + List alarmRuleActionDTOList = new ArrayList<>(); + alarmRuleSettingDTOList.forEach(item -> { + AlarmRuleActionDTO alarmRuleActionDTO = ConvertUtils.sourceToTarget(item, AlarmRuleActionDTO.class); + alarmRuleActionDTO.setId(null); + alarmRuleActionDTO.setRuleId(ruleId); + alarmRuleActionDTO.setSettingId(item.getId()); + alarmRuleActionDTOList.add(alarmRuleActionDTO); + }); + insertAlarmRuleActionList(alarmRuleActionDTOList); + } + + /** + * 设置保存列表 + * + * @param settingId 设置主键 + * @param ruleActionList 列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void insertAlarmRuleAction(Long settingId, List ruleActionList) { + List actionEntityList = mapper.selectListByQuery(QueryWrapper.create().eq(AlarmRuleActionEntity::getSettingId, settingId)); + if(CollectionUtil.isNotEmpty(ruleActionList)) { + saveBatch(ConvertUtils.sourceToTarget(ruleActionList, AlarmRuleActionEntity.class)); + } + if(CollectionUtil.isNotEmpty(actionEntityList)) { + mapper.deleteBatchByIds(actionEntityList.stream().map(AlarmRuleActionEntity::getId).collect(Collectors.toList())); + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, actionEntityList.stream().map(AlarmRuleActionEntity::getRuleId).distinct().collect(Collectors.toList()))); + } + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleEntityServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleEntityServiceImpl.java new file mode 100644 index 0000000..ccaed93 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleEntityServiceImpl.java @@ -0,0 +1,115 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.entity.AlarmRuleEntityEntity; +import com.thing.alarm.alarm.mapper.AlarmRuleEntityMapper; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 告警实体 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Service +public class AlarmRuleEntityServiceImpl extends BaseServiceImpl implements AlarmRuleEntityService { + @Override + public QueryWrapper getWrapper(Map params) { + String ruleId = (String) params.get("ruleId"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(AlarmRuleEntityEntity::getRuleId, StringUtils.isBlank(ruleId) ? -1L : Long.parseLong(ruleId)); + return wrapper; + } + + /** + * 批量新增 + * + * @param ruleId 规则主键 + * @param entityList 告警物列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveBatch(Long ruleId, List entityList) { + mapper.deleteByQuery(QueryWrapper.create().eq(AlarmRuleEntityEntity::getRuleId, ruleId)); + mapper.insertBatch(entityList.parallelStream().map(item -> { + AlarmRuleEntityEntity entity = ConvertUtils.sourceToTarget(item, AlarmRuleEntityEntity.class); + entity.setRuleId(ruleId); + return entity; + }).collect(Collectors.toList())); + } + + /** + * 删除 + * + * @param ruleIds 规则主键集合 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByRuleIds(List ruleIds) { + mapper.deleteByQuery(QueryWrapper.create().in(AlarmRuleEntityEntity::getRuleId, ruleIds)); + } + + /** + * 规则主键查询 + * + * @param ruleIds 规则主键集合 + * @return list + */ + @Override + public List selectByRuleIds(List ruleIds) { + if (CollectionUtil.isEmpty(ruleIds)) { + return new ArrayList<>(); + } + List entities = mapper.selectListByQuery(QueryWrapper.create().in(AlarmRuleEntityEntity::getRuleId, ruleIds)); + return ConvertUtils.sourceToTarget(entities, AlarmRuleEntityDTO.class); + } + + /** + * 列表 + * + * @param param 入参 + * @return list + */ + @Override + public List getThingNameList(Map param) { + return mapper.getThingNameList(param); + } + + /** + * 列表 + * + * @param ruleIds 规则主键集合 + * @return list + */ + @Override + public Map getThingNameMap(List ruleIds) { + if(CollectionUtil.isEmpty(ruleIds)) { + return Maps.newHashMapWithExpectedSize(1); + } + Map param = Maps.newHashMapWithExpectedSize(1); + param.put("ids", ruleIds); + List thingNameList = mapper.getThingNameList(param); + if (CollectionUtil.isEmpty(thingNameList)) { + return Maps.newHashMapWithExpectedSize(1); + } + Map> collect = thingNameList.parallelStream() + .collect(Collectors.groupingBy(AlarmRuleEntityDTO::getRuleId, Collectors.mapping(AlarmRuleEntityDTO::getThingName, Collectors.toSet()))); + Map result = Maps.newHashMapWithExpectedSize(collect.size()); + collect.forEach((key, set) -> result.put(key, String.join(",", set))); + return result; + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleLogServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleLogServiceImpl.java new file mode 100644 index 0000000..8563508 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleLogServiceImpl.java @@ -0,0 +1,192 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.NumberUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmProcessingUserDTO; +import com.thing.alarm.alarm.dto.AlarmRuleLogDTO; +import com.thing.alarm.alarm.entity.AlarmRuleLogEntity; +import com.thing.alarm.alarm.mapper.AlarmRuleLogMapper; +import com.thing.alarm.alarm.service.AlarmDisposeLogService; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.alarm.alarm.service.AlarmRuleLogService; +import com.thing.alarm.configuration.event.LogSaveActionEvent; +import com.thing.common.core.enumeration.AlarmStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 告警记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-20 + */ +@Service +@RequiredArgsConstructor +public class AlarmRuleLogServiceImpl extends BaseServiceImpl implements AlarmRuleLogService { + + private final AlarmRuleEntityService alarmRuleEntityService; + private final AlarmDisposeLogService alarmDisposeLogService; + private final ThingManageContextService thingManageContextService; + + + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String status = (String) params.get("status"); + wrapper.eq( AlarmRuleLogEntity::getStatus, status,StringUtils.isNotBlank(status)); + return wrapper; + } + + @Override + @DataFilter + public PageData page(Map params) { + if(Objects.nonNull(params.get("name"))){ + params.putIfAbsent("ruleName", params.get("name")); + } + if(Objects.nonNull(params.get("level"))){ + params.putIfAbsent("ruleLevel", params.get("level")); + } + // 转换成like + paramsToLike(params, "ruleName","key"); + // 获取逆变器code列表 + List thingCodes = getThingCodesByPlantIds(params); + params.put("thingCodes", thingCodes); + //分页 + Integer page = Optional.ofNullable(MapUtil.getInt(params, "page")).orElse(1); + Integer limit = Optional.ofNullable(MapUtil.getInt(params, "limit")).orElse(10); + params.put("limit", limit); + params.put("offset", (page - 1) * limit); + long count = mapper.getCount(params); + if(count <= 0){ + return new PageData<>(Collections.emptyList(), 0); + } + List list = mapper.getList(params); + fillExtraInfo(list); + return new PageData<>(list, count); + } + + private List getThingCodesByPlantIds(Map params) { + String plantIds = MapUtil.getStr(params, "plantIds"); + if (StringUtils.isNotBlank(plantIds)) { + List ids = Arrays.stream(plantIds.split(",")).map(Long::parseLong).collect(Collectors.toList()); + // 基于光伏的物关系查询所有逆变器 + Optional> optionalList = thingManageContextService.findRootDetailAllByFromIds(ids); + return optionalList.orElseGet(Collections::emptyList).stream().map(IotThingRelationDetailDTO::getToCode).toList(); + } else { + return null; + } + } + + @Override + public List listInfo(Map params) { + paramsToLike(params, "ruleName","key"); + List list = mapper.getList(params); + fillExtraInfo(list); + return list; + + } + + private void fillExtraInfo(List list) { + if (CollectionUtil.isEmpty(list)) { + return; + } + List ruleIds = list.stream().map(AlarmRuleLogDTO::getRuleId).collect(Collectors.toList()); + List disposeIds = list.stream().map(AlarmRuleLogDTO::getDisposeUserId).collect(Collectors.toList()); + Map thingNameMap = alarmRuleEntityService.getThingNameMap(ruleIds); + List processingUserList = + CollectionUtil.isNotEmpty(disposeIds) + ? mapper.getProcessingUserList(disposeIds) + : Lists.newArrayList(); + + list.forEach( + item -> { + Long createTime = DateTimeUtils.dateToTimestamp(item.getAlarmTime()); + Long disposeTime = + item.getDisposeTime() == null + ? System.currentTimeMillis() + : DateTimeUtils.dateToTimestamp(item.getDisposeTime()); + item.setExistTime(NumberUtil.roundStr((disposeTime - createTime) / 3600000d, 1) + "h"); + item.setThingName(thingNameMap.get(item.getRuleId())); + processingUserList.stream() + .filter(user -> Objects.equals(user.getId(), item.getDisposeUserId())) + .findFirst() + .ifPresent(user -> item.setDisposeUserName(user.getRealName())); + }); + } + + @Override + public AlarmRuleLogDTO get(Long id) { + Map params = Maps.newHashMapWithExpectedSize(1); + params.put("id", id); + List list = mapper.getList(params); + if (CollectionUtil.isEmpty(list)) { + throw new SysException("未找到数据"); + } + AlarmRuleLogDTO alarmRuleLogDTO = list.get(0); + Map thingNameMap = alarmRuleEntityService.getThingNameMap(CollectionUtil.newArrayList(alarmRuleLogDTO.getRuleId())); + alarmRuleLogDTO.setThingName(thingNameMap.get(alarmRuleLogDTO.getRuleId())); + return alarmRuleLogDTO; + } + + @Override + public List getIntervalPushTask() { + return mapper.getIntervalPushTask(); + } + + /** + * 保存/修改日志 + * + * @param actionEvent 事件 + */ + @Override + public void save(LogSaveActionEvent actionEvent) { + AlarmRuleLogEntity entity = new AlarmRuleLogEntity(); + entity.setRuleId(actionEvent.getAlarmRuleId()); + entity.setActionId(actionEvent.getActionId()); + entity.setStatus(AlarmStatus.PENDING.getStatus()); + entity.setContent(actionEvent.getContent()); + entity.setAlarmTime(new Date(actionEvent.getTs())); + mapper.insert(entity); + } + + @Override + public Long updateStatus(Long id, String status, Long disposeUserId, long disposeTime) { + AlarmRuleLogEntity alarmRuleLogEntity = mapper.selectOneById(id); + if (Objects.isNull(alarmRuleLogEntity)) { + return null; + } + alarmRuleLogEntity.setDisposeUserId(disposeUserId); + alarmRuleLogEntity.setDisposeTime(DateTimeUtils.timestampToDate(disposeTime)); + alarmRuleLogEntity.setStatus(status); + updateById(alarmRuleLogEntity); + return alarmRuleLogEntity.getRuleId(); + } + + @Override + @Transactional(rollbackFor = SysException.class) + public void deleteAlarmRuleLog(Long[] ids) { + this.batchDelete(ids); + // 删除相关处理记录 + alarmDisposeLogService.deleteAlarmDisposeLog(Lists.newArrayList(ids)); + } + + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleServiceImpl.java new file mode 100644 index 0000000..e808447 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleServiceImpl.java @@ -0,0 +1,340 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Maps; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleDTO; +import com.thing.alarm.alarm.dto.AlarmRuleEntityDTO; +import com.thing.alarm.alarm.entity.AlarmRuleEntity; +import com.thing.alarm.alarm.mapper.AlarmRuleMapper; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.AlarmStatus; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.event.CacheAlarmEvent; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 告警规则 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-07-19 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class AlarmRuleServiceImpl extends BaseServiceImpl implements AlarmRuleService { + + private final AlarmRuleEntityService alarmRuleEntityService; + private final ApplicationEventPublisher applicationEventPublisher; + private final SysUserMapper userDao; + @Resource @Lazy private AlarmRuleActionService alarmRuleActionService; + @Resource + private ThingManageContextService thingManageContextService; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + String type = (String) params.get("type"); + String level = (String) params.get("level"); + List ids = (List) params.get("ids"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in(AlarmRuleEntity::getId, ids,CollectionUtil.isNotEmpty(ids)) + .like( AlarmRuleEntity::getName, name,StringUtils.isNotBlank(name)) + .eq(AlarmRuleEntity::getType, type,StringUtils.isNotBlank(type)) + .eq( AlarmRuleEntity::getLevel, level,StringUtils.isNotBlank(level)); + return wrapper; + } + + @Override + @DataFilter(completeDataMark = false) + public PageData page(Map params) { + //转换成like + paramsToLike(params, "name"); + getIdByThingName(params); + PageData page = super.getPageData(params,AlarmRuleDTO.class); + List ruleList = page.getList(); + if(CollectionUtil.isNotEmpty(ruleList)) { + List ids = ruleList.stream().map(AlarmRuleDTO::getId).collect(Collectors.toList()); + Map thingNameMap = alarmRuleEntityService.getThingNameMap(ids); + List userIds = ruleList.stream().map(AlarmRuleDTO::getCreator).distinct().collect(Collectors.toList()); + List userList = userDao.selectListByIds(userIds); + page.setList(ruleList.stream().peek(item -> { + item.setThingName(thingNameMap.get(item.getId())); + userList.stream() + .filter(user -> Objects.equals(item.getCreator(), user.getId())) + .findFirst() + .ifPresent(user -> item.setCreateName(user.getRealName())); + }).collect(Collectors.toList())); + } + return page; + } + + private void getIdByThingName(Map params) { + String thingName = (String) params.get("thingName"); + if (StringUtils.isBlank(thingName)) { + return; + } + paramsToLike(params, "thingName"); + List list = alarmRuleEntityService.getThingNameList(params); + List collect = list.stream().map(AlarmRuleEntityDTO::getRuleId).distinct().collect(Collectors.toList()); + params.put("ids", CollectionUtil.isNotEmpty(collect) ? collect : CollectionUtil.newArrayList(-1L)); + } + + @Override + public AlarmRuleDTO get(Long id) { + AlarmRuleDTO alarmRuleDTO = super.getByIdAs(id,AlarmRuleDTO.class); + if (alarmRuleDTO == null) { + throw new SysException("未找到数据"); + } + List ruleItems = alarmRuleEntityService.selectByRuleIds(CollectionUtil.toList(id)); + if (!ruleItems.isEmpty()) { + List thingIds = ruleItems.stream().map(AlarmRuleEntityDTO::getThingId).collect(Collectors.toList()); + List attrCodes = ruleItems.stream().map(AlarmRuleEntityDTO::getThingAttr).collect(Collectors.toList()); + Optional> optionalIotThingViewDTOS = thingManageContextService.findEntityAllById(thingIds); + Optional> optionalIotThingDictRelationDTOS = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(thingIds, attrCodes); + Map thingMap = optionalIotThingViewDTOS.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingEntityDTO::getId, IotThingEntityDTO::getName)); + Map attrMap = optionalIotThingDictRelationDTOS.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingDictRelationParamDTO::getCode, IotThingDictRelationParamDTO::getName)); + ruleItems.forEach( + item -> { + item.setThingName(thingMap.get(item.getThingId())); + item.setThingAttrName(attrMap.get(item.getThingAttr())); + }); + alarmRuleDTO.setEntityList(ruleItems); + } + + return alarmRuleDTO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(AlarmRuleDTO dto) { + validateAlarmCondition(dto.getCondition()); + long id = new SnowFlakeIDKeyGenerator().nextId(); + dto.setId(id); + dto.setEnable(true); + super.saveDto(dto); + fillRuleEntity(dto.getEntityList()); + alarmRuleEntityService.saveBatch(dto.getId(), dto.getEntityList()); + alarmRuleActionService.operationActionByRule(id, dto.getType(), dto.getLevel()); + if (dto.getEnable() && StringUtils.equals(dto.getTriggerType(), TriggerTypeEnum.SOCKET.getValue())) { + //缓存 + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, CollectionUtil.newArrayList(id))); + } + + // 创建数据缓存设置 + // createCacheDataSetting(dto.getEntityList()); + } + + /** 给告警实体添加上entityId */ + private void fillRuleEntity(List originalList) { + List thingCodeSet = + originalList.stream() + .map(AlarmRuleEntityDTO::getThingCode) + .collect(Collectors.toList()); + List optionalList = thingManageContextService.findModelByCodes(thingCodeSet).orElseGet(Collections::emptyList); + Map modelMap =optionalList.stream().collect(Collectors.toMap( + k-> k.get(CacheNameEnum.ModelField.THING_MODEL_CODE.getField()).asText(), + v-> v.get(CacheNameEnum.ModelField.THING_MODEL_ID.getField()).asLong())); + + originalList.forEach(item -> item.setEntityId(modelMap.get(item.getThingCode()))); + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(AlarmRuleDTO dto) { + validateAlarmCondition(dto.getCondition()); + AlarmRuleEntity entity = mapper.selectOneById(dto.getId()); + dto.setEnable(true); + super.updateDto(dto); + alarmRuleEntityService.saveBatch(dto.getId(), dto.getEntityList()); + //修改推送表 + if(!StringUtils.equals(entity.getType(), dto.getType()) && !StringUtils.equals(entity.getLevel(), dto.getLevel())) { + alarmRuleActionService.operationActionByRule(dto.getId(), dto.getType(), dto.getLevel()); + } else if (!StringUtils.equals(entity.getType(), dto.getType())) { + alarmRuleActionService.operationActionByRule(dto.getId(), dto.getType(), null); + } else if (!StringUtils.equals(entity.getLevel(), dto.getLevel())) { + alarmRuleActionService.operationActionByRule(dto.getId(), null, dto.getLevel()); + } + //缓存 + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, CollectionUtil.newArrayList(dto.getId()))); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + List ruleIds = Arrays.asList(ids); + //删除 + super.batchDelete(ids); + alarmRuleEntityService.deleteByRuleIds(ruleIds); + //缓存 + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, ruleIds)); + } + + /** + * 获取所有启用规则 + * + * @return list + */ + @Override + public List getEnableList(TriggerTypeEnum triggerType) { + List alarmRuleList = getList(triggerType); + return getAlarmJsons(alarmRuleList); + } + + /** + * 缓存 + * + * @param ids 主键集合 + * @return list + */ + @Override + public List getAlarmJsonList(List ids) { + List alarmRuleList = mapper.selectListByQuery(QueryWrapper.create().in(AlarmRuleEntity::getId, ids) + .eq(AlarmRuleEntity::getEnable, true).eq(AlarmRuleEntity::getTriggerType, TriggerTypeEnum.SOCKET.getValue())); + return getAlarmJsons(alarmRuleList); + } + + private List getAlarmJsons(List alarmRuleList) { + if (CollectionUtil.isEmpty(alarmRuleList)) { + return new ArrayList<>(); + } + List ruleEntityList = getThingNameList(alarmRuleList); + List thingAttrList = getThingAttrList(ruleEntityList); + return alarmRuleList.parallelStream().map(item -> convertJson(item, ruleEntityList, thingAttrList)).collect(Collectors.toList()); + } + + private List getList(TriggerTypeEnum triggerType) { + return mapper.selectListByQuery(QueryWrapper.create().eq(AlarmRuleEntity::getEnable, true).eq(AlarmRuleEntity::getTriggerType, triggerType.getValue())); + } + + private List getThingNameList(List alarmRuleList) { + Map params = Maps.newHashMapWithExpectedSize(1); + params.put("ids", alarmRuleList.stream().map(AlarmRuleEntity::getId).collect(Collectors.toList())); + return alarmRuleEntityService.getThingNameList(params); + } + + private List getThingAttrList(List ruleEntityList) { + Map> listMap = ruleEntityList.parallelStream() + .collect(Collectors.groupingBy(AlarmRuleEntityDTO::getThingId, Collectors.mapping(AlarmRuleEntityDTO::getThingAttr, Collectors.toList()))); + if(CollectionUtil.isEmpty(listMap)) { + return new ArrayList<>(); + } + Optional> optionalList = thingManageContextService + .findDictRelationAllByEntityIdsAndCodes(new ArrayList<>(listMap.keySet()), listMap.values().stream().flatMap(Collection::stream).distinct().collect(Collectors.toList())); + return optionalList.orElseGet(Collections::emptyList); + } + + private AlarmJson convertJson(AlarmRuleEntity entity, List ruleEntityList, List thingAttrList) { + AlarmJson model = new AlarmJson(); + model.setRuleId(entity.getId().toString()); + model.setName(entity.getName()); + model.setCondition(entity.getCondition()); + model.setContent(entity.getContent()); + model.setRepeat(entity.getRepeat()); + model.setStatus(entity.getStatus()); + model.setList(ruleEntityList.parallelStream().filter(item -> Objects.equals(entity.getId(), item.getRuleId())) + .map(item -> { + AlarmJson.AlarmEntity alarmEntity = new AlarmJson.AlarmEntity(); + alarmEntity.setLabel(item.getLabel()); + alarmEntity.setThingId(item.getThingId()); + alarmEntity.setThingCode(item.getThingCode()); + alarmEntity.setThingName(item.getThingName()); + alarmEntity.setThingAttr(item.getThingAttr()); + String thingAttrName = thingAttrList.stream().filter(attr -> Objects.equals(attr.getEntityId(), item.getThingId()) && StringUtils.equals(attr.getCode(), item.getThingAttr())) + .map(IotThingDictRelationParamDTO::getName).findFirst().orElse(item.getThingAttr()); + alarmEntity.setThingAttrName(thingAttrName); + return alarmEntity; + }).collect(Collectors.toList())); + return model; + } + + /** + * 主键查询 + * + * @param ids 主键集合 + * @return list + */ + @Override + public List selectListByIds(List ids) { + return mapper.selectListByIds(ids); + } + + /** + * 订阅物列表 + * + * @return list + */ + @Override + public List getSubscribeList() { + List alarmRuleList = getList(TriggerTypeEnum.SOCKET); + if (CollectionUtil.isEmpty(alarmRuleList)) { + return new ArrayList<>(); + } + return alarmRuleEntityService.selectByRuleIds(alarmRuleList.parallelStream().map(AlarmRuleEntity::getId).collect(Collectors.toList())); + } + + @Override + public List getAlarmRuleDTO(List typeList, List levelList){ + List alarmRuleEntityList = mapper.selectListByQuery(QueryWrapper.create() + .in( AlarmRuleEntity::getType, typeList,CollectionUtil.isNotEmpty(typeList)) + .in(AlarmRuleEntity::getLevel, levelList,CollectionUtil.isNotEmpty(levelList)) + .eq(AlarmRuleEntity::getEnable, true)); + return ConvertUtils.sourceToTarget(alarmRuleEntityList, AlarmRuleDTO.class); + } + + /** + * 处理完成 + * + * @param id 主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void completed(Long id) { + AlarmRuleEntity entity = mapper.selectOneById(id); + if(entity == null) { + throw new SysException("未找到记录"); + } + entity.setStatus(AlarmStatus.PROCESSED.getStatus()); + mapper.update(entity); + //缓存 + if(StringUtils.equals(entity.getTriggerType(), TriggerTypeEnum.SOCKET.getValue())) { + applicationEventPublisher.publishEvent(new CacheAlarmEvent(this, CollectionUtil.newArrayList(id))); + } + } + + private void validateAlarmCondition(String condition){ + boolean isValid = FormulaUtil.validateFormula(condition); + if(!isValid){ + throw new SysException("当前条件不合法"); + } + + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleSettingServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleSettingServiceImpl.java new file mode 100644 index 0000000..491e599 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmRuleSettingServiceImpl.java @@ -0,0 +1,181 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmRuleActionDTO; +import com.thing.alarm.alarm.dto.AlarmRuleDTO; +import com.thing.alarm.alarm.dto.AlarmRuleSettingDTO; +import com.thing.alarm.alarm.dto.AlarmRuleSettingPageDTO; +import com.thing.alarm.alarm.entity.AlarmRuleSettingEntity; +import com.thing.alarm.alarm.mapper.AlarmRuleSettingMapper; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.alarm.alarm.service.AlarmRuleSettingService; +import com.thing.alarm.msgpush.entity.MsgPushSettingEntity; +import com.thing.alarm.msgpush.mapper.MsgPushSettingMapper; +import com.thing.common.core.enumeration.AlarmSettingType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysUserMapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 告警设置 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-08-15 + */ +@Service +public class AlarmRuleSettingServiceImpl extends BaseServiceImpl implements AlarmRuleSettingService { + + @Autowired + private SysUserMapper sysUserDao; + + @Autowired + private MsgPushSettingMapper msgPushSettingDao; + + @Autowired + private AlarmRuleService alarmRuleService; + + @Autowired + private AlarmRuleActionService alarmRuleActionService; + + @DataFilter + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public PageData getAlarmRuleSettingPage(Map params) { + String receiver = (String) params.get("receiver"); + if (StringUtils.isNotBlank(receiver)) { + List msgPushList = msgPushSettingDao.selectListByQuery(QueryWrapper.create()); + if (CollectionUtil.isEmpty(msgPushList)) { + return new PageData<>(new ArrayList<>(), 0); + } + List userEntityList = sysUserDao.selectListByQuery(QueryWrapper.create().like(SysUserEntity::getRealName, receiver)); + if (CollectionUtil.isEmpty(userEntityList)) { + return new PageData<>(new ArrayList<>(), 0); + } + List userIds = userEntityList.parallelStream().map(item -> item.getId().toString()).collect(Collectors.toList()); + List actionIds = msgPushList.stream() + .filter(msg -> userIds.parallelStream().anyMatch(userId -> msg.getReceivers().contains(userId))) + .map(MsgPushSettingEntity::getId) + .collect(Collectors.toList()); + params.put("actionIdList", CollectionUtil.isNotEmpty(actionIds) ? actionIds : CollectionUtil.newArrayList(-1L)); + } + String pushId = (String) params.get("pushId"); + params.put("pushId", StringUtils.isNotBlank(pushId) ? Long.parseLong(pushId) : null); + PageData iPage = getPageData(params); + List alarmRuleSettingPageList = mapper.getAlarmRuleSettingPageList(params); + List userList = getUserList(alarmRuleSettingPageList); + return getPageData(alarmRuleSettingPageList.stream().peek(item -> item.setReceivers(userList.parallelStream() + .filter(user -> item.getReceivers().contains(user.getId().toString())) + .map(SysUserEntity::getRealName) + .collect(Collectors.joining(",")))).collect(Collectors.toList()), iPage.getTotal(), AlarmRuleSettingPageDTO.class); + } + + private List getUserList(List alarmRuleSettingPageList) { + List userIdList = alarmRuleSettingPageList.parallelStream() + .flatMap(item -> Arrays.stream(item.getReceivers().split(",").clone()).map(Long::parseLong)) + .distinct() + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(userIdList)) { + return new ArrayList<>(); + } + return sysUserDao.selectListByIds(userIdList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void insertAlarmRuleSetting(AlarmRuleSettingDTO alarmRuleSettingDTO) { + this.saveDto(alarmRuleSettingDTO); + alarmRuleActionService.insertAlarmRuleAction(alarmRuleSettingDTO.getId(), buildAlarmRuleActionList(alarmRuleSettingDTO)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateAlarmRuleSetting(AlarmRuleSettingDTO alarmRuleSettingDTO){ + this.updateDto(alarmRuleSettingDTO); + alarmRuleActionService.insertAlarmRuleAction(alarmRuleSettingDTO.getId(), buildAlarmRuleActionList(alarmRuleSettingDTO)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteAlarmRuleSetting(Long[] ids){ + this.batchDelete(ids); + alarmRuleActionService.deleteAlarmRuleActionDTOList(Lists.newArrayList(ids)); + } + + + private List buildAlarmRuleActionList(AlarmRuleSettingDTO alarmRuleSettingDTO){ + List settingIds = Arrays.asList(alarmRuleSettingDTO.getSettingIds().split(",")); + List alarmRuleList; + if (AlarmSettingType.NAME.getStatus().equals(alarmRuleSettingDTO.getSettingType())){ + return settingIds.parallelStream().map(item -> { + AlarmRuleActionDTO ruleAction = new AlarmRuleActionDTO(); + ruleAction.setSettingId(alarmRuleSettingDTO.getId()); + ruleAction.setRuleId(Long.parseLong(item)); + ruleAction.setActionType(alarmRuleSettingDTO.getActionType()); + ruleAction.setActionId(alarmRuleSettingDTO.getActionId()); + return ruleAction; + }).collect(Collectors.toList()); + }else if (AlarmSettingType.TYPE.getStatus().equals(alarmRuleSettingDTO.getSettingType())){ + alarmRuleList = alarmRuleService.getAlarmRuleDTO(settingIds, null); + }else if (AlarmSettingType.LEVEL.getStatus().equals(alarmRuleSettingDTO.getSettingType())){ + alarmRuleList = alarmRuleService.getAlarmRuleDTO(null, settingIds); + }else { + throw new SysException("类型错误!"); + } + return alarmRuleList.parallelStream().map(item -> { + AlarmRuleActionDTO ruleAction = new AlarmRuleActionDTO(); + ruleAction.setSettingId(alarmRuleSettingDTO.getId()); + ruleAction.setRuleId(item.getId()); + ruleAction.setActionType(alarmRuleSettingDTO.getActionType()); + ruleAction.setActionId(alarmRuleSettingDTO.getActionId()); + return ruleAction; + }).collect(Collectors.toList()); + } + + @Override + public List getAlarmRuleSettingDTOList(String type, String level){ + QueryColumn setting_type = column("setting_type"); + List alarmRuleSettingDTOList = new ArrayList<>(); + List alarmRuleSettingList = mapper.selectListByQuery( + QueryWrapper.create().and(setting_type.eq(AlarmSettingType.TYPE.getStatus(),StringUtils.isNotBlank(type)) + .or(setting_type.eq(AlarmSettingType.LEVEL.getStatus(),StringUtils.isNotBlank(level))))); + + List typeList = new ArrayList<>(); + List levelList = new ArrayList<>(); + if (StringUtils.isNotBlank(type)){ + typeList = alarmRuleSettingList.stream().filter(item -> AlarmSettingType.TYPE.getStatus().equals(item.getSettingType())) + .filter(item -> Lists.newArrayList(item.getSettingIds().split(",")).contains(type)).collect(Collectors.toList()); + } + if (StringUtils.isNotBlank(level)){ + levelList = alarmRuleSettingList.stream().filter(item -> AlarmSettingType.LEVEL.getStatus().equals(item.getSettingType())) + .filter(item -> Lists.newArrayList(item.getSettingIds().split(",")).contains(level)).collect(Collectors.toList()); + } + if (CollectionUtil.isNotEmpty(typeList)) alarmRuleSettingDTOList.addAll(ConvertUtils.sourceToTarget(typeList, AlarmRuleSettingDTO.class)); + if (CollectionUtil.isNotEmpty(levelList)) alarmRuleSettingDTOList.addAll(ConvertUtils.sourceToTarget(levelList, AlarmRuleSettingDTO.class)); + return alarmRuleSettingDTOList; + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmServiceImpl.java new file mode 100644 index 0000000..33b3144 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/alarm/service/impl/AlarmServiceImpl.java @@ -0,0 +1,168 @@ +package com.thing.alarm.alarm.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.BooleanUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Maps; +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleActionCache; +import com.thing.alarm.alarm.entity.AlarmRuleEntity; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.alarm.alarm.service.AlarmService; +import com.thing.alarm.configuration.IntegrationContext; +import com.thing.alarm.configuration.condition.Condition; +import com.thing.alarm.configuration.event.ActionEvent; +import com.thing.alarm.configuration.event.LogSaveActionEvent; +import com.thing.alarm.configuration.event.MsgActionEvent; +import com.thing.common.core.enumeration.ActionType; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-07-26 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class AlarmServiceImpl implements AlarmService { + + private final AlarmRuleService alarmRuleService; + private final IntegrationContext integrationContext; + + /** + * socket告警 + * + * @param alarmJsonList 告警信息 + * @param actionList 告警动作 + */ + @Override + public void alarm(List alarmJsonList, List actionList) { + if (CollectionUtil.isEmpty(alarmJsonList)) { + return; + } + //保存最新值 + updateElementInfo(alarmJsonList); + List alarmList = alarmJsonList.parallelStream() + //排除报错数据 + .filter(item -> item.getEvaluate() != null) + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(alarmList)) { + return; + } + //恢复正常 + List eventList = createRecoveryEvent(alarmList.parallelStream().filter(item -> !item.getEvaluate()), actionList); + //告警 + eventList.addAll(createAlarmEvent(alarmList.parallelStream().filter(AlarmJson::getEvaluate), actionList)); + //推送动作 + for (ActionEvent actionEvent : eventList) { + integrationContext.getEventBus().post(actionEvent); + } + } + + /** + * 恢复正常 + * + * @param alarmJsonList 告警列表 + * @param actionList 动作列表 + * @return list + */ + private List createRecoveryEvent(Stream alarmJsonList, List actionList) { + return alarmJsonList.flatMap(alarmJson -> { + List actionCacheList = actionList.stream() + .filter(action -> Objects.equals(action.getRuleId(), Long.parseLong(alarmJson.getRuleId()))) + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(actionCacheList)) { + return Stream.empty(); + } + return actionCacheList.parallelStream().flatMap(item -> { + List events = new ArrayList<>(); + if (ActionType.MSG.getValue().equals(item.getActionType())) { + events.add(new MsgActionEvent(item.getRuleId(), item.getActionId(), alarmJson.getName(), "规则【" + alarmJson.getName() + "】恢复正常")); + } + return events.stream(); + }); + }).collect(Collectors.toList()); + } + + @Override + public Boolean evaluate(AlarmJson alarmJson) { + List list = alarmJson.getList(); + Map env = Maps.newHashMapWithExpectedSize(list.size()); + try { + list.stream().filter(alarmEntity -> alarmEntity.getValue() != null) + .forEach(alarmEntity -> env.put(alarmEntity.getLabel(), alarmEntity.getValue())); + log.debug("{} 公式计算, 入参: {}", alarmJson.getCondition(), env); + return env.size() == list.size() && integrationContext.getConditionEvaluator().evaluate(new Condition(alarmJson.getCondition(), env)); + } catch (Exception e) { + log.error("{} 公式出错, 入参: {}, 错误信息:{}", alarmJson.getCondition(), env, e.getMessage()); + return null; + } + } + + /** + * 触发告警 + * + * @param alarmJsonList 告警列表 + * @param actionList 动作列表 + * @return list + */ + private List createAlarmEvent(Stream alarmJsonList, List actionList) { + return alarmJsonList.flatMap(alarmJson -> { + //list里time相同,随便取一个 + Long ts = alarmJson.getList().get(0).getTime(); + String content = alarmJson.getCondition(); + for (AlarmJson.AlarmEntity alarmEntity : alarmJson.getList()) { + content = content.replace(alarmEntity.getLabel(), alarmEntity.getThingName() + "_" + alarmEntity.getThingAttrName() + "_" + alarmEntity.getValue()); + } + String finalContent = StringUtils.isBlank(alarmJson.getContent()) ? content : alarmJson.getContent() + ":\n" + content; + + List actionCacheList = actionList.stream() + .filter(action -> Objects.equals(action.getRuleId(), Long.parseLong(alarmJson.getRuleId()))) + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(actionCacheList)) { + return Stream.of(new LogSaveActionEvent(Long.parseLong(alarmJson.getRuleId()), null, ts, finalContent)); + } + + return actionCacheList.parallelStream().flatMap(item -> { + List events = new ArrayList<>(); + if (ActionType.MSG.getValue().equals(item.getActionType())) { + events.add(new MsgActionEvent(Long.parseLong(alarmJson.getRuleId()), item.getActionId(), alarmJson.getName(), finalContent)); + } + //todo CONTROL... + //日志 + events.add(new LogSaveActionEvent(Long.parseLong(alarmJson.getRuleId()), item.getActionId(), ts, finalContent)); + return events.stream(); + }); + }).collect(Collectors.toList()); + } + + /** + * 保存最新值 + * + * @param alarmJsonList 列表 + */ + private void updateElementInfo(List alarmJsonList) { + List ids = alarmJsonList.parallelStream().map(item -> Long.parseLong(item.getRuleId())).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(ids)) { + return; + } + List alarmRuleList = alarmRuleService.selectListByIds(ids); + alarmRuleService.saveOrUpdateBatch(alarmRuleList.stream() + .peek(alarm -> alarmJsonList.parallelStream().filter(item -> StringUtils.equals(alarm.getId().toString(), item.getRuleId())) + .findFirst().ifPresent(item -> { + alarm.setElementInfo(JSONObject.toJSONString(item.getList())); + if(BooleanUtil.isTrue(item.getEvaluate()) && StringUtils.isNotBlank(item.getStatus())) { + alarm.setStatus(item.getStatus()); + } + })).collect(Collectors.toList())); + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheInitializer.java b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheInitializer.java new file mode 100644 index 0000000..9f26a4c --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheInitializer.java @@ -0,0 +1,91 @@ +package com.thing.alarm.cache; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Maps; +import com.thing.alarm.alarm.dto.*; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.data.dto.AttrCache; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 告警启动放入缓存 + * + * @author zhenghh. 2022-12-05 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class AlarmCacheInitializer extends AbstractCacheService { + + private final AlarmRuleService alarmRuleService; + private final AlarmRuleEntityService alarmRuleEntityService; + private final AlarmRuleActionService alarmRuleActionService; + private final AlarmCacheService alarmCacheService; + + @Override + public String getCacheName() { + return CacheNameEnum.ALARM_RULE + ", " + CacheNameEnum.ALARM_RULE_ACTION; + } + + @Override + public void initCache() { + initAlarmCache(); + initAlarmRuleActionCache(); + } + + /** + * 初始化告警规则缓存 + */ + private void initAlarmCache() { + List alarmJsonList = alarmRuleService.getEnableList(TriggerTypeEnum.SOCKET); + if (CollectionUtil.isEmpty(alarmJsonList)) { + return; + } + List ruleEntityList = alarmRuleEntityService.selectByRuleIds(alarmJsonList.stream() + .map(item -> Long.parseLong(item.getRuleId())).distinct().collect(Collectors.toList())); + Map> entityMap = ruleEntityList.stream() + .collect(Collectors.groupingBy(item -> item.getThingCode() + ":" + item.getThingAttr(), Collectors.mapping(item -> String.valueOf(item.getRuleId()), Collectors.toList()))); + List attributeCacheList = new ArrayList<>(entityMap.size()); + entityMap.forEach((key, list) -> attributeCacheList.add(new AttrCache(key, list.parallelStream().distinct().collect(Collectors.toList())))); + attributeCacheList.forEach(alarmCacheService::registerRuleEntity); + alarmJsonList.forEach(alarmCacheService::registerRule); + } + + /** + * 初始化告警动作缓存 + */ + private void initAlarmRuleActionCache() { + List alarmRuleActionEntityList = alarmRuleActionService.listAs(Maps.newHashMap(),AlarmRuleActionDTO.class); + if (CollectionUtil.isEmpty(alarmRuleActionEntityList)) { + return; + } + Map> alarmRuleActionEntityMap = alarmRuleActionEntityList.stream().collect(Collectors.groupingBy(AlarmRuleActionDTO::getRuleId)); + alarmRuleActionEntityMap.forEach((key, list) -> { + AlarmRuleActionCacheDTO alarmRuleActionCacheDTO = new AlarmRuleActionCacheDTO(); + alarmRuleActionCacheDTO.setKey(key); + List caches = new ArrayList<>(); + list.forEach(item -> { + if (caches.stream().noneMatch(cache -> Objects.equals(cache.getActionId(), item.getActionId()))) { + caches.add(ConvertUtils.sourceToTarget(item, AlarmRuleActionCache.class)); + } + }); + alarmRuleActionCacheDTO.setActionList(caches); + alarmCacheService.registerAlarmRuleActionCacheDTO(alarmRuleActionCacheDTO); + }); + } + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheService.java b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheService.java new file mode 100644 index 0000000..263f1c1 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheService.java @@ -0,0 +1,78 @@ +package com.thing.alarm.cache; + + +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleActionCacheDTO; +import com.thing.common.data.dto.AttrCache; + +/** + * @author zhenghh. 2022-07-21 + **/ +public interface AlarmCacheService { + + /** + * 注册规则 + * + * @param alarmRule 规则 + * @return AlarmJson + */ + AlarmJson registerRule(AlarmJson alarmRule); + + /** + * 注册告警实体 + * + * @param alarmEntityCache 规则 + * @return AlarmEntityCache + */ + AttrCache registerRuleEntity(AttrCache alarmEntityCache); + + /** + * 注册告警推送 + * @param alarmRuleActionCacheDTO + * @return + */ + AlarmRuleActionCacheDTO registerAlarmRuleActionCacheDTO(AlarmRuleActionCacheDTO alarmRuleActionCacheDTO); + + /** + * 移除规则缓存 + * @param key 缓存key + */ + void unRegisterRule(String key); + + /** + * 移除实体缓存 + * @param key 缓存key + */ + void unRegisterRuleEntity(String key); + + /** + * 移除告警推送缓存 + * + * @param key 缓存key + */ + void unRegisterRuleActionEntity(Long key); + + /** + * 获取告警规则 + * + * @param key 缓存主键 + * @return AlarmJson + */ + AlarmJson getRule(String key); + + /** + * 获取告警实体 + * + * @param key 缓存主键 + * @return AlarmEntityCache + */ + AttrCache getRuleEntity(String key); + + /** + * 获取告警动作 + * + * @param key + * @return + */ + AlarmRuleActionCacheDTO getAlarmRuleActionCacheDTO(Long key); +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheServiceImpl.java new file mode 100644 index 0000000..b7435e0 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmCacheServiceImpl.java @@ -0,0 +1,130 @@ +package com.thing.alarm.cache; + +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleActionCacheDTO; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.data.dto.AttrCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +/** + * @author zhenghh. 2022-07-21 + **/ +@Slf4j +@Service +public class AlarmCacheServiceImpl implements AlarmCacheService { + + + /** + * 注册告警规则 + * + * @param alarmRule 告警规则 + */ + @Override + @CachePut(value = CacheNameEnum.ALARM_RULE, key = "#alarmRule.ruleId") + public AlarmJson registerRule(AlarmJson alarmRule) { + //log.debug("告警规则缓存保存: {}", alarmRule.getRuleId()); + return alarmRule; + } + + /** + * 注册告警实体 + * + * @param alarmEntityCache 规则 + */ + @Override + @CachePut(value = CacheNameEnum.ALARM_RULE_ENTITY, key = "#alarmEntityCache.key") + public AttrCache registerRuleEntity(AttrCache alarmEntityCache) { + //log.debug("告警规则实体缓存保存: {}", alarmEntityCache.getKey()); + return alarmEntityCache; + } + + /** + * 注册告警动作 + * + * @param alarmRuleActionCacheDTO 动作 + * @return AlarmRuleActionCacheDTO + */ + @Override + @CachePut(value = CacheNameEnum.ALARM_RULE_ACTION, key = "#alarmRuleActionCacheDTO.key") + public AlarmRuleActionCacheDTO registerAlarmRuleActionCacheDTO(AlarmRuleActionCacheDTO alarmRuleActionCacheDTO) { + //log.debug("告警动作缓存保存: {}", alarmRuleActionCacheDTO.getKey()); + return alarmRuleActionCacheDTO; + } + + /** + * 移除规则缓存 + * + * @param key 缓存key + */ + @Override + @CacheEvict(value = CacheNameEnum.ALARM_RULE, key = "#key") + public void unRegisterRule(String key) { + //log.debug("告警规则缓存删除: {}", key); + } + + /** + * 移除缓存 + * + * @param key 缓存key + */ + @Override + @CacheEvict(value = CacheNameEnum.ALARM_RULE_ENTITY, key = "#key") + public void unRegisterRuleEntity(String key) { + //log.debug("告警实体缓存删除: {}", key); + } + + /** + * 移除告警动作缓存 + * + * @param key 缓存key + */ + @Override + @CacheEvict(value = CacheNameEnum.ALARM_RULE_ACTION, key = "#key") + public void unRegisterRuleActionEntity(Long key) { + //log.debug("告警动作缓存删除: {}", key); + } + + + /** + * 获取告警规则 + * + * @param key 缓存主键 + * @return AlarmJson + */ + @Override + @Cacheable(value = CacheNameEnum.ALARM_RULE, key = "#key") + public AlarmJson getRule(String key) { + //log.warn("告警规则缓存未找到: {}", key); + return null; + } + + /** + * 获取告警实体 + * + * @param key 缓存主键 + * @return AlarmEntityCache + */ + @Override + @Cacheable(value = CacheNameEnum.ALARM_RULE_ENTITY, key = "#key") + public AttrCache getRuleEntity(String key) { + //log.warn("告警实体缓存未找到: {}", key); + return null; + } + + /** + * 获取告警动作 + * + * @param key key + * @return AlarmRuleActionCacheDTO + */ + @Override + @Cacheable(value = CacheNameEnum.ALARM_RULE_ACTION, key = "#key") + public AlarmRuleActionCacheDTO getAlarmRuleActionCacheDTO(Long key) { + //log.warn("告警动作缓存未找到: {}", key); + return null; + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmEventListener.java b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmEventListener.java new file mode 100644 index 0000000..89f2a08 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/cache/AlarmEventListener.java @@ -0,0 +1,209 @@ +package com.thing.alarm.cache; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.BooleanUtil; +import com.thing.alarm.alarm.dto.*; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleEntityService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.alarm.alarm.service.AlarmService; +import com.thing.common.core.enumeration.AlarmStatus; +import com.thing.common.core.event.CacheAlarmActionEvent; +import com.thing.common.core.event.CacheAlarmEvent; +import com.thing.common.core.event.QueueAlarmEvent; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.data.dto.AttrCache; +import com.thing.common.data.dto.QueueMsgDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-12-22 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class AlarmEventListener { + + private final AlarmRuleService alarmRuleService; + private final AlarmRuleEntityService alarmRuleEntityService; + private final AlarmRuleActionService alarmRuleActionService; + private final AlarmCacheService alarmCacheService; + private final AlarmService alarmService; + + /** + * 告警规则缓存 + * + * @param event 事件 + */ + @TransactionalEventListener(value = CacheAlarmEvent.class, phase = TransactionPhase.AFTER_COMMIT) + public void onAlarmCacheEvent(CacheAlarmEvent event) { + log.info("listened to cache event type {}", event.getEventType()); + onAlarmCache(event.getIds()); + } + + /** + * 告警动作缓存 + * + * @param event 事件 + */ + @TransactionalEventListener(value = CacheAlarmActionEvent.class, phase = TransactionPhase.AFTER_COMMIT) + public void onAlarmActionCacheEvent(CacheAlarmActionEvent event) { + log.info("listened to cache event type {}", event.getEventType()); + onAlarmActionCache(event.getIds()); + } + + @EventListener(QueueAlarmEvent.class) + public void onAlarmEvent(QueueAlarmEvent event) { + List list = event.getList(); + try { + List alarmJsonList = list.parallelStream() + .flatMap(item -> { + AttrCache attrCache = alarmCacheService.getRuleEntity(item.getThingCode() + ":" + item.getAttrKey()); + if (attrCache == null || CollectionUtil.isEmpty(attrCache.getList())) { + return Stream.empty(); + } + return attrCache.getList().parallelStream() + .map(alarmId -> { + AlarmJson rule = alarmCacheService.getRule(alarmId); + //未处理 不重复告警 跳过 + if (!StringUtils.equals(rule.getStatus(), AlarmStatus.PROCESSED.getStatus()) && !rule.getRepeat()) { + return null; + } + rule.setList(rule.getList().parallelStream() + .peek(entity -> { + if (StringUtils.equals(entity.getThingCode(), item.getThingCode()) + && StringUtils.equals(entity.getThingAttr(), item.getAttrKey())) { + entity.setTime(item.getTs()); + entity.setValue(new BigDecimal(item.getVal())); + } + }).collect(Collectors.toList())); + return rule; + }).filter(Objects::nonNull); + }) + .peek(item -> { + // todo 暂时判断为 所有值已存在 且 时间一致 + if (CollectionUtil.isNotEmpty(item.getList()) && + item.getList().parallelStream().allMatch(entity -> entity.getValue() != null) && + item.getList().parallelStream().map(AlarmJson.AlarmEntity::getTime).filter(Objects::nonNull).distinct().count() == 1) { + item.setEvaluate(alarmService.evaluate(item)); + } + //第一次或处理完成后第一次告警 修改状态为待处理 + if (BooleanUtil.isTrue(item.getEvaluate()) && + (StringUtils.isBlank(item.getStatus()) || StringUtils.equals(item.getStatus(), AlarmStatus.PROCESSED.getStatus()))) { + item.setStatus(AlarmStatus.PENDING.getStatus()); + } + }).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(alarmJsonList)) { + return; + } + //修改缓存 + alarmJsonList.forEach(alarmCacheService::registerRule); + //获取动作列表 + List actionList = alarmJsonList.parallelStream() + .filter(item -> BooleanUtil.isTrue(item.getEvaluate())) + .flatMap(item -> { + AlarmRuleActionCacheDTO cache = alarmCacheService.getAlarmRuleActionCacheDTO(Long.valueOf(item.getRuleId())); + if (cache == null) { + return Stream.empty(); + } + return cache.getActionList().stream(); + }).collect(Collectors.toList()); + alarmService.alarm(alarmJsonList, actionList); + } catch (Exception e) { + log.error("告警失败: {}", e.getMessage()); + } + } + + + /** + * 告警规则缓存修改 + * + * @param ruleIds 告警规则主键 + */ + private void onAlarmCache(List ruleIds) { + if (CollectionUtil.isEmpty(ruleIds)) { + return; + } + List alarmCacheList = ruleIds.stream().map(key -> alarmCacheService.getRule(String.valueOf(key))).filter(Objects::nonNull).collect(Collectors.toList()); + List attrCacheKeys = alarmCacheList.parallelStream() + .flatMap(cache -> cache.getList().stream().map(item -> item.getThingCode() + ":" + item.getThingAttr())) + .distinct().collect(Collectors.toList()); + + List cacheNewList = alarmRuleService.getAlarmJsonList(ruleIds); + //告警规则 + //新增 修改 + cacheNewList.stream() + .filter(newCache -> alarmCacheList.parallelStream().noneMatch(cache -> StringUtils.equals(cache.getRuleId(), newCache.getRuleId())) + || alarmCacheList.parallelStream().anyMatch(cache -> StringUtils.equals(cache.getRuleId(), newCache.getRuleId())) + ).forEach(alarmCacheService::registerRule); + //删除 + alarmCacheList.stream().map(AlarmJson::getRuleId) + .filter(ruleId -> cacheNewList.parallelStream().noneMatch(cache -> StringUtils.equals(cache.getRuleId(), ruleId))) + .forEach(alarmCacheService::unRegisterRule); + + //告警对象 + List ruleEntityList = alarmRuleEntityService.selectByRuleIds(ruleIds); + Map> attributeMap = ruleEntityList.parallelStream() + .collect(Collectors.groupingBy(item -> item.getThingCode() + ":" + item.getThingAttr(), Collectors.mapping(item -> String.valueOf(item.getRuleId()), Collectors.toList()))); + //新增 + attributeMap.keySet().stream().filter(key -> !attrCacheKeys.contains(key)) + .forEach(key -> alarmCacheService.registerRuleEntity(new AttrCache(key, attributeMap.get(key)))); + attrCacheKeys.forEach(key -> { + List list = attributeMap.get(key); + //不存在删除 + if (CollectionUtil.isEmpty(list)) { + alarmCacheService.unRegisterRuleEntity(key); + } else { + //存在修改 + alarmCacheService.registerRuleEntity(new AttrCache(key, list)); + } + }); + } + + + /** + * 告警动作缓存修改 + * + * @param ruleIds 告警规则主键 + */ + private void onAlarmActionCache(List ruleIds) { + if (CollectionUtil.isEmpty(ruleIds)) { + return; + } + //先移除 + ruleIds.forEach(alarmCacheService::unRegisterRuleActionEntity); + //新增 + List actionList = alarmRuleActionService.getListByRuleIds(ruleIds); + if (CollectionUtil.isEmpty(actionList)) { + return; + } + actionList.stream() + .collect(Collectors.groupingBy(AlarmRuleActionDTO::getRuleId)) + .forEach((key, list) -> { + List caches = new ArrayList<>(); + list.forEach(item -> { + if (caches.stream().noneMatch(cache -> Objects.equals(cache.getActionId(), item.getActionId()))) { + caches.add(ConvertUtils.sourceToTarget(item, AlarmRuleActionCache.class)); + } + }); + AlarmRuleActionCacheDTO alarmRuleActionCacheDTO = new AlarmRuleActionCacheDTO(); + alarmRuleActionCacheDTO.setKey(key); + alarmRuleActionCacheDTO.setActionList(caches); + alarmCacheService.registerAlarmRuleActionCacheDTO(alarmRuleActionCacheDTO); + }); + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/DefaultIntegrationContext.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/DefaultIntegrationContext.java new file mode 100644 index 0000000..b2d27f4 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/DefaultIntegrationContext.java @@ -0,0 +1,78 @@ +package com.thing.alarm.configuration; + +import com.google.common.eventbus.AsyncEventBus; +import com.google.common.eventbus.EventBus; +import com.thing.alarm.configuration.condition.AviatorScriptConditionEvaluator; +import com.thing.alarm.configuration.condition.ConditionEvaluator; +import com.thing.alarm.configuration.event.ActionEventListener; +import com.thing.api.FormulaInvokeService; +import com.thing.common.util.thread.ThingThreadFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Service; + +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * @author zhenghh. 2022-07-15 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class DefaultIntegrationContext implements IntegrationContext, CommandLineRunner { + + private final FormulaInvokeService formulaInvokeService; + private final ActionEventListener actionEventListener; + + private EventBus eventBus; + private AviatorScriptConditionEvaluator scriptConditionEvaluator; + + @Value("${alarm.event_bus.queue_size:50}") + private int queueSize; + + @Value("${alarm.event_bus.core_pool_size:5}") + private int corePoolSize; + + @Value("${alarm.event_bus.max_pool_size:20}") + private int maxPoolSize; + + @Override + public void run(String... args) throws Exception { + //初始化事件总线 + initEventBus(); + //载入规则执行器 + this.scriptConditionEvaluator = new AviatorScriptConditionEvaluator(formulaInvokeService); + } + + private void initEventBus() { + ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize,maxPoolSize,30, TimeUnit.SECONDS, + new LinkedBlockingDeque<>(queueSize), ThingThreadFactory.forName("alarmEventBus")); + this.eventBus = new AsyncEventBus("alarmSystemEventBus", executor); + this.eventBus.register(actionEventListener); + } + + /** + * 获取事件总线 + * + * @return EventBus + */ + @Override + public EventBus getEventBus() { + return this.eventBus; + } + + /** + * 规则执行器 + * + * @return 规则执行器 + */ + @Override + public ConditionEvaluator getConditionEvaluator() { + return this.scriptConditionEvaluator; + } + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/IntegrationContext.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/IntegrationContext.java new file mode 100644 index 0000000..1e40afb --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/IntegrationContext.java @@ -0,0 +1,25 @@ +package com.thing.alarm.configuration; + +import com.google.common.eventbus.EventBus; +import com.thing.alarm.configuration.condition.ConditionEvaluator; + +/** + * @author zhenghh. 2022-07-15 + **/ +public interface IntegrationContext { + + /** + * 获取事件总线 + * + * @return EventBus + */ + EventBus getEventBus(); + + /** + * 规则执行器 + * + * @return 规则执行器 + */ + ConditionEvaluator getConditionEvaluator(); + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/AviatorScriptConditionEvaluator.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/AviatorScriptConditionEvaluator.java new file mode 100644 index 0000000..b8c306f --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/AviatorScriptConditionEvaluator.java @@ -0,0 +1,44 @@ +package com.thing.alarm.configuration.condition; + +import com.thing.api.FormulaInvokeService; +import lombok.extern.slf4j.Slf4j; + +/** + * @author zhenghh. 2022-07-15 + **/ +@Slf4j +public class AviatorScriptConditionEvaluator implements ConditionEvaluator { + + private final FormulaInvokeService formulaInvokeService; + + public AviatorScriptConditionEvaluator(FormulaInvokeService formulaInvokeService) { + this.formulaInvokeService = formulaInvokeService; + } + + /** + * 校验器类型 + * + * @return 校验器类型 + */ + @Override + public String type() { + return "AviatorScript"; + } + + /** + * 校验 + * + * @param condition 规则 + * @return boolean + */ + @Override + public boolean evaluate(Condition condition) { + try { + Object result = formulaInvokeService.invokeFormula(condition.getFormula(), condition.getEnv()); + return (Boolean) result; + } catch (Exception e) { + log.error("Error evaluating JS formula", e); + throw new RuntimeException(e.getMessage()); + } + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/Condition.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/Condition.java new file mode 100644 index 0000000..e589847 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/Condition.java @@ -0,0 +1,17 @@ +package com.thing.alarm.configuration.condition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Map; + +/** + * @author zhenghh. 2022-07-15 + **/ +@Getter +@AllArgsConstructor +public class Condition { + + private final String formula; + private final Map env; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/ConditionEvaluator.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/ConditionEvaluator.java new file mode 100644 index 0000000..cd4ef72 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/condition/ConditionEvaluator.java @@ -0,0 +1,21 @@ +package com.thing.alarm.configuration.condition; + +/** + * 规则条件校验器 + * @author zhenghh. 2022-07-15 + **/ +public interface ConditionEvaluator { + + /** + * 校验器类型 + * @return 校验器类型 + */ + String type(); + + /** + * 校验 + * @param condition 规则 + * @return boolean + */ + boolean evaluate(Condition condition); +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEvent.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEvent.java new file mode 100644 index 0000000..b3e6193 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEvent.java @@ -0,0 +1,25 @@ +package com.thing.alarm.configuration.event; + + +import com.thing.common.core.enumeration.ActionType; + +/** + * @author zhenghh. 2022-07-15 + **/ +public interface ActionEvent { + + + /** + * 告警规则主键 + * @return 主键 + */ + Long getAlarmRuleId(); + + /** + * 事件类型 + * + * @return actionType + */ + ActionType getActionType(); + +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEventListener.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEventListener.java new file mode 100644 index 0000000..53c46ff --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/ActionEventListener.java @@ -0,0 +1,38 @@ +package com.thing.alarm.configuration.event; + +import com.google.common.eventbus.AllowConcurrentEvents; +import com.google.common.eventbus.Subscribe; + +import com.thing.alarm.alarm.service.AlarmRuleLogService; +import com.thing.alarm.msgpush.dto.AlarmMsgDTO; +import com.thing.alarm.msgpush.service.MsgPushSettingService; +import com.thing.common.core.utils.ConvertUtils; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-07-15 + **/ +@Slf4j +@Component +public class ActionEventListener { + @Autowired + private MsgPushSettingService msgPushSettingService; + @Autowired + private AlarmRuleLogService alarmRuleLogService; + + @Subscribe + @AllowConcurrentEvents + public void msgListener(MsgActionEvent actionEvent) { + log.debug("触发告警: {}", actionEvent); + msgPushSettingService.sendAlarmMsg(ConvertUtils.sourceToTarget(actionEvent, AlarmMsgDTO.class)); + } + + @Subscribe + @AllowConcurrentEvents + public void logSaveListener(LogSaveActionEvent actionEvent) { + log.debug("保存告警日志: {}", actionEvent); + alarmRuleLogService.save(actionEvent); + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/event/LogSaveActionEvent.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/LogSaveActionEvent.java new file mode 100644 index 0000000..48753b1 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/LogSaveActionEvent.java @@ -0,0 +1,39 @@ +package com.thing.alarm.configuration.event; + +import com.thing.common.core.enumeration.ActionType; +import lombok.Getter; +import lombok.ToString; + +import java.io.Serializable; + +/** + * @author zhenghh. 2022-07-27 + **/ +@ToString +@Getter +public class LogSaveActionEvent implements Serializable, ActionEvent { + + private static final long serialVersionUID = 9047983727081784887L; + + private final Long alarmRuleId; + private final Long actionId; + private final Long ts; + private final String content; + + public LogSaveActionEvent(Long alarmRuleId, Long actionId, Long ts, String content) { + this.alarmRuleId = alarmRuleId; + this.actionId = actionId; + this.ts = ts; + this.content = content; + } + + /** + * 事件类型 + * + * @return actionType + */ + @Override + public ActionType getActionType() { + return ActionType.LOG_SAVE; + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/configuration/event/MsgActionEvent.java b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/MsgActionEvent.java new file mode 100644 index 0000000..81b4210 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/configuration/event/MsgActionEvent.java @@ -0,0 +1,51 @@ +package com.thing.alarm.configuration.event; + +import com.thing.common.core.enumeration.ActionType; +import lombok.Getter; +import lombok.ToString; + +import java.io.Serializable; + +/** + * @author zhenghh. 2022-07-18 + **/ +@Getter +@ToString +public class MsgActionEvent implements Serializable, ActionEvent { + + private static final long serialVersionUID = -847141124512484776L; + + /** + * 告警主键 + */ + private final Long alarmRuleId; + /** + * 推送设置主键 + */ + private final Long msgPushSettingId; + /** + * 告警名称 + */ + private final String name; + /** + * 告警内容 + */ + private final String content; + + public MsgActionEvent(Long alarmRuleId, Long msgPushSettingId, String name, String content) { + this.alarmRuleId = alarmRuleId; + this.msgPushSettingId = msgPushSettingId; + this.name = name; + this.content = content; + } + + /** + * 事件类型 + * + * @return actionType + */ + @Override + public ActionType getActionType() { + return ActionType.MSG; + } +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/controller/MsgPushSettingController.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/controller/MsgPushSettingController.java new file mode 100644 index 0000000..1da54f7 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/controller/MsgPushSettingController.java @@ -0,0 +1,114 @@ +package com.thing.alarm.msgpush.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.alarm.msgpush.dto.AlarmMsgDTO; +import com.thing.alarm.msgpush.dto.MsgPushSettingDTO; +import com.thing.alarm.msgpush.service.MsgPushSettingService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.history.dto.MsgHisDTO; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("msgpush/setting") +@Tag(name="推送消息设置") +public class MsgPushSettingController { + @Autowired + private MsgPushSettingService msgPushSettingService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "推送名称"), + @Parameter(name = "repeatPush", description = "是否重复推送") + }) + //@RequiresPermissions("bi:msgpushsetting:page") + public Result> page( @RequestParam Map params){ + PageData page = msgPushSettingService.getPageData(params,MsgPushSettingDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("bi:msgpushsetting:info") + public Result get(@PathVariable("id") Long id){ + MsgPushSettingDTO data = msgPushSettingService.getByIdAs(id,MsgPushSettingDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("bi:msgpushsetting:save") + public Result save(@RequestBody MsgPushSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (ObjectUtil.isNotNull(msgPushSettingService.findMsgPushSettingByName(dto.getName()))) throw new SysException("该名称已存在,请换一个名称。"); + msgPushSettingService.saveDto(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("bi:msgpushsetting:update") + public Result update(@RequestBody MsgPushSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgPushSettingService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("bi:msgpushsetting:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + msgPushSettingService.batchDelete(ids); + + return new Result(); + } + + @PostMapping("sendAlarmMsg") + @Operation(summary="发送告警信息") + public Result sendAlarmMsg(@RequestBody AlarmMsgDTO dto){ + msgPushSettingService.sendAlarmMsg(dto); + return new Result(); + } + + @PostMapping("manualPushMsg") + @Operation(summary="手动推送一次记录") + public Result manualPushMsg(@RequestBody List msgHisDTOList){ + msgPushSettingService.manualPushMsg(msgHisDTOList); + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/AlarmMsgDTO.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/AlarmMsgDTO.java new file mode 100644 index 0000000..5bcd9ac --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/AlarmMsgDTO.java @@ -0,0 +1,14 @@ +package com.thing.alarm.msgpush.dto; + +import lombok.Data; + +/** + * @author zzx + */ +@Data +public class AlarmMsgDTO { + private Long alarmRuleId; + private Long msgPushSettingId; + private String name; + private String content; +} diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/MsgPushSettingDTO.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/MsgPushSettingDTO.java new file mode 100644 index 0000000..434d68c --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/dto/MsgPushSettingDTO.java @@ -0,0 +1,67 @@ +package com.thing.alarm.msgpush.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +@Schema( name= "推送消息设置") +public class MsgPushSettingDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + + @Schema(description = "推送设置名称") + private String name; + + @Schema(description = "推送ids") + private String pushIds; + + @Schema(description = "模板id") + private Long templateId; + + @Schema(description = "接收人集合") + private String receivers; + + @Schema(description = "重复推送,0-开启,1-关闭") + private String repeatPush; + + @Schema(description = "重复推送频率") + private String repeatFrequency; + + @Schema(description = "间隔推送,0-开启,1-关闭") + private String intervalPush; + + @Schema(description = "间隔推送频率") + private String intervalFrequency; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "企业详情id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Long createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "备注") + private String remark; + + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/entity/MsgPushSettingEntity.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/entity/MsgPushSettingEntity.java new file mode 100644 index 0000000..545c745 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/entity/MsgPushSettingEntity.java @@ -0,0 +1,58 @@ +package com.thing.alarm.msgpush.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * ${comments} + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-07-20 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_push_setting") +public class MsgPushSettingEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 推送设置名称 + */ + private String name; + + /** + * 推送ids + */ + private String pushIds; + /** + * 模板id + */ + private Long templateId; + /** + * 接收人集合 + */ + private String receivers; + /** + * 重复推送,0-开启,1-关闭 + */ + private String repeatPush; + /** + * 重复推送频率 + */ + private String repeatFrequency; + /** + * 间隔推送,0-开启,1-关闭 + */ + private String intervalPush; + /** + * 间隔推送频率 + */ + private String intervalFrequency; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/excel/MsgPushSettingExcel.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/excel/MsgPushSettingExcel.java new file mode 100644 index 0000000..6788285 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/excel/MsgPushSettingExcel.java @@ -0,0 +1,45 @@ +package com.thing.alarm.msgpush.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * ${comments} + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-07-20 + */ +@Data +public class MsgPushSettingExcel { + @Excel(name = "主键") + private Long id; + @Excel(name = "推送ids") + private String pushIds; + @Excel(name = "模板id") + private Long templateId; + @Excel(name = "接收人集合") + private String receivers; + @Excel(name = "重复推送,0-开启,1-关闭") + private String repeatPush; + @Excel(name = "间隔推送,0-开启,1-关闭") + private String intervalPush; + @Excel(name = "间隔推送频率") + private String intervalFrequency; + @Excel(name = "租户编码") + private Long tenantCode; + @Excel(name = "企业详情id") + private Long companyId; + @Excel(name = "部门id") + private Long deptId; + @Excel(name = "创建者") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新者") + private Long updater; + @Excel(name = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/mapper/MsgPushSettingMapper.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/mapper/MsgPushSettingMapper.java new file mode 100644 index 0000000..911681d --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/mapper/MsgPushSettingMapper.java @@ -0,0 +1,11 @@ +package com.thing.alarm.msgpush.mapper; + + +import com.thing.alarm.msgpush.entity.MsgPushSettingEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface MsgPushSettingMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/MsgPushSettingService.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/MsgPushSettingService.java new file mode 100644 index 0000000..4b96ebd --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/MsgPushSettingService.java @@ -0,0 +1,38 @@ +package com.thing.alarm.msgpush.service; + +import com.thing.alarm.msgpush.dto.AlarmMsgDTO; +import com.thing.alarm.msgpush.dto.MsgPushSettingDTO; +import com.thing.alarm.msgpush.entity.MsgPushSettingEntity; +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.history.dto.MsgHisDTO; + +import java.util.List; + +/** + * @author zzx + */ +public interface MsgPushSettingService extends IBaseService { + /** + * 发送告警推送接口 + * @return + */ + void sendAlarmMsg(AlarmMsgDTO dto); + + /** + * 重复推送接口 + * @return + */ + void repeatPushMsg(List list); + + /** + * 通过名称检索推送设置相关内容 + * @return + */ + MsgPushSettingDTO findMsgPushSettingByName(String name); + + /** + * 手动推送接口 + * @return + */ + void manualPushMsg(List list); +} \ No newline at end of file diff --git a/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/impl/MsgPushSettingServiceImpl.java b/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/impl/MsgPushSettingServiceImpl.java new file mode 100644 index 0000000..5ed0b34 --- /dev/null +++ b/modules/alarm/src/main/java/com/thing/alarm/msgpush/service/impl/MsgPushSettingServiceImpl.java @@ -0,0 +1,197 @@ +package com.thing.alarm.msgpush.service.impl; + +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.util.ObjectUtil; + +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.msgpush.dto.AlarmMsgDTO; +import com.thing.alarm.msgpush.dto.MsgPushSettingDTO; +import com.thing.alarm.msgpush.entity.MsgPushSettingEntity; +import com.thing.alarm.msgpush.mapper.MsgPushSettingMapper; +import com.thing.alarm.msgpush.service.MsgPushSettingService; +import com.thing.common.core.enumeration.IntervalPushStatus; +import com.thing.common.core.enumeration.PushReceiveStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; + +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.mapper.MsgHisMapper; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.entity.MsgPushEntity; +import com.thing.msg.push.mapper.MsgPushMapper; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import org.apache.commons.lang3.StringUtils; +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.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class MsgPushSettingServiceImpl extends BaseServiceImpl implements MsgPushSettingService { + + @Autowired + private MsgPushMapper msgPushDao; + @Autowired + private MsgPushService msgPushService; + @Autowired + private MsgHisService msgHisService; + @Autowired + private MsgHisMapper msgHisDao; + @Autowired + private SysUserService sysUserService; + @Autowired + private MsgUserService msgUserService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String name = (String) params.get("name"); + String repeatPush = (String) params.get("repeatPush"); + wrapper.like( "name", name,StringUtils.isNotEmpty(name)); + wrapper.eq("repeat_push", repeatPush,StringUtils.isNotEmpty(repeatPush)); + return wrapper; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendAlarmMsg(AlarmMsgDTO dto) { + //获取推送设置的id + Long msgPushSettingId = dto.getMsgPushSettingId(); + //获取告警规则的id + Long alarmRuleId = dto.getAlarmRuleId(); + //获取告警名称 + String name = dto.getName(); + //获取推送设置内容 + String content = dto.getContent(); + MsgPushSettingDTO msgPushSettingDTO = getByIdAs(msgPushSettingId,MsgPushSettingDTO.class); + assert !ObjectUtil.isNull(msgPushSettingDTO) : "未检索到推送设置相关信息"; + //解析推送所需参数 + //1.解析接收推送方式ids集合 + List pushIdList = Lists.newArrayList(stringToLong(msgPushSettingDTO.getPushIds().split(","))); + List msgPushEntityList = msgPushDao.selectListByQuery(QueryWrapper.create().in(MsgPushEntity::getId, pushIdList)); + //2.校验是否开启间接推送 + if (CompareUtil.compare(IntervalPushStatus.ON.getStatus(), msgPushSettingDTO.getIntervalPush()) == 0) { + //检索推送记录信息 + MsgHisEntity msgHisEntity = msgHisDao.selectOneByQuery(QueryWrapper.create().eq(MsgHisEntity::getPublisher, String.valueOf(alarmRuleId)).eq(MsgHisEntity::getPushSettingId, msgPushSettingId).limit(1)); + //校验间隔时间是否达到要求 + if (ObjectUtil.isNotNull(msgHisEntity) && ((System.currentTimeMillis() - (Long.parseLong(msgPushSettingDTO.getIntervalFrequency()) * 60000)) < msgHisEntity.getCreateDate())) return; + } + //4.拼装好后,设置推送用户集合,并进行推送 + //4.1替换模板信息 + //4.2修改发送的content文本内容 + msgPushEntityList.forEach(item -> { + item.setTemplateId(msgPushSettingDTO.getTemplateId()); + item.setTemplateMsg(content); + }); + //推送前,先将上一轮的推送信息标志为结束 + MsgHisEntity msgHisEntity = msgHisDao.selectOneByQuery(QueryWrapper.create().eq(MsgHisEntity::getPublisher, String.valueOf(alarmRuleId)).eq(MsgHisEntity::getPushSettingId, msgPushSettingId).eq(MsgHisEntity::getPushReceiveStatus, PushReceiveStatus.NO_RECEIVE.getStatus()).limit(1)); + if (ObjectUtil.isNotNull(msgHisEntity)) { + msgHisEntity.setPushReceiveStatus(PushReceiveStatus.RECEIVE.getStatus()); + msgHisService.updateById(msgHisEntity); + } + msgPushService.sendCustomContentMsgByUserId(ConvertUtils.sourceToTarget(msgPushEntityList, MsgPushDTO.class), getRealName(msgPushSettingDTO.getReceivers()), name, msgPushSettingId, false); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void repeatPushMsg(List msgHisDTOList) { + //循环推送记录表,将这次的推送状态修改成已确认,并发送一笔新的未确认的数据 + msgHisDTOList.forEach(dto -> { + MsgPushSettingEntity msgPushSettingEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(MsgPushSettingEntity::getId, dto.getPushSettingId())); + //校验重复时间是否达到 + if ((System.currentTimeMillis() - (Long.parseLong(msgPushSettingEntity.getRepeatFrequency()) * 60000)) < dto.getCreateDate().getTime()) return; + List pushIdList = Lists.newArrayList(stringToLong(msgPushSettingEntity.getPushIds().split(","))); + List msgPushEntityList = msgPushDao.selectListByQuery(QueryWrapper.create().in(MsgPushEntity::getId, pushIdList)); + List msgPushDTOList = ConvertUtils.sourceToTarget(msgPushEntityList, MsgPushDTO.class); + //1.替换模板信息并修改发送的content文本内容 + msgPushDTOList.forEach(item -> { + item.setTemplateId(null); + item.setTemplateMsg(null); + item.setContent(dto.getHisMsg()); + }); + msgPushService.sendCustomContentMsgByUserId(msgPushDTOList, getRealName(msgPushSettingEntity.getReceivers()), dto.getPublisher(), dto.getPushSettingId(), true); + dto.setPushReceiveStatus(PushReceiveStatus.RECEIVE.getStatus()); + msgHisService.updateDto(dto); + }); + } + + @Override + public MsgPushSettingDTO findMsgPushSettingByName(String name) { + if (StringUtils.isBlank(name)) throw new SysException("请输入推送设置名称"); + MsgPushSettingEntity entity = mapper.selectOneExt(QueryWrapper.create().eq(MsgPushSettingEntity::getName, name)); + return ObjectUtil.isNull(entity) ? null : ConvertUtils.sourceToTarget(entity, MsgPushSettingDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void manualPushMsg(List msgHisDTOList) { + try { + //循环推送记录表,发送一笔新的已确认的数据 + msgHisDTOList.forEach(dto -> { + if (StringUtils.isBlank(dto.getHisMsg())) throw new SysException("推送消息为空,无法重新推送该条信息"); + if (ObjectUtil.isNull(dto.getPushSettingId())) throw new SysException("暂不支持推送非告警流程产生的推送信息"); + MsgPushSettingEntity msgPushSettingEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(MsgPushSettingEntity::getId, dto.getPushSettingId())); + List pushIdList = Lists.newArrayList(stringToLong(msgPushSettingEntity.getPushIds().split(","))); + List msgPushEntityList = msgPushDao.selectListByQuery(QueryWrapper.create().in(MsgPushEntity::getId, pushIdList)); + List msgPushDTOList = ConvertUtils.sourceToTarget(msgPushEntityList, MsgPushDTO.class); + //1.替换模板信息并修改发送的content文本内容 + msgPushDTOList.forEach(item -> { + item.setTemplateId(null); + item.setTemplateMsg(null); + item.setContent(dto.getHisMsg()); + item.setManual(Boolean.TRUE); + }); + msgPushService.sendCustomContentMsgByUserId(msgPushDTOList, getRealName(msgPushSettingEntity.getReceivers()), dto.getPublisher(), dto.getPushSettingId(), true); + }); + } catch (Exception e) { + throw new SysException("该条推送数据存在问题,请走正常流程的推送"); + } + } + + private Long[] stringToLong(String[] str) { + Long[] arr = new Long[str.length]; + for (int i = 0; i < str.length; i++) { + arr[i] = Long.parseLong(str[i]); + } + return arr; + } + + private List getRealName(String userIds) { + List userIdList = Lists.newArrayList(stringToLong(userIds.split(","))); + List msgUserDTOList = msgUserService.getInfoByUserIds(userIdList); + if(CollectionUtils.isEmpty(msgUserDTOList)){ + return Collections.emptyList(); + } + Map wxopenIdMap = msgUserDTOList.stream().collect(Collectors.toMap(MsgUserDTO::getUserId, u -> Optional.ofNullable(u.getWxOpenId()).orElse(""))); + Map dingdingPhoneMap = msgUserDTOList.stream().collect(Collectors.toMap(MsgUserDTO::getUserId, u -> Optional.ofNullable(u.getDingdingPhone()).orElse(""))); + Map emailMap = msgUserDTOList.stream().collect(Collectors.toMap(MsgUserDTO::getUserId, u -> Optional.ofNullable(u.getEmailUsername()).orElse(""))); + Map wxPhoneMap = msgUserDTOList.stream().collect(Collectors.toMap(MsgUserDTO::getUserId, u -> Optional.ofNullable(u.getWxPhone()).orElse(""))); + List sysUserDTOList = sysUserService.getSysUserDTOList(userIdList); + return sysUserDTOList.stream().map(item -> { + SysUserInfoDTO sysUserInfoDTO = new SysUserInfoDTO(); + sysUserInfoDTO.setId(item.getId()); + sysUserInfoDTO.setRealName(item.getRealName()); + sysUserInfoDTO.setWxOpenId(StringUtils.isNotBlank(wxopenIdMap.get(item.getId())) ? wxopenIdMap.get(item.getId()) : null); + sysUserInfoDTO.setDingdingPhone(StringUtils.isNotBlank(dingdingPhoneMap.get(item.getId())) ? dingdingPhoneMap.get(item.getId()) : null); + sysUserInfoDTO.setEmailUsername(StringUtils.isNotBlank(emailMap.get(item.getId())) ? emailMap.get(item.getId()) : null); + sysUserInfoDTO.setWxPhone(StringUtils.isNotBlank(wxPhoneMap.get(item.getId())) ? wxPhoneMap.get(item.getId()) : null); + return sysUserInfoDTO; + }).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmConfigMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmConfigMapper.xml new file mode 100644 index 0000000..f4cc41c --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmConfigMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmDictMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmDictMapper.xml new file mode 100644 index 0000000..92bdd93 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmDictMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmDisposeLogMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmDisposeLogMapper.xml new file mode 100644 index 0000000..ca75625 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmDisposeLogMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmRuleActionMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmRuleActionMapper.xml new file mode 100644 index 0000000..05ba616 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmRuleActionMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmRuleEntityMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmRuleEntityMapper.xml new file mode 100644 index 0000000..d8bd1c7 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmRuleEntityMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmRuleLogMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmRuleLogMapper.xml new file mode 100644 index 0000000..5acd7ba --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmRuleLogMapper.xml @@ -0,0 +1,114 @@ + + + + + + + + + + + + + + + + + + + + + + AND arl.id = #{params.id} + + + AND arl.alarm_type = #{params.alarmType} + + + AND ar.name like #{params.ruleName} + + + AND ar.level = #{params.ruleLevel} + + + AND arl.status = #{params.status} + + + AND arl.alarm_thing_code in + + #{item} + + + + AND (te.code like #{params.key} or te.name like #{params.key}) + + + AND arl.alarm_time BETWEEN #{params.startTime} AND #{params.endTime} + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmRuleMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmRuleMapper.xml new file mode 100644 index 0000000..a3b280c --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmRuleMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/AlarmRuleSettingMapper.xml b/modules/alarm/src/main/resources/mapper/AlarmRuleSettingMapper.xml new file mode 100644 index 0000000..693de96 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/AlarmRuleSettingMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/alarm/src/main/resources/mapper/MsgPushSettingMapper.xml b/modules/alarm/src/main/resources/mapper/MsgPushSettingMapper.xml new file mode 100644 index 0000000..9a551d6 --- /dev/null +++ b/modules/alarm/src/main/resources/mapper/MsgPushSettingMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/calculation/pom.xml b/modules/calculation/pom.xml new file mode 100644 index 0000000..022d33f --- /dev/null +++ b/modules/calculation/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + calculation + jar + ThingBI Server Modules calculation + + UTF-8 + + + + + com.thing.modules + thing + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + + + com.thing.modules + report-analysis + 5.1 + compile + + + + \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/cache/CalcConfigCacheInitializer.java b/modules/calculation/src/main/java/com/thing/calculation/cache/CalcConfigCacheInitializer.java new file mode 100644 index 0000000..9a49fd6 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/cache/CalcConfigCacheInitializer.java @@ -0,0 +1,33 @@ +package com.thing.calculation.cache; + +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Component; + +/** + * @author siyang + * @date 2024-03-04 + * @description 物计算配置缓存初始化器 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class CalcConfigCacheInitializer extends AbstractCacheService { + + private final CalcTargetConfigService calcTargetConfigService; + + @Override + public String getCacheName() { + return CacheNameEnum.THING_CALC_CONFIG; + } + + @Override + public void initCache() { + calcTargetConfigService.getAllEnabled(); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheInitializer.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheInitializer.java new file mode 100644 index 0000000..be2de64 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheInitializer.java @@ -0,0 +1,57 @@ +package com.thing.calculation.calculation.cache; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.data.dto.AttrCache; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 物计算启动放入缓存 + * + * @author zhenghh. 2022-12-05 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class CalculationCacheInitializer extends AbstractCacheService { + + private final CalculationFormulaSettingService calculationFormulaSettingService; + private final CalculationFormulaAttributeService calculationFormulaAttributeService; + private final CalculationCacheService calculationCacheService; + + @Override + public String getCacheName() { + return CacheNameEnum.CALCULATION_ATTRIBUTES; + } + + @Override + public void initCache() { +// //所有启用的socket计算设置 +// List calculationList = calculationFormulaSettingService.getEnableList(TriggerTypeEnum.SOCKET); +// if (CollectionUtil.isEmpty(calculationList)) { +// return; +// } +// //获取计算元素 +// List calculationIdList = calculationList.parallelStream().map(CalculationFormulaSettingEntity::getId).collect(Collectors.toList()); +// List attributeList = calculationFormulaAttributeService.getByCalculationIdList(calculationIdList); +// +// Map> attributeMap = attributeList.parallelStream() +// .collect(Collectors.groupingBy(item -> item.getThingCode() + ":" + item.getThingAttr(), Collectors.mapping(item -> String.valueOf(item.getCalculationId()), Collectors.toSet()))); +// attributeMap.forEach((key, list) -> calculationCacheService.saveCalculationAttribute(new AttrCache(key, new ArrayList<>(list)))); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheService.java new file mode 100644 index 0000000..5e179fd --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheService.java @@ -0,0 +1,31 @@ +package com.thing.calculation.calculation.cache; + + +import com.thing.common.data.dto.AttrCache; + +/** + * @author zhenghh. 2022-07-21 + **/ +public interface CalculationCacheService { + + /** + * 保存计算元素 + * @param calculationAttributeCache 计算元素 + * @return 计算元素 + */ + AttrCache saveCalculationAttribute(AttrCache calculationAttributeCache); + + /** + * 删除计算元素 + * @param key 计算元素key + */ + void deleteCalculationAttribute(String key); + + /** + * 获取计算元素缓存 + * @param key 计算元素key + * @return 计算设置主键集合 + */ + AttrCache getCalculationIdList(String key); + +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheServiceImpl.java new file mode 100644 index 0000000..508df27 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationCacheServiceImpl.java @@ -0,0 +1,54 @@ +package com.thing.calculation.calculation.cache; + +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.data.dto.AttrCache; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +/** + * @author zhenghh. 2022-07-21 + **/ +@Slf4j +@Service +public class CalculationCacheServiceImpl implements CalculationCacheService { + + /** + * 保存计算元素 + * + * @param attributeCache 计算元素 + */ + @Override + @CachePut(value = CacheNameEnum.CALCULATION_ATTRIBUTES, key = "#attributeCache.key") + public AttrCache saveCalculationAttribute(AttrCache attributeCache) { + //log.debug("计算元素缓存保存: {}", attributeCache.getKey()); + return attributeCache; + } + + /** + * 删除计算元素 + * + * @param key 计算元素key + */ + @Override + @CacheEvict(value = CacheNameEnum.CALCULATION_ATTRIBUTES, key = "#key") + public void deleteCalculationAttribute(String key) { + //log.debug("计算元素缓存删除: {}", key); + } + + /** + * 获取计算元素缓存 + * + * @param key 计算元素key + * @return 计算设置主键集合 + */ + @Override + @Cacheable(value = CacheNameEnum.CALCULATION_ATTRIBUTES, key = "#key") + public AttrCache getCalculationIdList(String key) { + //log.warn("计算设置缓存未找到: {}", key); + return null; + } + +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationEventListener.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationEventListener.java new file mode 100644 index 0000000..f6c3599 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/cache/CalculationEventListener.java @@ -0,0 +1,101 @@ +package com.thing.calculation.calculation.cache; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.calculation.calculation.dto.CalculationForm; +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.dto.CalculationFormulaSettingDTO; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.calculation.calculation.service.CalculationService; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.event.CacheCalculationEvent; +import com.thing.common.core.event.QueueCalculationEvent; +import com.thing.common.data.dto.AttrCache; +import com.thing.common.data.dto.QueueMsgDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-12-22 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class CalculationEventListener { + + private final CalculationFormulaSettingService calculationFormulaSettingService; + private final CalculationFormulaAttributeService calculationFormulaAttributeService; + private final CalculationCacheService calculationCacheService; + private final CalculationService calculationService; + + /** + * 物计算缓存事件监听 + * + * @param event 事件 + */ + @TransactionalEventListener(value = CacheCalculationEvent.class, phase = TransactionPhase.AFTER_COMMIT) + public void onCalculationCacheEvent(CacheCalculationEvent event) { + log.info("listened to cache event type {}", event.getEventType()); + List list = event.getList(); + if (CollectionUtil.isEmpty(list)) { + return; + } + List keys = list.stream().map(item -> item.getCode() + item.getAttr()).distinct().collect(Collectors.toList()); + List attributeList = calculationFormulaAttributeService.getListByCodeAndAttr(keys); + Map> attrMap = attributeList.stream() + .collect(Collectors.groupingBy(item -> item.getThingCode() + ":" + item.getThingAttr(), + Collectors.mapping(item -> String.valueOf(item.getCalculationId()), Collectors.toSet()))); + //移除 + List removeList = list.parallelStream() + .filter(attr -> !attrMap.containsKey(attr.getCode() + ":" + attr.getAttr())) + .map(attr -> attr.getCode() + ":" + attr.getAttr()).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(removeList)) { + removeList.forEach(calculationCacheService::deleteCalculationAttribute); + } + //修改 + if (CollectionUtil.isNotEmpty(attrMap)) { + attrMap.forEach((key, ids) -> calculationCacheService.saveCalculationAttribute(new AttrCache(key, new ArrayList<>(ids)))); + } + } + + /** + * 队列推送数据计算 + * + * @param event 事件 + */ + @EventListener(QueueCalculationEvent.class) + public void onQueueCalculationEvent(QueueCalculationEvent event) { + List list = event.getList(); + try { + List attrCaches = list.stream() + .map(item -> calculationCacheService.getCalculationIdList(item.getThingCode() + ":" + item.getAttrKey())) + .filter(Objects::nonNull).distinct().collect(Collectors.toList()); + if (CollectionUtil.isEmpty(attrCaches)) { + return; + } + List socketDataList = list.parallelStream() + .filter(item -> attrCaches.stream().anyMatch(attrCache -> StringUtils.equals(item.getThingCode(), attrCache.getCode()) && StringUtils.equals(item.getAttrKey(), attrCache.getAttr()))) + .map(item -> new CalculationForm(item.getThingCode(), item.getAttrKey(), item.getTs(), new BigDecimal(item.getVal()))) + .collect(Collectors.toList()); + List calculationIds = attrCaches.parallelStream() + .flatMap(attrCache -> (attrCache == null || CollectionUtil.isEmpty(attrCache.getList())) ? Stream.empty() : attrCache.getList().stream().map(Long::parseLong)) + .distinct().collect(Collectors.toList()); + //获取计算记录 + List times = socketDataList.stream().map(CalculationForm::getTs).distinct().collect(Collectors.toList()); + List calculationList = calculationFormulaSettingService.selectCalculationList(times, calculationIds, TriggerTypeEnum.SOCKET); + calculationService.calculation(calculationList, socketDataList, TriggerTypeEnum.SOCKET); + } catch (Exception e) { + log.error("计算失败: {}", e.getMessage()); + } + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationController.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationController.java new file mode 100644 index 0000000..004a34d --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationController.java @@ -0,0 +1,52 @@ +package com.thing.calculation.calculation.controller; + +import com.thing.calculation.calculation.dto.CalculationTrialForm; +import com.thing.calculation.calculation.dto.CalculationTrialResult; +import com.thing.calculation.calculation.dto.RecalculationForm; +import com.thing.calculation.calculation.service.CalculationService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 计算 + * @author zhenghh. 2022-05-06 + **/ +@RestController +@RequestMapping("calculation") +@Tag(name = "物计算") +public class CalculationController { + + @Autowired + private CalculationService calculationService; + + @PostMapping("trial") + @Operation(summary="试算") + @LogOperation("试算") + public Result trial(@RequestBody CalculationTrialForm form) { + //效验数据 + ValidatorUtils.validateEntity(form, DefaultGroup.class); + form.getAttrs().forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + return new Result().ok(calculationService.trial(form)); + } + + @PostMapping("recalculation") + @Operation(summary="重新计算") + @LogOperation("重新计算") + public Result recalculation(@RequestBody RecalculationForm form) { + //效验数据 + ValidatorUtils.validateEntity(form, DefaultGroup.class); + + calculationService.recalculation(form); + + return new Result(); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaLogController.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaLogController.java new file mode 100644 index 0000000..7957aec --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaLogController.java @@ -0,0 +1,90 @@ +package com.thing.calculation.calculation.controller; + +import com.thing.calculation.calculation.dto.CalculationFormulaLogDTO; +import com.thing.calculation.calculation.service.CalculationFormulaLogService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Map; + + +/** + * 计算异常日志表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@RestController +@RequestMapping("calculation/formula/log") +@Tag(name = "计算异常日志表") +public class CalculationFormulaLogController { + @Autowired + private CalculationFormulaLogService calculationFormulaLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params) { + PageData page = calculationFormulaLogService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + CalculationFormulaLogDTO data = calculationFormulaLogService.getByIdAs(id,CalculationFormulaLogDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CalculationFormulaLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + calculationFormulaLogService.saveDto(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CalculationFormulaLogDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + calculationFormulaLogService.updateDto(dto); + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + calculationFormulaLogService.removeByIds(Arrays.asList(ids)); + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaSettingController.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaSettingController.java new file mode 100644 index 0000000..1b28d94 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationFormulaSettingController.java @@ -0,0 +1,113 @@ +package com.thing.calculation.calculation.controller; + +import cn.hutool.core.map.MapUtil; +import com.thing.calculation.calculation.dto.CalculationFormulaSettingDTO; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@RestController +@RequestMapping("calculation/formula/setting") +@Tag(name = "计算设置表") +public class CalculationFormulaSettingController { + @Autowired + private CalculationFormulaSettingService calculationFormulaSettingService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "物名称/编码"), + @Parameter(name = "enable", description = "是否启用"), + @Parameter(name = "defVal", description = "是否使用默认值"), + @Parameter(name = "repeat", description = "是否重复计算") + }) + public Result> page( @RequestParam Map params) { + PageData page = calculationFormulaSettingService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + CalculationFormulaSettingDTO data = calculationFormulaSettingService.get(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CalculationFormulaSettingDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.getAttributes().forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + calculationFormulaSettingService.saveDto(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CalculationFormulaSettingDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + dto.getAttributes().forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + calculationFormulaSettingService.update(dto); + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + calculationFormulaSettingService.delete(ids); + return new Result(); + } + + @PostMapping("updateSort") + @Operation(summary="修改排序") + public Result update(@RequestBody List> dtos){ + List collect = dtos.stream().map(dto -> + { + CalculationFormulaSettingEntity entity = new CalculationFormulaSettingEntity(); + entity.setId(MapUtil.getLong(dto, "id")); + entity.setSort(MapUtil.getLong(dto, "sort")); + return entity; + }).collect(Collectors.toList()); + calculationFormulaSettingService.updateBatchById(collect); + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationUsageDictController.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationUsageDictController.java new file mode 100644 index 0000000..15eb731 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/controller/CalculationUsageDictController.java @@ -0,0 +1,33 @@ +package com.thing.calculation.calculation.controller; + +import com.thing.calculation.calculation.dto.CalculationUsageDictRequest; +import com.thing.calculation.calculation.service.CalculationUsageDictService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("v2/calculation/dict") +@Tag(name = "数据补发,用量属性计算") +@RequiredArgsConstructor +public class CalculationUsageDictController { + + private final CalculationUsageDictService service; + + @PostMapping("usage/recalculate") + @Operation(summary = "重新计算用量属性") + @LogOperation("重新计算用量属性") + public Result recalculationForUsageAttr(@RequestBody CalculationUsageDictRequest request) { + ValidatorUtils.validateEntity(request, DefaultGroup.class); + service.recalculate(request); + return new Result<>(); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationAttr.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationAttr.java new file mode 100644 index 0000000..d737ee4 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationAttr.java @@ -0,0 +1,23 @@ +package com.thing.calculation.calculation.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author zhenghh. 2022-11-15 + **/ +@Data +@AllArgsConstructor +public class CalculationAttr { + + @Schema(description = "AA/AB/AC") + private String formulaLabel; + @Schema(description = "最新值") + private BigDecimal lastValue; + @Schema(description = "最新时间") + private Long lastTime; +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationConditionForm.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationConditionForm.java new file mode 100644 index 0000000..bacd7bb --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationConditionForm.java @@ -0,0 +1,41 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2021-11-15 + **/ +@Data +@Schema( name= "物计算表单") +public class CalculationConditionForm { + + @Schema(description = "开始时间") + @NotNull(message = "开始时间不能为空", groups = DefaultGroup.class) + private Long startTime; + @Schema(description = "结束时间") + @NotNull(message = "结束时间不能为空", groups = DefaultGroup.class) + private Long endTime; + + @Schema(description = "计算物属性列表") + @NotNull(message = "计算物属性列表不能为空", groups = DefaultGroup.class) + private List list; + + @Data + @Schema( name= "计算物属性") + public static class CalculationThing { + + @Schema(description = "设备编码") + @NotBlank(message = "设备编码不能为空", groups = DefaultGroup.class) + private String code; + @Schema(description = "设备属性") + @NotNull(message = "设备属性不能为空", groups = DefaultGroup.class) + private List keys; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationForm.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationForm.java new file mode 100644 index 0000000..9bb465c --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationForm.java @@ -0,0 +1,41 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * @author zhenghh. 2021-11-03 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "计算表单") +public class CalculationForm implements Serializable { + + @Serial + private static final long serialVersionUID = -3416089260595761064L; + + @Schema(description = "设备") + @NotBlank(message = "设备不能为空", groups = DefaultGroup.class) + private String code; + @Schema(description = "设备属性") + @NotBlank(message = "设备属性不能为空", groups = DefaultGroup.class) + private String attr; + @Schema(description = "时间戳") + @NotNull(message = "时间戳不能为空", groups = DefaultGroup.class) + private Long ts; + @Schema(description = "数值") + @NotNull(message = "数值不能为空", groups = DefaultGroup.class) + private BigDecimal value; + +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaAttributeDTO.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaAttributeDTO.java new file mode 100644 index 0000000..9398322 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaAttributeDTO.java @@ -0,0 +1,48 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; + +/** +* 计算设置表 +* +* @author zhenghh sunlightcs@gmail.com +* @since 3.0 2022-05-01 +*/ +@Data +@Schema( name= "计算设置属性表") +public class CalculationFormulaAttributeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "计算配置主键") + private Long calculationId; + @Schema(description = "元素主键") + @NotNull(message="元素不能为空", groups = DefaultGroup.class) + private Long thingId; + @Schema(description = "元素") + @NotBlank(message="元素不能为空", groups = DefaultGroup.class) + private String thingCode; + @Schema(description = "元素属性") + @NotBlank(message="元素属性不能为空", groups = DefaultGroup.class) + private String thingAttr; + @Schema(description = "AA/AB/AC") + @NotBlank(message="元素编码不能为空", groups = DefaultGroup.class) + private String formulaLabel; + @Schema(description = "默认值") + private String defaultValue; + @Schema(description = "最新值") + private String lastValue; + @Schema(description = "最新时间") + private String lastTime; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaLogDTO.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaLogDTO.java new file mode 100644 index 0000000..54e3492 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaLogDTO.java @@ -0,0 +1,35 @@ +package com.thing.calculation.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 计算异常日志表 +* +* @author zhenghh sunlightcs@gmail.com +* @since 3.0 2022-05-01 +*/ +@Data +@Schema( name= "计算异常日志表") +public class CalculationFormulaLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "计算设置主键") + private Long calculationId; + @Schema(description = "计算时间") + private Long calculationTs; + @Schema(description = "计算状态(0数据缺失,1默认值计算,2计算失败,3计算成功,4推送tb失败)") + private String calculationStatus; + @Schema(description = "计算次数,超过指定次数不进行计算") + private Integer calculationNum; + @Schema(description = "计算元素值") + private String calculationInfo; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaSettingDTO.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaSettingDTO.java new file mode 100644 index 0000000..255da44 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationFormulaSettingDTO.java @@ -0,0 +1,105 @@ +package com.thing.calculation.calculation.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 计算设置表 +* +* @author zhenghh sunlightcs@gmail.com +* @since 3.0 2022-05-01 +*/ +@Data +@Schema( name= "计算设置表") +public class CalculationFormulaSettingDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "结果主键") + @NotNull(message="结果主键不能为空", groups = DefaultGroup.class) + private Long resultId; + @Schema(description = "结果code") + @NotBlank(message="结果编码不能为空", groups = DefaultGroup.class) + private String resultCode; + @Schema(description = "结果name") + private String resultName; + @Schema(description = "结果属性code") + @NotBlank(message="结果属性编码不能为空", groups = DefaultGroup.class) + private String resultAttr; + @Schema(description = "结果属性name") + private String resultAttrName; + @Schema(description = "计算公式中文") + @NotBlank(message="计算公式中文不能为空", groups = DefaultGroup.class) + private String formulaName; + @Schema(description = "计算公式") + @NotBlank(message="计算公式不能为空", groups = DefaultGroup.class) + private String formula; + @Schema(description = "计算类型") + @NotBlank(message="计算类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "是否启用计算") + @NotNull(message="是否启用计算不能为空", groups = DefaultGroup.class) + private Boolean enable; + @Schema(description = "是否启用默认值") + @NotNull(message="是否启用默认值不能为空", groups = DefaultGroup.class) + private Boolean enableDefVal; + @Schema(description = "是否允许重复计算") + @NotNull(message="是否允许重复计算不能为空", groups = DefaultGroup.class) + private Boolean repeat; + @Schema(description = "计算偏移量(s)") + @NotNull(message="计算偏移量不能为空", groups = DefaultGroup.class) + private Long formulaOffset; + @Schema(description = "下一次计算结果预期时间") + @Null(message = "下一次计算结果预期时间不应存在数据", groups = AddGroup.class) + private Long nextTs; + @Schema(description = "计算频率(s)") + @NotNull(message="计算频率不能为空", groups = DefaultGroup.class) + private Long frequency; + @Schema(description = "最新结果信息") + @Null(message = "最新结果信息不应存在数据", groups = AddGroup.class) + private String resultInfo; + @Schema(description = "最新元素信息") + @JsonIgnore + private String elementInfo; + @Schema(description = "描述") + private String remark; + @Schema(description = "元素列表") + @NotNull(message="元素列表不能为空", groups = DefaultGroup.class) + List attributes; + + /** + * 计算异常列表 + */ + @JsonIgnore + private List calculationLogList; + + /** + * 临时存放时间 + */ + @JsonIgnore + private Long tempTs; + + @Schema(description = "结果属性id") + private String resultAttrId; + + @Schema(description = "排序") + private Long sort; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationJson.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationJson.java new file mode 100644 index 0000000..8d48198 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationJson.java @@ -0,0 +1,63 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.enumeration.CalculationStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author zhenghh. 2022-05-18 + **/ +@Data +public class CalculationJson { + + @Schema(description = "计算设置主键主键") + private Long calculationId; + @Schema(description = "异常计算主键") + private Long logId; + @Schema(description = "最新值") + private BigDecimal lastValue; + @Schema(description = "最新值时间") + private Long lastTime; + @Schema(description = "下一次计算时间") + private Long nextTime; + @Schema(description = "结果code") + private String resultCode; + @Schema(description = "结果属性code") + private String resultAttr; + @Schema(description = "存在默认值") + private Boolean existDefVal; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "计算数据类型") + private CalculationStatusEnum calculationStatus; + @Schema(description = "是否推送tb,当计算时间、结果值与上次计算一样时不推送tb") + private Boolean sendTb; + @Schema(description = "计算属性列表") + private List attrList; + + @Data + @Schema( name= "计算属性") + public static class Attribute { + + @Schema(description = "计算属性主键") + private Long id; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物属性") + private String thingAttr; + @Schema(description = "AA/AB/AC") + private String formulaLabel; + @Schema(description = "默认值") + private String defaultValue; + @Schema(description = "最新值") + private String lastValue; + @Schema(description = "最新值时间") + private Long lastTime; + @Schema(description = "是否默认值") + private Boolean defVal; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationParam.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationParam.java new file mode 100644 index 0000000..4601f87 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationParam.java @@ -0,0 +1,59 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.enumeration.CalculationStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author zhenghh. 2022-10-28 + **/ +@Data +@Schema( name= "待计算数据") +public class CalculationParam { + + @Schema(description = "计算设置主键") + private Long id; + @Schema(description = "计算日志主键") + private Long logId; + @Schema(description = "计算时间") + private Long ts; + @Schema(description = "结果物编码") + private String code; + @Schema(description = "结果物属性") + private String attrCode; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "计算属性列表") + private List attrList; + @Schema(description = "计算结果值") + private BigDecimal resultVal; + @Schema(description = "计算状态") + private CalculationStatusEnum calcStatus; + + @Data + @Schema( name= "计算属性") + @AllArgsConstructor + public static class Attr { + @Schema(description = "AA/AB/AC") + private String label; + @Schema(description = "当前值") + private BigDecimal value; + @Schema(description = "是否默认值") + private Boolean defVal; + } + + public CalculationParam(Long id, Long logId, Long ts, String code, String attrCode, String formula, List attrList) { + this.id = id; + this.logId = logId; + this.ts = ts; + this.code = code; + this.attrCode = attrCode; + this.formula = formula; + this.attrList = attrList; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationResultDTO.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationResultDTO.java new file mode 100644 index 0000000..b01bd10 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationResultDTO.java @@ -0,0 +1,22 @@ +package com.thing.calculation.calculation.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @author zhenghh. 2021-11-03 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CalculationResultDTO { + + private Long calculationId; + private String code; + private String attrCode; + private Long ts; + private BigDecimal value; +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationSettingCache.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationSettingCache.java new file mode 100644 index 0000000..c4c5644 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationSettingCache.java @@ -0,0 +1,40 @@ +package com.thing.calculation.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * @author zhenghh. 2022-05-05 + **/ +@Data +@Schema( name= "计算设置缓存") +public class CalculationSettingCache implements Serializable { + @Serial + private static final long serialVersionUID = -408948764488161500L; + + @Schema(description = "主键") + private String id; + @Schema(description = "计算属性列表") + private List attributes; + + @Data + @Schema( name= "计算属性") + public static class Attribute implements Serializable { + + @Serial + private static final long serialVersionUID = -1174193781245009846L; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物属性") + private String thingAttr; + @Schema(description = "AA/AB/AC") + private String formulaLabel; + @Schema(description = "默认值") + private String defaultValue; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialForm.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialForm.java new file mode 100644 index 0000000..d60c499 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialForm.java @@ -0,0 +1,48 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; + +/** + * 试算 + * @author zhenghh. 2022-05-06 + **/ +@Data +@Schema( name= "试算") +public class CalculationTrialForm { + + @Schema(description = "计算公式") + @NotBlank(message="计算公式不能为空", groups = DefaultGroup.class) + private String formula; + + @Schema(description = "计算元素列表") + @NotNull(message="计算元素列表不能为空", groups = DefaultGroup.class) + private List attrs; + + @Data + public static class Attr { + @Schema(description = "AA/AB/AC") + @NotBlank(message="元素编码不能为空", groups = DefaultGroup.class) + private String formulaLabel; + @Schema(description = "物主键") + @NotNull(message="物主键不能为空", groups = DefaultGroup.class) + private Long thingId; + @Schema(description = "物编码") + @NotBlank(message="物编码不能为空", groups = DefaultGroup.class) + private String thingCode; + @Schema(description = "物属性") + @NotBlank(message="物属性不能为空", groups = DefaultGroup.class) + private String thingAttr; + @Schema(description = "计算值") + //@NotNull(message="计算值不能为空", groups = DefaultGroup.class) + private BigDecimal defaultValue; + + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialResult.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialResult.java new file mode 100644 index 0000000..226a3f9 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationTrialResult.java @@ -0,0 +1,37 @@ +package com.thing.calculation.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * 试算结果 + * @author zhenghh. 2022-10-25 + **/ +@Data +@Schema( name= "试算结果") +public class CalculationTrialResult { + + @Schema(description = "试算结果") + private BigDecimal resultValue; + + @Schema(description = "结果时间") + private Long resultTs; + + @Schema(description = "计算元素列表") + private List attrs; + + @Data + public static class Attr { + @Schema(description = "AA/AB/AC") + private String formulaLabel; + @Schema(description = "计算时间") + private Long ts; + @Schema(description = "计算值") + private BigDecimal value; + + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationUsageDictRequest.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationUsageDictRequest.java new file mode 100644 index 0000000..4157781 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/CalculationUsageDictRequest.java @@ -0,0 +1,37 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; + +import lombok.Data; + +import java.util.List; + +@Data +@Schema(name = "用量属性计算请求") +public class CalculationUsageDictRequest { + + @Schema(description = "租户编码") + @NotNull(message = "租户编码不能为空", groups = DefaultGroup.class) + private Long tenantCode; + + @Schema(description = "物列表") + @NotEmpty(message = "物列表列表不能为空", groups = DefaultGroup.class) + private List thingCodeList; + + @Schema(description = "用量属性列表") + @NotEmpty(message = "用量属性列表不能为空", groups = DefaultGroup.class) + private List attrCodeList; + + @Schema(description = "开始时间, 格式 yyyy-MM-dd HH:mm:ss") + @NotNull(message = "开始时间不能为空", groups = DefaultGroup.class) + private String startTime; + + @Schema(description = "结束时间, 格式 yyyy-MM-dd HH:mm:ss") + @NotNull(message = "结束时间不能为空", groups = DefaultGroup.class) + private String endTime; +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/RecalculationForm.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/RecalculationForm.java new file mode 100644 index 0000000..0afca29 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/dto/RecalculationForm.java @@ -0,0 +1,31 @@ +package com.thing.calculation.calculation.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-05-27 + **/ +@Data +@Schema( name= "重新计算") +public class RecalculationForm { + + @Schema(description = "计算设置主键") + private List calculationIds; + + @Schema(description = "开始时间") + @NotNull(message = "开始时间不能为空", groups = DefaultGroup.class) + private Long startTime; + + @Schema(description = "结束时间") + @NotNull(message = "结束时间不能为空", groups = DefaultGroup.class) + private Long endTime; + + @Schema(description = "类型") + private String type; +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaAttributeEntity.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaAttributeEntity.java new file mode 100644 index 0000000..6328e9b --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaAttributeEntity.java @@ -0,0 +1,59 @@ +package com.thing.calculation.calculation.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("calculation_formula_attribute") +public class CalculationFormulaAttributeEntity extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 计算配置主键 + */ + private Long calculationId; + /** + * 物实体主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 物属性 + */ + private String thingAttr; + /** + * AA/AB/AC + */ + private String formulaLabel; + /** + * 默认值 + */ + private String defaultValue; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaLogEntity.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaLogEntity.java new file mode 100644 index 0000000..0ccabe2 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaLogEntity.java @@ -0,0 +1,67 @@ +package com.thing.calculation.calculation.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 计算异常日志表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +@Table("calculation_formula_log") +public class CalculationFormulaLogEntity extends BaseDateEntity implements Serializable { + @Serial + private static final long serialVersionUID = 6205627949756983894L; + /** + * 计算设置主键 + */ + private Long calculationId; + /** + * 计算时间 + */ + private Long calculationTs; + /** + * 计算状态(0获取子级元素失败,1默认值计算,2计算失败) + */ + private String calculationStatus; + /** + * 计算次数,超过指定次数不进行计算 + */ + private Integer calculationNum; + /** + * 计算日志 + */ + private String calculationInfo; + + public CalculationFormulaLogEntity(Long calculationId, Long calculationTs, String calculationInfo, String calculationStatus) { + this.calculationId = calculationId; + this.calculationTs = calculationTs; + this.calculationInfo = calculationInfo; + this.calculationStatus = calculationStatus; + this.calculationNum = 0; + } + + public CalculationFormulaLogEntity(Long id, String calculationInfo, String calculationStatus, Integer calculationNum) { + super(id); + this.calculationInfo = calculationInfo; + this.calculationStatus = calculationStatus; + this.calculationNum = calculationNum; + } + + public CalculationFormulaLogEntity(Long id, Integer calculationNum) { + super(id); + this.calculationNum = calculationNum; + } +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaSettingEntity.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaSettingEntity.java new file mode 100644 index 0000000..3862764 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/entity/CalculationFormulaSettingEntity.java @@ -0,0 +1,124 @@ +package com.thing.calculation.calculation.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("calculation_formula_setting") +public class CalculationFormulaSettingEntity extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 结果物主键 + */ + private Long resultId; + /** + * 结果物code + */ + private String resultCode; + /** + * 结果属性code + */ + private String resultAttr; + /** + * 计算公式中文 + */ + private String formulaName; + /** + * 计算公式 + */ + private String formula; + /** + * 计算类型 + */ + private String type; + /** + * 是否启用计算 + */ + private Boolean enable; + /** + * 是否启用默认值 + */ + private Boolean enableDefVal; + /** + * 是否允许重复计算 + */ + private Boolean repeat; + /** + * 下一次计算结果预期时间 + */ + private Long nextTs; + /** + * 计算频率 + */ + private Long frequency; + /** + * 计算偏移量 + */ + private Long formulaOffset; + /** + * 最新结果信息 + */ + private String resultInfo; + /** + * 最新元素信息 + */ + private String elementInfo; + /** + * 描述 + */ + private String remark; + /** + * 所属租户 + */ + private Long tenantCode; + /** + * 企业详情id + */ + private Long companyId; + /** + * 部门id + */ + private Long deptId; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 结果name + */ + @Column(ignore = true) + private String resultName; + /** + * 结果属性name + */ + @Column(ignore = true) + private String resultAttrName; + + private String resultAttrId; + + private Long sort; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaAttributeMapper.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaAttributeMapper.java new file mode 100644 index 0000000..543ed28 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaAttributeMapper.java @@ -0,0 +1,27 @@ +package com.thing.calculation.calculation.mapper; + +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.entity.CalculationFormulaAttributeEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Mapper +public interface CalculationFormulaAttributeMapper extends PowerBaseMapper { + + /** + * 获取列表 + * + * @param keys code+attr集合 + * @return list + */ + List getListByCodeAndAttr(@Param("keys") List keys); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaLogMapper.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaLogMapper.java new file mode 100644 index 0000000..965777c --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaLogMapper.java @@ -0,0 +1,25 @@ +package com.thing.calculation.calculation.mapper; + +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 计算异常日志表 +* +* @author zhenghh sunlightcs@gmail.com +* @since 3.0 2022-05-01 +*/ +@Mapper +public interface CalculationFormulaLogMapper extends PowerBaseMapper { + + /** + * 获取错误日志 + * @param params 查询 + * @return list + */ + List selectErrorList(Map params); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaSettingMapper.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaSettingMapper.java new file mode 100644 index 0000000..54866ca --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/mapper/CalculationFormulaSettingMapper.java @@ -0,0 +1,25 @@ +package com.thing.calculation.calculation.mapper; + +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 计算设置表 +* +* @author zhenghh sunlightcs@gmail.com +* @since 3.0 2022-05-01 +*/ +@Mapper +public interface CalculationFormulaSettingMapper extends PowerBaseMapper { + + /** + * 分页 + * @param params 查询参数 + * @return list + */ + List getList(Map params); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaAttributeService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaAttributeService.java new file mode 100644 index 0000000..1b12bef --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaAttributeService.java @@ -0,0 +1,47 @@ +package com.thing.calculation.calculation.service; + +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.entity.CalculationFormulaAttributeEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +public interface CalculationFormulaAttributeService extends IBaseService { + + /** + * 获取元素列表 + * + * @param calculationId 计算设置主键 + * @return list + */ + List getByCalculationId(Long calculationId); + + /** + * 获取元素列表 + * @param calculationIdList 计算设置主键集合 + * @return list + */ + List getByCalculationIdList(List calculationIdList); + + /** + * 批量保存 + * + * @param attributes 列表 + * @param calculationId 计算设置主键 + * @param enableDefVal 是否启用默认值 + */ + void saveList(List attributes, Long calculationId, Boolean enableDefVal); + + /** + * 获取元素列表 + * @param keys code + attr集合 + * @return list + */ + List getListByCodeAndAttr(List keys); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaLogService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaLogService.java new file mode 100644 index 0000000..5eaff7b --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaLogService.java @@ -0,0 +1,63 @@ +package com.thing.calculation.calculation.service; + +import com.thing.calculation.calculation.dto.CalculationFormulaLogDTO; +import com.thing.calculation.calculation.dto.CalculationJson; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 计算异常日志表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +public interface CalculationFormulaLogService extends IBaseService { + + + PageData page(Map params); + + /** + * 批量保存 + * @param logList 列表 + */ + void saveBatch(List logList); + + /** + * 获取日志 + * @param calculationJsonList 列表 + * @return list + */ + List selectListByCalcList(List calculationJsonList); + + /** + * 获取需计算列表 + * @param calculationNum 最大计算次数 + * @return list + */ + List getCalculationList(Integer calculationNum); + + /** + * 获取计算记录 + * + * @param times 时间 + * @param calculationIds 计算主键 + * @return list + */ + List selectByCalculationTimeAndIds(List times, List calculationIds); + + /** + * 日志保存 + * @param list 列表 + */ + void saveOrUpdateList(List list); + + /** + * 删除 + * @param calculationIds 计算设置主键 + */ + void deleteCalculationIdList(List calculationIds); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaSettingService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaSettingService.java new file mode 100644 index 0000000..b43d395 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationFormulaSettingService.java @@ -0,0 +1,47 @@ +package com.thing.calculation.calculation.service; + +import com.thing.calculation.calculation.dto.CalculationForm; +import com.thing.calculation.calculation.dto.CalculationFormulaSettingDTO; +import com.thing.calculation.calculation.dto.CalculationParam; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.tskv.dto.TsKvDTO; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +public interface CalculationFormulaSettingService extends IBaseService { + + PageData page(Map params); + + CalculationFormulaSettingDTO get(Long id); + + void save(CalculationFormulaSettingDTO dto); + + void update(CalculationFormulaSettingDTO dto); + + void delete(Long[] ids); + + List getEnableList(TriggerTypeEnum triggerType); + + List selectCacheListByIds(List ids, TriggerTypeEnum triggerType); + + List getTsKvList(List calculationList, LocalDateTime startTime, LocalDateTime endTime); + + List selectCalculationList(List times, List calculationIds, TriggerTypeEnum triggerType); + + CalculationParam getCalculationParam(CalculationFormulaSettingDTO setting, Long ts, List list); + + List sendTb(List calculationList); + + void updateBatchById(List calculationLastList); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationService.java new file mode 100644 index 0000000..6e1f82a --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationService.java @@ -0,0 +1,36 @@ +package com.thing.calculation.calculation.service; + +import com.thing.calculation.calculation.dto.*; +import com.thing.common.core.enumeration.TriggerTypeEnum; + +import java.util.List; + +/** + * @author zhenghh. 2022-05-06 + **/ +public interface CalculationService { + + /** + * 试算 + * + * @param form 试算数据 + * @return 试算结果 + */ + CalculationTrialResult trial(CalculationTrialForm form); + + /** + * + * + * @param form 时间 + */ + void recalculation(RecalculationForm form); + + /** + * 计算 + * + * @param calculationList 计算设置列表 + * @param socketDataList 待计算列表 + * @param triggerType 触发类型 + */ + void calculation(List calculationList, List socketDataList, TriggerTypeEnum triggerType); +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationUsageDictService.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationUsageDictService.java new file mode 100644 index 0000000..a80e81d --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/CalculationUsageDictService.java @@ -0,0 +1,7 @@ +package com.thing.calculation.calculation.service; + +import com.thing.calculation.calculation.dto.CalculationUsageDictRequest; + +public interface CalculationUsageDictService { + void recalculate(CalculationUsageDictRequest request); +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaAttributeServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaAttributeServiceImpl.java new file mode 100644 index 0000000..8c91bd6 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaAttributeServiceImpl.java @@ -0,0 +1,105 @@ +package com.thing.calculation.calculation.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.entity.CalculationFormulaAttributeEntity; +import com.thing.calculation.calculation.mapper.CalculationFormulaAttributeMapper; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Service +public class CalculationFormulaAttributeServiceImpl extends BaseServiceImpl implements CalculationFormulaAttributeService { + + @Autowired + private CalculationFormulaAttributeMapper calculationFormulaAttributeMapper; + + @Override + public QueryWrapper getWrapper(Map params) { + String calculationId = (String) params.get("calculationId"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(CalculationFormulaAttributeEntity::getCalculationId, StringUtils.isBlank(calculationId) ? -1L : Long.parseLong(calculationId)); + return wrapper; + } + + /** + * 获取元素列表 + * + * @param calculationId 计算设置主键 + * @return list + */ + @Override + public List getByCalculationId(Long calculationId) { + if (calculationId == null) { + return new ArrayList<>(); + } + List list = mapper.selectListByQuery(QueryWrapper.create() + .eq(CalculationFormulaAttributeEntity::getCalculationId, calculationId)); + return ConvertUtils.sourceToTarget(list, CalculationFormulaAttributeDTO.class); + } + + /** + * 获取元素列表 + * + * @param calculationIdList 计算设置主键集合 + * @return list + */ + @Override + public List getByCalculationIdList(List calculationIdList) { + if (CollectionUtil.isEmpty(calculationIdList)) { + return new ArrayList<>(); + } + List list = mapper.selectListByQuery(QueryWrapper.create() + .in(CalculationFormulaAttributeEntity::getCalculationId, calculationIdList)); + return ConvertUtils.sourceToTarget(list, CalculationFormulaAttributeDTO.class); + } + + /** + * 批量保存 + * + * @param attributes 列表 + * @param calculationId 计算设置主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveList(List attributes, Long calculationId, Boolean enableDefVal) { + if (enableDefVal && attributes.stream().anyMatch(attr -> StringUtils.isBlank(attr.getDefaultValue()))) { + throw new SysException("选择启用默认值时,默认值元素默认值为必填项"); + } + List attributeEntities = attributes.parallelStream().map(item -> { + CalculationFormulaAttributeEntity entity = ConvertUtils.sourceToTarget(item, CalculationFormulaAttributeEntity.class); + entity.setDefaultValue(enableDefVal ? entity.getDefaultValue() : null); + entity.setCalculationId(calculationId); + return entity; + }).collect(Collectors.toList()); + mapper.insertBatch(attributeEntities); + } + + /** + * 获取元素列表 + * + * @param keys code + attr集合 + * @return list + */ + @Override + public List getListByCodeAndAttr(List keys) { + return calculationFormulaAttributeMapper.getListByCodeAndAttr(keys); + } +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaLogServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaLogServiceImpl.java new file mode 100644 index 0000000..e03cd61 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaLogServiceImpl.java @@ -0,0 +1,168 @@ +package com.thing.calculation.calculation.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.calculation.calculation.dto.CalculationFormulaLogDTO; +import com.thing.calculation.calculation.dto.CalculationJson; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.calculation.calculation.entity.table.CalculationFormulaLogEntityTableDef; +import com.thing.calculation.calculation.mapper.CalculationFormulaLogMapper; +import com.thing.calculation.calculation.service.CalculationFormulaLogService; +import com.thing.common.core.enumeration.CalculationStatusEnum; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 计算异常日志表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Slf4j +@Service +public class CalculationFormulaLogServiceImpl extends BaseServiceImpl implements CalculationFormulaLogService { + + private static final CalculationFormulaLogEntityTableDef ACCOUNT = new CalculationFormulaLogEntityTableDef(); + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + private List getExistedList(List logList) { + QueryWrapper queryWrapper = QueryWrapper.create(); + logList.forEach( item -> + queryWrapper.and(ACCOUNT.CALCULATION_ID.eq(item.getCalculationId()).or(ACCOUNT.CALCULATION_TS.eq(item.getCalculationTs()))) + ); + return mapper.selectListByQuery(queryWrapper); + } + + @Override + public PageData page(Map params) { + Long page = MapUtil.getLong(params, "page"); + Long limit = MapUtil.getLong(params, "limit"); + QueryWrapper wrapper = getWrapper(params); + Page paginate = mapper.paginate(page, limit, wrapper); + return new PageData<>(ConvertUtils.sourceToTarget(paginate.getRecords(),CalculationFormulaLogDTO.class),paginate.getTotalRow()); + } + + /** + * 批量保存 + * + * @param logList 列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveBatch(List logList) { + if (CollectionUtil.isEmpty(logList)) { + return; + } + List existedList = getExistedList(logList); + List collect = logList.parallelStream() + .filter(item -> existedList.parallelStream() + .noneMatch(exist -> Objects.equals(item.getCalculationId(), exist.getCalculationId()) && Objects.equals(item.getCalculationTs(), exist.getCalculationTs()))) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(collect)) { + mapper.insertBatch(collect); + } + } + + /** + * 获取日志 + * + * @param calculationJsonList 列表 + * @return list + */ + @Override + public List selectListByCalcList(List calculationJsonList) { + List calculationIdList = calculationJsonList.stream().map(CalculationJson::getCalculationId).distinct().collect(Collectors.toList()); + List tsList = calculationJsonList.stream().map(CalculationJson::getLastTime).distinct().collect(Collectors.toList()); + return selectByCalculationTimeAndIds(tsList, calculationIdList); + } + + /** + * 获取需计算列表 + * + * @param calculationNum 最大计算次数 + * @return list + */ + @Override + public List getCalculationList(Integer calculationNum) { + Map params = Maps.newHashMap(); + params.put("status", CalculationStatusEnum.FAIL.getValue()); + params.put("type", TriggerTypeEnum.JOB.getValue()); + params.put("successStatus", CalculationStatusEnum.SUCCESS.getValue()); + if (calculationNum != null) { + params.put("num", calculationNum); + } + return mapper.selectErrorList(params); + } + + /** + * 获取计算记录 + * + * @param times 时间 + * @param calculationIds 计算主键 + * @return list + */ + @Override + public List selectByCalculationTimeAndIds(List times, List calculationIds) { + if (CollectionUtil.isEmpty(times) || CollectionUtil.isEmpty(calculationIds)) { + return Lists.newArrayList(); + } + return mapper.selectListByQuery(QueryWrapper.create() + .in(CalculationFormulaLogEntity::getCalculationTs, times) + .in(CalculationFormulaLogEntity::getCalculationId, calculationIds)); + } + + /** + * 日志保存 + * + * @param list 列表 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdateList(List list) { + if(CollectionUtil.isEmpty(list)) { + return; + } + List insertList = list.stream().filter(item -> Objects.isNull(item.getId())).collect(Collectors.toList()); + List updateList = list.stream().filter(item -> Objects.nonNull(item.getId())).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(insertList)) { + mapper.insertBatch(insertList); + } + if(CollectionUtil.isNotEmpty(updateList)) { + for (CalculationFormulaLogEntity calculationFormulaLogEntity : updateList) { + mapper.update(calculationFormulaLogEntity); + } + } + } + + /** + * 删除 + * + * @param calculationIds 计算设置主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteCalculationIdList(List calculationIds) { + if (CollectionUtil.isEmpty(calculationIds)) { + return; + } + mapper.deleteByQuery(QueryWrapper.create().in(CalculationFormulaLogEntity::getCalculationId, calculationIds)); + } +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaSettingServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaSettingServiceImpl.java new file mode 100644 index 0000000..760c245 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationFormulaSettingServiceImpl.java @@ -0,0 +1,455 @@ +package com.thing.calculation.calculation.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.api.FormulaInvokeService; +import com.thing.calculation.calculation.dto.*; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.calculation.calculation.mapper.CalculationFormulaSettingMapper; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.calculation.calculation.service.CalculationFormulaLogService; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.CalculationStatusEnum; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.event.CacheCalculationEvent; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.dto.TelemetrySaveDTO; +import com.thing.common.data.dto.TsKvReqParam; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import com.thing.transport.util.ConvertSendUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 计算设置表 + * + * @author zhenghh sunlightcs@gmail.com + * @since 3.0 2022-05-01 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CalculationFormulaSettingServiceImpl extends BaseServiceImpl implements CalculationFormulaSettingService { + + private final CalculationFormulaAttributeService attributeService; + private final CalculationFormulaLogService logService; + private final TskvService tsKvService; + private final FormulaInvokeService formulaInvokeService; + private final ApplicationEventPublisher applicationEventPublisher; + private final ConvertSendUtil convertSendUtil; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like(CalculationFormulaSettingEntity::getResultCode, name,StringUtils.isNotBlank(name)); + return wrapper; + } + + @Override + public PageData page(Map params) { + //转换成like + paramsToLike(params, "name"); + //查询 + params.put("enable", StringUtils.isNotBlank((String) params.get("enable")) + ? Boolean.valueOf((String) params.get("enable")) : null); + params.put("repeat", StringUtils.isNotBlank((String) params.get("repeat")) + ? Boolean.valueOf((String) params.get("repeat")) : null); + params.put("defVal", StringUtils.isNotBlank((String) params.get("defVal")) + ? Boolean.valueOf((String) params.get("defVal")) : null); + //分页 + // Page page = getPage(params, "cfs.create_date", false); + Page page = getPage(params); + List list = mapper.getList(params); + return getPageData(list, page.getTotalRow(), CalculationFormulaSettingDTO.class); + } + + @Override + public CalculationFormulaSettingDTO get(Long id) { + CalculationFormulaSettingEntity entity = mapper.selectOneById(id); + if (entity == null) { + throw new SysException("未找到数据"); + } + List attributes = attributeService.getByCalculationId(id); + if (StringUtils.isNotBlank(entity.getElementInfo())) { + List list = JSON.parseArray(entity.getElementInfo(), JSONObject.class); + attributes = attributes.parallelStream() + .peek(attr -> list.stream().filter(item -> StringUtils.equals(attr.getFormulaLabel(), item.getString("formulaLabel"))) + .findFirst().ifPresent(item -> { + attr.setLastValue(item.getString(Constant.LAST_VALUE)); + attr.setLastTime(item.getString(Constant.LAST_TIME)); + })).collect(Collectors.toList()); + } + CalculationFormulaSettingDTO dto = ConvertUtils.sourceToTarget(entity, CalculationFormulaSettingDTO.class); + dto.setAttributes(attributes); + return dto; + } + + /** + * 入库及保存缓存 + * + * @param dto 计算信息 + */ + @Override + @DataFilter(completeDataMark = false) + @Transactional(rollbackFor = Exception.class) + public void save(CalculationFormulaSettingDTO dto) { + if (CollectionUtil.isEmpty(dto.getAttributes())) { + throw new SysException("元素列表不能为空"); + } + if (!formulaCheck(dto.getFormula(), dto.getAttributes())) { + throw new SysException("计算公式不合法"); + } + long id = new SnowFlakeIDKeyGenerator().nextId(); + dto.setId(id); + //入库 + attributeService.saveList(dto.getAttributes(), id, dto.getEnableDefVal()); + //触发一次计算 + calculation(dto); + super.saveDto(dto); + if (dto.getEnable() && StringUtils.equals(dto.getType(), TriggerTypeEnum.SOCKET.getValue())) { + //放入缓存 + List keyList = dto.getAttributes().stream() + .map(item -> new CacheCalculationEvent.Attr(item.getThingCode(), item.getThingAttr())).collect(Collectors.toList()); + applicationEventPublisher.publishEvent(new CacheCalculationEvent(this, keyList)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(CalculationFormulaSettingDTO dto) { + if (CollectionUtil.isEmpty(dto.getAttributes())) { + throw new SysException("元素列表不能为空"); + } + if (!formulaCheck(dto.getFormula(), dto.getAttributes())) { + throw new SysException("计算公式不合法"); + } + CalculationFormulaSettingEntity entity = mapper.selectOneById(dto.getId()); + if (entity == null) { + throw new SysException("未找到数据"); + } + //频率修改后获取最新时间 + if (!Objects.equals(entity.getFrequency(), dto.getFrequency()) && entity.getNextTs() != null) { + Long lastTs = getLastTs(dto.getAttributes()); + if (lastTs > 0) { + dto.setNextTs(lastTs); + } + } + //先删除 + List attributeList = attributeService.getByCalculationId(dto.getId()); + List keyList = attributeList.stream() + .map(item -> new CacheCalculationEvent.Attr(item.getThingCode(), item.getThingAttr())).collect(Collectors.toList()); + attributeService.removeByIds(attributeList); + //入库 + attributeService.saveList(dto.getAttributes(), dto.getId(), dto.getEnableDefVal()); + //触发一次计算 + calculation(dto); + + CalculationFormulaSettingEntity calculationFormulaSettingEntity = ConvertUtils.sourceToTarget(dto, CalculationFormulaSettingEntity.class); + super.updateById(calculationFormulaSettingEntity); + //置空nextTs + if (TriggerTypeEnum.SOCKET.getValue().equals(dto.getType())) { + calculationFormulaSettingEntity.setNextTs(null); + mapper.update(calculationFormulaSettingEntity); + } + //放入缓存 + keyList.addAll(dto.getAttributes().stream() + .filter(item -> keyList.parallelStream().noneMatch(old -> StringUtils.equals(old.getCode(), item.getThingCode()) && StringUtils.equals(old.getAttr(), item.getThingAttr()))) + .map(item -> new CacheCalculationEvent.Attr(item.getThingCode(), item.getThingAttr())) + .toList()); + applicationEventPublisher.publishEvent(new CacheCalculationEvent(this, keyList)); + } + + /** + * 校验公式是否合法 + * + * @param formula 公式 + * @param attributes 计算项 + * @return boolean + */ + public Boolean formulaCheck(String formula, List attributes) { + try { + for (CalculationFormulaAttributeDTO attribute : attributes) { + formula = formula.replace(attribute.getFormulaLabel(), "1"); + } + if (StringUtils.isNotBlank(formula)) { + formulaInvokeService.evalFormula(formula).execute(); + return true; + } + } catch (Exception e) { + log.error("保存试算失败: {}", e.getMessage()); + } + return false; + } + + private void calculation(CalculationFormulaSettingDTO dto) { + try { + LocalDateTime startTime = null, endTime = null; + if (TriggerTypeEnum.JOB.getValue().equals(dto.getType()) && Objects.nonNull(dto.getNextTs())) { + LocalDateTime dateTime = LocalDateTimeUtil.of(dto.getNextTs()); + startTime = dateTime.minusSeconds(1); + endTime = dateTime.plusSeconds(1); + } + List tsKvList = getTsKvList(Lists.newArrayList(dto), startTime, endTime); + if (CollectionUtil.isNotEmpty(tsKvList)) { + Optional max = tsKvList.parallelStream().map(TsKvDTO::getTs).max(Comparator.naturalOrder()); + if (!max.isPresent()) { + return; + } + List collect = tsKvList.parallelStream() + .map(tsKv -> new CalculationForm(tsKv.getThingCode(), tsKv.getThingAttr(), tsKv.getTs(), new BigDecimal(tsKv.getVal()))) + .collect(Collectors.toList()); + CalculationParam calculationParam = getCalculationParam(dto, max.get(), collect); + List elementList = dto.getAttributes().stream() + .map(attribute -> tsKvList.parallelStream() + .filter(tsKv -> StringUtils.equals(attribute.getThingCode(), tsKv.getThingCode()) && StringUtils.equals(attribute.getThingAttr(), tsKv.getThingAttr())) + .findFirst().map(tsKv -> new CalculationAttr(attribute.getFormulaLabel(), new BigDecimal(tsKv.getVal()), tsKv.getTs())).orElse(null)) + .filter(Objects::nonNull).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(elementList)) { + dto.setElementInfo(JSONObject.toJSONString(elementList)); + } + if (Objects.nonNull(calculationParam.getResultVal())) { + JSONObject result = new JSONObject(); + result.put(Constant.RESULT_TIME, calculationParam.getTs()); + result.put(Constant.RESULT_VALUE, calculationParam.getResultVal().toPlainString()); + dto.setResultInfo(result.toJSONString()); + //推送tb + sendTb(Lists.newArrayList(calculationParam)); + } + } + } catch (Exception e) { + log.error("保存/修改计算失败: {}", e.getMessage()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除元素 + List calculationIds = Arrays.asList(ids); + List attributeList = attributeService.getByCalculationIdList(calculationIds); + List keyList = attributeList.stream() + .map(item -> new CacheCalculationEvent.Attr(item.getThingCode(), item.getThingAttr())).collect(Collectors.toList()); + attributeService.removeByIds(attributeList); + //删除日志 + logService.deleteCalculationIdList(calculationIds); + //删除设置 + super.removeByIds(Arrays.asList(ids)); + //放入缓存 + applicationEventPublisher.publishEvent(new CacheCalculationEvent(this, keyList)); + } + + @Override + public List getEnableList(TriggerTypeEnum triggerType) { + return mapper.selectListByQuery(QueryWrapper.create() + .eq(CalculationFormulaSettingEntity::getType, triggerType.getValue()) + .eq(CalculationFormulaSettingEntity::getEnable, true)); + } + + /** + * 获取最新时间 + * + * @param attributes 计算元素 + * @return 最新时间 + */ + private Long getLastTs(List attributes) { + //按物编码分组 + Map> collect = attributes.parallelStream() + .collect(Collectors.groupingBy(item -> item.getThingId() + "," + item.getThingCode(), + Collectors.mapping(CalculationFormulaAttributeDTO::getThingAttr, Collectors.toList()))); + return tsKvService.getLastTimeSeries(collect).parallelStream() + .mapToLong(TsKvDTO::getTs).max().orElse(0); + } + + /** + * 获取列表 + * + * @param ids 主键集合 + * @return list + */ + @Override + public List selectCacheListByIds(List ids, TriggerTypeEnum triggerType) { + if (CollectionUtil.isEmpty(ids)) { + return new ArrayList<>(); + } + + return mapper.selectListByQuery(QueryWrapper.create().in(CalculationFormulaSettingEntity::getId, ids) + .eq(CalculationFormulaSettingEntity::getType, triggerType.getValue(), true) + .eq(CalculationFormulaSettingEntity::getEnable, true)); + } + + /** + * 获取数据 + * + * @param calculationList 计算设置 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTsKvList(List calculationList, LocalDateTime startTime, LocalDateTime endTime) { + Map> thingMap = calculationList.parallelStream() + .flatMap(item -> item.getAttributes().stream()) + .collect(Collectors.groupingBy(item -> item.getThingId() + "," + item.getThingCode(), Collectors.mapping(CalculationFormulaAttributeDTO::getThingAttr, Collectors.toList()))); + return Objects.isNull(startTime) || Objects.isNull(endTime) + ? tsKvService.getLastTimeSeries(thingMap) + : tsKvService.getTimeSeries(thingMap, LocalDateTimeUtil.toEpochMilli(startTime), LocalDateTimeUtil.toEpochMilli(endTime)); + } + + /** + * 计算列表 + * + * @param times 时间 + * @param calculationIds 计算设置主键 + * @param triggerType 触发方式 + * @return list + */ + @Override + public List selectCalculationList(List times, List calculationIds, TriggerTypeEnum triggerType) { + List calculationList = selectCacheListByIds(calculationIds, triggerType); + if (CollectionUtil.isEmpty(calculationList)) { + return Lists.newArrayList(); + } + List collect = calculationList.stream().map(CalculationFormulaSettingEntity::getId).collect(Collectors.toList()); + List attributeList = attributeService.getByCalculationIdList(collect); + List logEntityList = logService.selectByCalculationTimeAndIds(times, collect); + return calculationList.parallelStream().map(item -> { + CalculationFormulaSettingDTO dto = ConvertUtils.sourceToTarget(item, CalculationFormulaSettingDTO.class); + dto.setTempTs(dto.getNextTs()); + dto.setAttributes(attributeList.stream().filter(attr -> Objects.equals(attr.getCalculationId(), item.getId())).collect(Collectors.toList())); + dto.setCalculationLogList(logEntityList.stream().filter(log -> Objects.equals(log.getCalculationId(), item.getId())).collect(Collectors.toList())); + return dto; + }).collect(Collectors.toList()); + } + + /** + * 计算数据组装 + * + * @param setting 计算设置 + * @param ts 时间 + * @param list 计算值 + * @return 计算数据 + */ + @Override + public CalculationParam getCalculationParam(CalculationFormulaSettingDTO setting, Long ts, List list) { + CalculationFormulaLogEntity formulaLogEntity = CollectionUtil.isEmpty(setting.getCalculationLogList()) ? null + : setting.getCalculationLogList().stream().filter(log -> Objects.equals(ts, log.getCalculationTs())).findFirst().orElse(null); + List infoList = Objects.isNull(formulaLogEntity) ? Lists.newArrayList() : JSONArray.parseArray(formulaLogEntity.getCalculationInfo(), CalculationParam.Attr.class); + List attrs = setting.getAttributes().parallelStream().map(attr -> { + Optional dataOptional = list.stream() + .filter(data -> StringUtils.equals(attr.getThingCode(), data.getCode()) && StringUtils.equals(attr.getThingAttr(), data.getAttr())) + .findFirst(); + //socket数据 + if (dataOptional.isPresent()) { + return new CalculationParam.Attr(attr.getFormulaLabel(), dataOptional.get().getValue(), false); + } + //历史数据 + if (CollectionUtil.isNotEmpty(infoList)) { + //不为默认值 + Optional first = infoList.stream().filter(info -> !info.getDefVal() && StringUtils.equals(info.getLabel(), attr.getFormulaLabel())).findFirst(); + if (first.isPresent()) { + return new CalculationParam.Attr(attr.getFormulaLabel(), first.get().getValue(), false); + } + } + //使用默认值 //todo 判断第三次,赋值为0 + if (setting.getEnableDefVal()) { + return new CalculationParam.Attr(attr.getFormulaLabel(), new BigDecimal(attr.getDefaultValue()), true); + } + return null; + }).filter(Objects::nonNull).collect(Collectors.toList()); + CalculationParam calculationParam = new CalculationParam(setting.getId(), Objects.isNull(formulaLogEntity) ? null : formulaLogEntity.getId(), + ts, setting.getResultCode(), setting.getResultAttr(), setting.getFormula(), attrs); + if (!Objects.equals(attrs.size(), setting.getAttributes().size())) { + calculationParam.setCalcStatus(CalculationStatusEnum.MISS); + } else { + calculation(calculationParam); + if (attrs.stream().anyMatch(CalculationParam.Attr::getDefVal)) { + calculationParam.setCalcStatus(CalculationStatusEnum.DEFAULT); + } + } + return calculationParam; + } + + /** + * 计算 + * + * @param calculationParam 计算 + */ + private void calculation(CalculationParam calculationParam) { + Map env = Maps.newHashMapWithExpectedSize(calculationParam.getAttrList().size()); + try { + calculationParam.getAttrList().forEach(attr -> env.put(attr.getLabel(), attr.getValue())); + calculationParam.setResultVal(formulaInvokeService.invokeFormula(calculationParam.getFormula(), env, 4)); + calculationParam.setCalcStatus(CalculationStatusEnum.SUCCESS); + } catch (Exception e) { + log.error("{},{}计算失败: {}", calculationParam.getFormula(), env, e.getMessage()); + calculationParam.setCalcStatus(CalculationStatusEnum.ERROR); + } + } + + /** + * 推送tb + * + * @param calculationList 计算结果列表 + */ + @Override + public List sendTb(List calculationList) { + List errorIdList = new ArrayList<>(); + if (CollectionUtil.isEmpty(calculationList)) { + return errorIdList; + } + Map> resultList = calculationList.parallelStream() + .filter(item -> item.getResultVal() != null) + .collect(Collectors.groupingBy(CalculationParam::getCode, Collectors.mapping(item -> { + Map map = Maps.newHashMapWithExpectedSize(1); + map.put(item.getAttrCode(), item.getResultVal()); + return new TsKvReqParam(item.getTs(), map); + }, Collectors.toList()))); + resultList.forEach((thingCode, list) -> { + try { +// if(deviceDataFacade.checkTbLink()){ +// deviceDataFacade.saveEntityTelemetryByRuleEngine(Lists.newArrayList(new TelemetrySaveDTO(thingCode, list))); +// } + //发送至本地队列 + convertSendUtil.convertPushMsg(Lists.newArrayList(new TelemetrySaveDTO(thingCode, list))); + } catch (Exception e) { + List collect = calculationList.parallelStream() + .filter(item -> StringUtils.equals(thingCode, item.getCode()) && CalculationStatusEnum.SUCCESS.equals(item.getCalcStatus())) + .map(CalculationParam::getId).distinct().toList(); + errorIdList.addAll(collect); + } + }); + return errorIdList; + } + + @Override + public void updateBatchById(List calculationLastList) { + for (CalculationFormulaSettingEntity calculationFormulaSettingEntity : calculationLastList) { + mapper.update(calculationFormulaSettingEntity); + } + } +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationServiceImpl.java new file mode 100644 index 0000000..53a6a7f --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationServiceImpl.java @@ -0,0 +1,453 @@ +package com.thing.calculation.calculation.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.api.FormulaInvokeService; +import com.thing.calculation.calculation.dto.*; +import com.thing.calculation.calculation.entity.CalculationFormulaAttributeEntity; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.calculation.calculation.service.CalculationFormulaLogService; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.calculation.calculation.service.CalculationService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.CalculationStatusEnum; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.dto.TelemetrySaveDTO; +import com.thing.common.data.dto.TsKvReqParam; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import com.thing.transport.util.ConvertSendUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-05-06 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class CalculationServiceImpl implements CalculationService { + + private final CalculationFormulaAttributeService formulaAttributeService; + private final CalculationFormulaSettingService formulaSettingService; + private final CalculationFormulaLogService logService; + private final FormulaInvokeService formulaInvokeService; + private final TskvService tsKvService; + private final ConvertSendUtil convertSendUtil; + /** + * 试算 + * + * @param form 试算数据 + * @return 试算结果 + */ + @Override + public CalculationTrialResult trial(CalculationTrialForm form) { + try { + List attrs = new ArrayList<>(); + Map env = Maps.newHashMapWithExpectedSize(form.getAttrs().size()); + Map> listMap = form.getAttrs().stream() + .filter(item -> item.getDefaultValue() == null) + .collect(Collectors.groupingBy(item -> item.getThingId() + "," + item.getThingCode(), Collectors.mapping(CalculationTrialForm.Attr::getThingAttr, Collectors.toList()))); + List tsKvDTOList = tsKvService.getLastTimeSeries(listMap); + form.getAttrs().forEach(item -> { + CalculationTrialResult.Attr attr = new CalculationTrialResult.Attr(); + attr.setFormulaLabel(item.getFormulaLabel()); + BigDecimal defaultValue = item.getDefaultValue(); + if (defaultValue == null) { + Optional first = tsKvDTOList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), item.getThingCode()) && StringUtils.equals(tsKv.getThingAttr(), item.getThingAttr())) + .findFirst(); + if (first.isPresent()) { + TsKvDTO tsKvDTO = first.get(); + defaultValue = new BigDecimal(tsKvDTO.getVal()); + attr.setTs(tsKvDTO.getTs()); + } + } + env.put(item.getFormulaLabel(), defaultValue); + attr.setValue(defaultValue); + attrs.add(attr); + }); + CalculationTrialResult result = new CalculationTrialResult(); + OptionalLong max = attrs.stream().filter(item -> item.getTs() != null).mapToLong(CalculationTrialResult.Attr::getTs).max(); + if (max.isPresent()) { + result.setResultTs(max.getAsLong()); + } + result.setResultValue(formulaInvokeService.invokeFormula(form.getFormula(), env, 4)); + result.setAttrs(attrs); + return result; + } catch (Exception e) { + log.error("计算失败: {}", e.getMessage()); + throw new SysException("计算失败,请检查参数是否合法"); + } + } + + + /** + * 重新计算 + * + * @param form 时间 + */ + @Override + public void recalculation(RecalculationForm form) { + List calculationList = getCalculationList(form.getCalculationIds(),form.getType()); + LocalDateTime startTime = DateTimeUtils.parseDateTime(form.getStartTime() - 1); + LocalDateTime endTime = DateTimeUtils.parseDateTime(form.getEndTime()); + while (startTime.isBefore(endTime)) { + LocalDateTime tempTime = startTime.plusDays(1); + if (tempTime.compareTo(endTime) > 0) { + tempTime = endTime; + } + //计算 + List list = getCalculationTsKvList(calculationList, startTime, tempTime); + convertSendUtil.convertPushMsg(list); +// if(deviceDataFacade.checkTbLink()){ +// //推送tb +// deviceDataFacade.saveEntityTelemetryByRuleEngine(list); +// } + startTime = tempTime; + } + } + + /** + * 计算 + * + * @param calculationList 计算设置 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + private List getCalculationTsKvList(List calculationList, LocalDateTime startTime, LocalDateTime endTime) { + //获取数据 + List tsKvList = formulaSettingService.getTsKvList(calculationList, startTime, endTime); + //存在时间列表 + //计算 + return calculationList.parallelStream().map(item -> { + List existTsKvList = tsKvList.parallelStream() + .filter(tsKv -> item.getAttributes().stream() + .anyMatch(attr -> StringUtils.equals(tsKv.getThingCode(), attr.getThingCode()) && StringUtils.equals(tsKv.getThingAttr(), attr.getThingAttr()))) + .collect(Collectors.toList()); + if (CollectionUtil.isEmpty(existTsKvList)) { + //启用默认值使用默认值计算 + return defaultCalculation(item, startTime, endTime); + } + List tsKvReqParams = getTsKvParams(existTsKvList, item.getAttributes(), item.getEnableDefVal(), item.getFormula(), item.getResultAttr()); + if (CollectionUtil.isEmpty(tsKvReqParams)) { + return null; + } + return new TelemetrySaveDTO(item.getResultCode(), tsKvReqParams); + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + + /** + * 默认值计算 + * + * @param item 计算设置 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + private TelemetrySaveDTO defaultCalculation(CalculationFormulaSettingDTO item, LocalDateTime startTime, LocalDateTime endTime) { + if (item.getEnableDefVal()) { + //时间列表 + List timeList = DateTimeUtils.getIntervalTimeList(startTime, endTime, (int) (item.getFrequency() / 60)); + List collect = timeList.parallelStream() + .map(time -> { + Map value = calculation(item.getAttributes(), item.getFormula(), item.getResultAttr()); + return CollectionUtil.isEmpty(value) ? null : new TsKvReqParam(time, value); + }).filter(Objects::nonNull).sorted(Comparator.comparing(TsKvReqParam::getTs)).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(collect)) { + return null; + } + return new TelemetrySaveDTO(item.getResultCode(), collect); + } + return null; + } + + /** + * 计算 + * + * @param tsKvList tb数据 + * @param attributes 元素列表 + * @param enableDefVal 是否启用默认值 + * @param formula 公式 + * @param attrCode 结果属性 + * @return list + */ + private List getTsKvParams(List tsKvList, List attributes, Boolean enableDefVal, String formula, String attrCode) { + List timeList = tsKvList.parallelStream().map(TsKvDTO::getTs).distinct().collect(Collectors.toList()); + return timeList.parallelStream().map(time -> { + List existTsKvList = tsKvList.parallelStream().filter(exist -> Objects.equals(exist.getTs(), time)).collect(Collectors.toList()); + Map value = calculation(existTsKvList, attributes, enableDefVal, formula, attrCode); + return CollectionUtil.isEmpty(value) ? null : new TsKvReqParam(time, value); + }).filter(Objects::nonNull).sorted(Comparator.comparing(TsKvReqParam::getTs)).collect(Collectors.toList()); + } + + /** + * 计算 + * + * @param tsKvList 数据列表 + * @param attributes 元素列表 + * @param enableDefVal 是否启用默认值 + * @param formula 公式 + * @param attrCode 结果属性 + * @return list + */ + private Map calculation(List tsKvList, List attributes, Boolean enableDefVal, String formula, String attrCode) { + Map env = Maps.newHashMapWithExpectedSize(attributes.size()); + attributes.forEach(attr -> { + TsKvDTO cacheTsKv = tsKvList.parallelStream() + .filter(tsKv -> StringUtils.equals(attr.getThingCode(), tsKv.getThingCode()) && StringUtils.equals(attr.getThingAttr(), tsKv.getThingAttr())) + .findFirst().orElse(null); + //使用默认值 + if (cacheTsKv == null && enableDefVal) { + env.put(attr.getFormulaLabel(), new BigDecimal(attr.getDefaultValue())); + } else if (cacheTsKv != null) { + env.put(attr.getFormulaLabel(), new BigDecimal(cacheTsKv.getVal())); + } + }); + //都存在计算 + if (env.size() == attributes.size()) { + try { + Map result = Maps.newHashMapWithExpectedSize(1); + result.put(attrCode, formulaInvokeService.invokeFormula(formula, env, 4)); + return result; + } catch (Exception e) { + log.error("{},{}计算失败: {}", formula, env, e.getMessage()); + } + } + return null; + } + + /** + * 默认值计算 + * + * @param attributes 元素列表 + * @param formula 公式 + * @param attrCode 结果属性 + * @return map + */ + private Map calculation(List attributes, String formula, String attrCode) { + Map env = Maps.newHashMapWithExpectedSize(attributes.size()); + try { + attributes.forEach(attr -> env.put(attr.getFormulaLabel(), new BigDecimal(attr.getDefaultValue()))); + Map result = Maps.newHashMapWithExpectedSize(1); + result.put(attrCode, formulaInvokeService.invokeFormula(formula, env, 4)); + return result; + } catch (Exception e) { + log.error("{},{}计算失败: {}", formula, env, e.getMessage()); + } + return null; + } + + /** + * 计算设置 + * + * @param ids 主键集合 + * @return list + */ + private List getCalculationList(List ids,String type) { + List list; + if (CollectionUtil.isNotEmpty(ids)) { + list = formulaSettingService.listByIds(ids); + } else if (StringUtils.isNotEmpty(type)){ + list = formulaSettingService.list(QueryWrapper.create().eq(CalculationFormulaSettingEntity::getEnable, true)); + }else { + Long tenantCode = UserContext.getTenantCode(); + list = formulaSettingService.list(QueryWrapper.create().eq(CalculationFormulaSettingEntity::getEnable, true) + .eq(CalculationFormulaSettingEntity::getTenantCode, tenantCode)); + } + if (CollectionUtil.isEmpty(list)) { + throw new SysException("未找到计算数据"); + } + List attributeList = getByCalculationIdList(list); + return list.parallelStream().map(item -> { + CalculationFormulaSettingDTO dto = ConvertUtils.sourceToTarget(item, CalculationFormulaSettingDTO.class); + dto.setAttributes(attributeList.parallelStream().filter(attr -> Objects.equals(dto.getId(), attr.getCalculationId())).collect(Collectors.toList())); + return dto; + }).collect(Collectors.toList()); + } + + + private List getByCalculationIdList(List settingEntityList) { + if (CollectionUtil.isEmpty(settingEntityList)) { + return new ArrayList<>(); + } + List list = formulaAttributeService.list(QueryWrapper.create().in(CalculationFormulaAttributeEntity::getCalculationId, + settingEntityList.parallelStream().map(CalculationFormulaSettingEntity::getId).collect(Collectors.toList()))); + return ConvertUtils.sourceToTarget(list, CalculationFormulaAttributeDTO.class); + } + + /** + * 计算 + * + * @param calculationList 计算设置列表 + * @param socketDataList 待计算列表 + * @param triggerType 触发类型 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void calculation(List calculationList, List socketDataList, TriggerTypeEnum triggerType) { + //计算数据组装 + List calculationParamList = calculationList.parallelStream() + .flatMap(setting -> convertCalculationParam(setting, socketDataList, triggerType)) + .collect(Collectors.toList()); + //error调用不需要保存最新值 + if (!TriggerTypeEnum.ERROR.equals(triggerType) && CollectionUtil.isNotEmpty(socketDataList)) { + //最新值保存 + List calculationLastList = convertCalculationLast(calculationList, calculationParamList, socketDataList); + //保存最新值 + formulaSettingService.updateBatchById(calculationLastList); + } + //推送tb + List errorIdList = formulaSettingService.sendTb(calculationParamList); + + saveLog(calculationParamList,errorIdList); + } + + + /** + * 保存物计算日志 + * @param calculationParamList + * @param errorIdList + */ + private void saveLog(List calculationParamList,List errorIdList){ + //保存日志 + List logEntityList = calculationParamList.stream() + .map(item -> { + if (errorIdList.contains(item.getId())) { + item.setCalcStatus(CalculationStatusEnum.FAIL); + } + CalculationFormulaLogEntity entity = new CalculationFormulaLogEntity(); + entity.setId(item.getLogId()); + //不修改计算次数 + if (Objects.isNull(item.getLogId())) { + entity.setCalculationId(item.getId()); + entity.setCalculationNum(0); + } + if(Objects.isNull(item.getTs())){ + entity.setCalculationTs(System.currentTimeMillis()); + }else { + entity.setCalculationTs(item.getTs()); + } + entity.setCalculationStatus(item.getCalcStatus().getValue()); + entity.setCalculationInfo(JSONObject.toJSONString(item.getAttrList())); + return entity; + }).collect(Collectors.toList()); + logService.saveOrUpdateList(logEntityList); + } + + /** + * 计算 + * + * @param setting 计算设置 + * @param socketDataList 数据列表 + * @param triggerType 触发类型 + * @return 计算结果 + */ + private Stream convertCalculationParam(CalculationFormulaSettingDTO setting, List socketDataList, TriggerTypeEnum triggerType) { + //按时间分组 + Map> socketDataMap = socketDataList.parallelStream() + .filter(data -> setting.getAttributes().stream() + .anyMatch(attr -> StringUtils.equals(attr.getThingCode(), data.getCode()) && StringUtils.equals(attr.getThingAttr(), data.getAttr()))) + .collect(Collectors.groupingBy(CalculationForm::getTs)); + if(CollectionUtil.isEmpty(socketDataMap)) { + return Stream.of(formulaSettingService.getCalculationParam(setting, setting.getTempTs(), Lists.newArrayList())); + } + //job只计算最新值 + if(TriggerTypeEnum.JOB.equals(triggerType)) { + return socketDataMap.keySet().stream() + .max(Comparator.naturalOrder()) + .map(aLong -> Stream.of(formulaSettingService.getCalculationParam(setting, aLong, socketDataMap.get(aLong)))) + .orElseGet(() -> Stream.of(formulaSettingService.getCalculationParam(setting, setting.getTempTs(), Lists.newArrayList()))); + } + //socket、error类型计算所有 + List result = new ArrayList<>(); + socketDataMap.forEach((ts, list) -> result.add(formulaSettingService.getCalculationParam(setting, ts, list))); + return result.stream(); + } + + /** + * 最新值 + * + * @param calculationList 计算设置列表 + * @param calculationParamList 计算结果列表 + * @param socketDataList 数据列表 + * @return 最新结果 + */ + private List convertCalculationLast(List calculationList, List calculationParamList, List socketDataList) { + //最新值 + Map lastValMap = socketDataList.parallelStream() + .collect(Collectors.toMap(item -> item.getCode() + item.getAttr(), Function.identity(), BinaryOperator.maxBy(Comparator.comparing(CalculationForm::getTs)))); + //最新结果 + Map lastResultMap = calculationParamList.parallelStream() + .filter(item -> Objects.nonNull(item.getResultVal()) && Objects.nonNull(item.getTs())) + .collect(Collectors.toMap(item -> item.getCode() + item.getAttrCode(), Function.identity(), BinaryOperator.maxBy(Comparator.comparing(CalculationParam::getTs)))); + return calculationList.parallelStream() + .map(setting -> { + CalculationFormulaSettingEntity entity = new CalculationFormulaSettingEntity(); + entity.setId(setting.getId()); + //结果值 + CalculationParam calculationParam = lastResultMap.get(setting.getResultCode() + setting.getResultAttr()); + if (Objects.nonNull(calculationParam)) { + JSONObject resultInfo = StringUtils.isNotBlank(setting.getResultInfo()) ? JSON.parseObject(setting.getResultInfo()) : null; + if (Objects.isNull(resultInfo) || resultInfo.getLong(Constant.RESULT_TIME) <= calculationParam.getTs()) { + JSONObject result = new JSONObject(); + result.put(Constant.RESULT_TIME, calculationParam.getTs()); + result.put(Constant.RESULT_VALUE, calculationParam.getResultVal().toPlainString()); + entity.setResultInfo(result.toJSONString()); + } + } + //元素值 + List elementList = StringUtils.isBlank(setting.getElementInfo()) ? Lists.newArrayList() + : JSON.parseArray(setting.getElementInfo(), CalculationAttr.class).stream().filter(Objects::nonNull).collect(Collectors.toList()); + List attrList = setting.getAttributes().stream() + .filter(attr -> lastValMap.containsKey(attr.getThingCode() + attr.getThingAttr())) + .map(attr -> { + CalculationForm socketData = lastValMap.get(attr.getThingCode() + attr.getThingAttr()); + return new CalculationAttr(attr.getFormulaLabel(), socketData.getValue(), socketData.getTs()); + }).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(elementList)) { + entity.setElementInfo(JSONObject.toJSONString(attrList)); + return entity; + } + //不存在 + List list = attrList.stream() + .filter(attr -> elementList.parallelStream().noneMatch(element -> StringUtils.equals(element.getFormulaLabel(), attr.getFormulaLabel()))) + .collect(Collectors.toList()); + //存在的比较时间 + list.addAll(elementList.stream() + .peek(element -> attrList.stream() + .filter(attr -> StringUtils.equals(element.getFormulaLabel(), attr.getFormulaLabel())).findFirst() + .ifPresent(attr -> { + if (Objects.isNull(element.getLastTime()) || element.getLastTime() <= attr.getLastTime()) { + element.setLastTime(attr.getLastTime()); + element.setLastValue(attr.getLastValue()); + } + })).collect(Collectors.toList())); + entity.setElementInfo(JSONObject.toJSONString(list)); + return entity; + }).collect(Collectors.toList()); + } + +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationUsageDictServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationUsageDictServiceImpl.java new file mode 100644 index 0000000..5403e39 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/calculation/service/impl/CalculationUsageDictServiceImpl.java @@ -0,0 +1,38 @@ +package com.thing.calculation.calculation.service.impl; + +import com.thing.calculation.calculation.dto.CalculationUsageDictRequest; +import com.thing.calculation.calculation.service.CalculationUsageDictService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.tskv.service.TsKvService; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CalculationUsageDictServiceImpl implements CalculationUsageDictService { + private final TsKvService tskvService; + + @Override + public void recalculate(CalculationUsageDictRequest request) { + List thingCodeList = request.getThingCodeList(); + + Map> thingAttrMap = + thingCodeList.stream() + .collect( + Collectors.toMap( + thingCode -> thingCode, + thingCode -> request.getAttrCodeList())); + String startTime = request.getStartTime(); + String endTime = request.getEndTime(); + Long startTs = DateTimeUtils.convertTimeToLong(startTime); + Long endTs = DateTimeUtils.convertTimeToLong(endTime); + assert startTs != null && endTs != null; + tskvService.amSumMaps(thingAttrMap, startTs, endTs); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/controller/CalcLogController.java b/modules/calculation/src/main/java/com/thing/calculation/controller/CalcLogController.java new file mode 100644 index 0000000..66b29f7 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/controller/CalcLogController.java @@ -0,0 +1,80 @@ +package com.thing.calculation.controller; + +import com.thing.calculation.dto.CalcLogDTO; +import com.thing.calculation.service.CalcLogService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 物计算日志表 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@RestController +@RequestMapping("v2/calculation/log") +@Tag(name="物计算日志表") +@RequiredArgsConstructor +public class CalcLogController { + + private final CalcLogService calcLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeywords", description = "模糊搜索关键词:物编码、物名称"), + @Parameter(name = "attrKeywords", description = "模糊搜索关键词:物属性名称、物属性编码"), + @Parameter(name = "status", description = "1-已计算,2-未计算,3-计算异常,4-不匹配"), + @Parameter(name = "configType", description = "计算类型:1,2,3,4, 5"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = calcLogService.handlePage(params); + + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + calcLogService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出excel") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "keyword", description = "模糊搜索关键词:物编码、物名称"), + @Parameter(name = "status", description = "1-已计算,2-未计算,3-计算异常,4-不匹配"), + @Parameter(name = "configType", description = "计算类型:1,2,3,4, 5"), + }) + public void exportExcel(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) { + calcLogService.exportExcel(params, response); + } + + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/controller/CalcTargetConfigController.java b/modules/calculation/src/main/java/com/thing/calculation/controller/CalcTargetConfigController.java new file mode 100644 index 0000000..1cac672 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/controller/CalcTargetConfigController.java @@ -0,0 +1,165 @@ +package com.thing.calculation.controller; + +import com.thing.calculation.dto.*; +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** +* 物计算(目标物)配置 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@RestController +@RequestMapping("v2/calculation/config") +@Tag(name="物计算配置") +@RequiredArgsConstructor +public class CalcTargetConfigController { + + private final CalcTargetConfigService calcTargetConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "targetThingKeyword", description = "结果对象关键词"), + @Parameter(name = "targetAttrKeyword", description = "结果属性关键词"), + @Parameter(name = "configType", description = "配置类型:1-数据映射、2-聚合映射、3-指标计算、4-高级计算、5-分摊计算", required = true), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = calcTargetConfigService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CalcTargetConfigDTO data = calcTargetConfigService.getDetail(id); + return new Result().ok(data); + } + + @GetMapping("cache") + @Operation(summary = "获取缓存数据【该接口用于调试,前端不要调用】") + public Result> getAllEnabled() { + List data = calcTargetConfigService.getAllEnabled(); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CalcTargetConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + calcTargetConfigService.handleSave(dto); + return new Result<>(); + } + + @PostMapping("batch/save") + @Operation(summary="批量新增, 在聚合映射和指标计算中使用") + @LogOperation("批量新增") + public Result batchSave(@RequestBody CalcTargetConfigDTO[] dtos){ + calcTargetConfigService.handleBatchSave(Arrays.asList(dtos)); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CalcTargetConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + calcTargetConfigService.handleUpdate(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + calcTargetConfigService.handleDelete(Arrays.asList(ids)); + return new Result<>(); + } + + @DeleteMapping("delete/sourceConfig") + @Operation(summary="删除计算物配置") + @LogOperation("删除计算物配置") + public Result deleteSourceConfig(@RequestBody Long[] sourceConfigId){ + if(sourceConfigId.length == 0){ + return new Result<>(); + } + calcTargetConfigService.deleteSourceConfig(sourceConfigId[0]); + return new Result<>(); + } + + @PostMapping("test/calc") + @Operation(summary="测试计算") + @LogOperation("测试计算") + public Result testCalc(@RequestBody TestCalcRequest request){ + TestCalcResponse data = calcTargetConfigService.testCalc(request); + return new Result().ok(data); + } + + @PostMapping("recalculate") + @Operation(summary="重新计算") + @LogOperation("重新计算") + public Result reCalculate(@RequestBody ReCalcRequest request){ + calcTargetConfigService.reCalculate(request); + return new Result<>(); + } + + @PostMapping("template/download") + @Operation(summary="模板下载") + @LogOperation("模板下载") + public void templateDownload(HttpServletResponse response) { + calcTargetConfigService.templateDownload(response); + } + + @PostMapping("import") + @Operation(summary="导入excel") + public Result importExcel(@RequestParam("file") MultipartFile file) { + calcTargetConfigService.importExcel(file); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出excel") + @Parameters({ + @Parameter(name = "targetThingName", description = "物名称"), + @Parameter(name = "targetThingCode", description = "物编码"), + @Parameter(name = "configType", description = "配置类型:1-数据映射、2-聚合映射、3-指标计算、4-高级计算、5-分摊计算", required = true), + }) + public void exportExcel(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) { + calcTargetConfigService.exportExcel(params, response); + } + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/CalcLogDTO.java b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcLogDTO.java new file mode 100644 index 0000000..d9f526a --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcLogDTO.java @@ -0,0 +1,72 @@ +package com.thing.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 物计算日志表 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Data +@Schema(description = "物计算日志表") +public class CalcLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "计算目标物配置表id") + private Long calcTargetConfigId; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "计算源信息,json格式") + private String sourceInfo; + @Schema(description = "计算状态:1-已计算、2-未计算、3-计算异常、4-不匹配") + private Integer status; + @Schema(description = "数据时间") + private Long time; + @Schema(description = "计算结果,只有已计算的记录才有值") + private String result; + @Schema(description = "计算耗时") + private Long duration; + @Schema(description = "错误信息") + private String errorInfo; + @Schema(description = "计算次数") + private String calcCount; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "目标物名称") + private String targetThingName; + + @Schema(description = "目标物编码") + private String targetThingCode; + + @Schema(description = "目标属性名称") + private String targetAttrName; + + @Schema(description = "目标属性编码") + private String targetAttrCode; + + @Schema(description = "计算类型") + private String configType; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/CalcSourceConfigDTO.java b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcSourceConfigDTO.java new file mode 100644 index 0000000..8912cad --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcSourceConfigDTO.java @@ -0,0 +1,44 @@ +package com.thing.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 计算物配置 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Data +@Accessors(chain = true) +@Schema(description = "物计算:计算对象配置") +public class CalcSourceConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "计算目标物配置表id") + private Long calcTargetConfigId; + @Schema(description = "源物名称") + private String sourceThingName; + @Schema(description = "源物编码") + private String sourceThingCode; + @Schema(description = "源属性名称") + private String sourceAttrName; + @Schema(description = "源属性编码") + private String sourceAttrCode; + /** 涉及到计算公式的逻辑为了更易于理解,有时也用variable来给该字段取名 */ + @Schema(description = "源属性在计算公式中的别名") + private String sourceAttrAlias; + + private Long tenantCode; + private Long companyId; + private Long deptId; + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/CalcTargetConfigDTO.java b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcTargetConfigDTO.java new file mode 100644 index 0000000..30aad68 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/CalcTargetConfigDTO.java @@ -0,0 +1,48 @@ +package com.thing.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 物计算(目标物)配置 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Data +@Accessors(chain = true) +@Schema(description = "物计算:目标对象配置") +public class CalcTargetConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "配置类型:1-数据映射、2-聚合映射、3-指标计算、4-高级计算") + private Integer configType; + @Schema(description = "目标物名称") + private String targetThingName; + @Schema(description = "目标物编码") + private String targetThingCode; + @Schema(description = "目标属性名称") + private String targetAttrName; + @Schema(description = "目标属性编码") + private String targetAttrCode; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "计算公式中文描述") + private String formulaDescription; + @Schema(description = "备注") + private String remark; + @Schema(description = "是否启用计算") + private Boolean enable; + + @Schema(description = "公式中的计算物信息") + public List sourceConfigs; +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/ExecuteCalcRequest.java b/modules/calculation/src/main/java/com/thing/calculation/dto/ExecuteCalcRequest.java new file mode 100644 index 0000000..fe9ca8c --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/ExecuteCalcRequest.java @@ -0,0 +1,37 @@ +package com.thing.calculation.dto; + +import com.thing.common.core.utils.DateTimeUtils; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * @author siyang + * @date 2024-03-07 + * @description 执行计算请求 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "执行计算请求") +public class ExecuteCalcRequest { + + @Schema(description = "计算范围:开始时间") + private String startTime; + + @Schema(description = "计算范围:结束时间") + private String endTime; + + public Long getStartTs(){ + return DateTimeUtils.convertTimeToLong(startTime); + } + + public Long getEndTs(){ + return DateTimeUtils.convertTimeToLong(endTime); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/LogSourceInfoValue.java b/modules/calculation/src/main/java/com/thing/calculation/dto/LogSourceInfoValue.java new file mode 100644 index 0000000..2cfbbad --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/LogSourceInfoValue.java @@ -0,0 +1,28 @@ +package com.thing.calculation.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024-03-05 + * @description + * 物计算日志sourceInfo为map形式的jsonString + * 该类为map中的value格式 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LogSourceInfoValue { + private String thingCode; + private String attrCode; + private String value; + + public BigDecimal convertVal(){ + return new BigDecimal(value); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/ReCalcRequest.java b/modules/calculation/src/main/java/com/thing/calculation/dto/ReCalcRequest.java new file mode 100644 index 0000000..cf6341c --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/ReCalcRequest.java @@ -0,0 +1,39 @@ +package com.thing.calculation.dto; + +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-11 + * @description 重新计算请求 + */ +@Data +@Accessors(chain = true) +@Schema(description = "重新(手动)计算请求") +public class ReCalcRequest { + @Schema(description = "计算范围:开始时间") + private String startTime; + + @Schema(description = "计算范围:结束时间") + private String endTime; + + @Schema(description = "目标对象配置id列表") + private List calcTargetConfigIds; + + public List getCalcTargetConfigIds() { + return calcTargetConfigIds.stream().distinct().toList(); + } + + public Long getStartTs(){ + return DateTimeUtils.convertTimeToLong(startTime); + } + + public Long getEndTs(){ + return DateTimeUtils.convertTimeToLong(endTime); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcRequest.java b/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcRequest.java new file mode 100644 index 0000000..72f13e2 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcRequest.java @@ -0,0 +1,50 @@ +package com.thing.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-06 + * @description 测试计算请求 + */ +@Data +@Schema(description = "物计算日志表") +public class TestCalcRequest { + + @Schema(description = "公式表达式") + private String formula; + + @Schema(description = "需要测试的数据元信息") + private List calcMetaData; + + @Data + public static class TestCalcMetaData { + private String thingCode; + private String attrCode; + private String alias; + } + + public Set obtainThingCodes() { + return calcMetaData.stream() + .map(TestCalcMetaData::getThingCode) + .collect(Collectors.toSet()); + } + + public Set obtainAttrCodes() { + return calcMetaData.stream().map(TestCalcMetaData::getAttrCode).collect(Collectors.toSet()); + } + + public Map metaDataToMap() { + return calcMetaData.stream() + .collect( + Collectors.toMap( + e -> e.getThingCode() + ":" + e.getAttrCode(), + TestCalcMetaData::getAlias)); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcResponse.java b/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcResponse.java new file mode 100644 index 0000000..58724a6 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/dto/TestCalcResponse.java @@ -0,0 +1,27 @@ +package com.thing.calculation.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-06 + * @description 测试计算响应 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "物计算日志表") +public class TestCalcResponse { + + @Schema(description = "计算结果") + private String result; + + @Schema(description = "数据时间") + private String time; +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/entity/CalcLogEntity.java b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcLogEntity.java new file mode 100644 index 0000000..3a3cc26 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcLogEntity.java @@ -0,0 +1,195 @@ +package com.thing.calculation.entity; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.calculation.dto.CalcSourceConfigDTO; +import com.thing.calculation.dto.LogSourceInfoValue; +import com.thing.calculation.enumeration.CalcStatus; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 物计算日志表 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Data +@Slf4j +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("calc_log") +public class CalcLogEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 计算目标物配置表id + */ + private Long calcTargetConfigId; + /** + * 计算公式 + */ + private String formula; + /** + * 计算源信息,json格式 + */ + private String sourceInfo; + /** + * 计算状态:1-已计算、2-未计算、3-计算异常、4-不匹配 + * {@link com.thing.calculation.enumeration.CalcStatus} + */ + private Integer status; + /** + * 数据时间 + */ + private Long time; + /** + * 计算结果,只有已计算的记录才有值 + */ + private String result; + /** + * 计算耗时 + */ + private Long duration; + /** + * 错误信息 + */ + private String errorInfo; + /** + * 计算次数 + */ + private Integer calcCount; + + @Column(ignore = true) + private Integer configType; + + private static final TypeReference> typeRef = new TypeReference<>() {}; + + /** + * 往sourceInfo中追加计算物属性信息和属性值 + * + * @param sourceConfig 计算物属性配置 + * @param value 计算物属性值 + * 注意:这里允许value为空,表示本次添加时值为空。 + * 后期计算时,若还为空,需要查库,若库中仍然为空就不计算 + * @return 日志对象 + */ + public CalcLogEntity appendSourceInfo(CalcSourceConfigDTO sourceConfig, String value) { + if (Objects.isNull(sourceConfig)) { + return this; + } + Map data = parseSourceInfo(); + putData(data, sourceConfig, value); + sourceInfo = JSONObject.toJSONString(data); + return this; + } + + private void putData(Map data, CalcSourceConfigDTO sourceConfig, String value){ + data.put( + sourceConfig.getSourceAttrAlias(), + new LogSourceInfoValue( + sourceConfig.getSourceThingCode(), + sourceConfig.getSourceAttrCode(), + value)); + } + + /** + * 合并两个日志对象: + * 当前对象可能是实时数据生成的日志对象,也可能是数据库中已有的旧对象; + * another则是实时数据生成的日志对象,其状态为“未匹配” + * 1. 主要合并两个对象的 sourceInfo + * 2. 当两个对象的状态不同时:依据refreshStatus来判断是否更新状态 + * + * @param another 要被合并的日志 + * @param refreshStatus 是否更新状态:主要用于已计算的日志,重新改写成未匹配,让其重新再计算一遍 + * @return 合并后的对象 + */ + public CalcLogEntity mergeLog(CalcLogEntity another, boolean refreshStatus) { + if (Objects.isNull(another) || StringUtils.isBlank(another.getSourceInfo())) { + return this; + } + if (!Objects.equals(calcTargetConfigId, another.getCalcTargetConfigId()) + || !Objects.equals(time, another.getTime())) { + return this; + } + if (refreshStatus) { + setStatus(another.status); + } + return checkToMerge(another.getSourceInfo()); + } + + /** + * 校验是否需要变更状态以及merge sourceInfo + * 1. 如果 sourceInfoStr 中有新的物属性 则初始化状态,并merge source + * 2. 如果 sourceInfoStr 的value与当前log的value有差异,则初始化状态,并merge source + * + * @param sourceInfoStr 待合并的sourceInfo + * @return 变更后的当前log对象 + */ + private CalcLogEntity checkToMerge(String sourceInfoStr) { + if(StringUtils.isBlank(sourceInfoStr)){ + return this; + } + Map currentSourceInfo = parseSourceInfo(); + Map anotherSourceInfo = parseSourceInfo(sourceInfoStr); + anotherSourceInfo.forEach( + (alias, sourceInfoObj) -> { + if (!currentSourceInfo.containsKey(alias) + || !Objects.equals( + currentSourceInfo.get(alias).getValue(), + anotherSourceInfo.get(alias).getValue())) { + setStatus(CalcStatus.MISMATCH.getCode()); + currentSourceInfo.put(alias, sourceInfoObj); + } + }); + setSourceInfo(JSONObject.toJSONString(currentSourceInfo)); + return this; + } + + /** + * 获取当前日志的sourceInfo与formula所涉及的变量之间的差集 + * + * @return sourceInfo与formula变量差集 + */ + public Set getMissingVariables() { + Map data = parseSourceInfo(); + Set aliases = data.keySet(); + Set formulaVariables = FormulaUtil.getFormulaVariables(getFormula()); + return formulaVariables.stream() + .filter(variable -> !aliases.contains(variable)) + .collect(Collectors.toSet()); + } + + public Map parseSourceInfo() { + if (StringUtils.isBlank(sourceInfo)) { + return new HashMap<>(); + } + return JSONObject.parseObject(getSourceInfo(), typeRef); + } + + private Map parseSourceInfo(String sourceInfo) { + if (StringUtils.isBlank(sourceInfo)) { + return new HashMap<>(); + } + return JSONObject.parseObject(sourceInfo, typeRef); + } + + public void increaseCalcCount() { + setCalcCount(getCalcCount() + 1); + } +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/entity/CalcSourceConfigEntity.java b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcSourceConfigEntity.java new file mode 100644 index 0000000..f04c1c2 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcSourceConfigEntity.java @@ -0,0 +1,52 @@ +package com.thing.calculation.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 计算物配置实体 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("calc_source_config") +public class CalcSourceConfigEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 计算目标物配置表id + */ + private Long calcTargetConfigId; + /** + * 源物名称 + */ + private String sourceThingName; + /** + * 源物编码 + */ + private String sourceThingCode; + /** + * 源属性名称 + */ + private String sourceAttrName; + /** + * 源属性编码 + */ + private String sourceAttrCode; + /** + * 源属性在计算公式中的别名 + * 涉及到计算公式的逻辑为了更易于理解,有时也用variable来给该字段取名 + */ + private String sourceAttrAlias; +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/entity/CalcTargetConfigEntity.java b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcTargetConfigEntity.java new file mode 100644 index 0000000..54ffb89 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/entity/CalcTargetConfigEntity.java @@ -0,0 +1,63 @@ +package com.thing.calculation.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 物计算(目标物)配置 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("calc_target_config") +public class CalcTargetConfigEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 配置类型:数据映射、聚合映射、指标计算、高级计算 + * {@link com.thing.calculation.enumeration.CalcConfigType} + */ + private Integer configType; + /** + * 目标物名称 + */ + private String targetThingName; + /** + * 目标物编码 + */ + private String targetThingCode; + /** + * 目标属性名称 + */ + private String targetAttrName; + /** + * 目标属性编码 + */ + private String targetAttrCode; + /** + * 计算公式 + */ + private String formula; + /** + * 计算公式中文描述 + */ + private String formulaDescription; + /** + * 备注 + */ + private String remark; + /** + * 是否启用计算 + */ + private Boolean enable; +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcConfigType.java b/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcConfigType.java new file mode 100644 index 0000000..6e90eda --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcConfigType.java @@ -0,0 +1,67 @@ +package com.thing.calculation.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; +import java.util.Optional; + +/** 物计算配置类型 */ +@Getter +@AllArgsConstructor +public enum CalcConfigType { + /** 数据映射 */ + DATA_MAPPING(1, "数据映射"), + + /** 聚合映射 */ + AGG_MAPPING(2, "聚合映射"), + + /** 指标计算 */ + IND_CALC(3, "指标计算"), + + /** 高级计算、复杂计算 */ + COMPLEX_CALC(4, "高级计算"), + + /** + * 简单分摊计算:只配置诸如 A * 10% 这种公式 + * 虽然所有功能都可通过指标计算完成,为了业务上更加清晰还是增加了此类型 + * 对于稍复杂的公式配置就通过指标计算来配置和实现功能 + */ + SHARE_CALC(5, "分摊计算"); + + private final int code; + private final String name; + + public static boolean isDataMapping(Integer code) { + return code == DATA_MAPPING.code; + } + + public static boolean isAggMapping(Integer code) { + return code == AGG_MAPPING.code; + } + + public static boolean isMapping(Integer code) { + return isDataMapping(code) || isAggMapping(code); + } + + public static int getCodeByName(String name) { + Optional calcConfigTypeOpt = + Arrays.stream(CalcConfigType.values()) + .filter(item -> Objects.equals(item.getName(), name)) + .findFirst(); + return calcConfigTypeOpt.map(CalcConfigType::getCode).orElse(-1); + } + + public static String getNameByCode(Integer code) { + CalcConfigType configType = getByCode(code); + return Objects.isNull(configType) ? null : configType.getName(); + } + + public static CalcConfigType getByCode(Integer code) { + return Arrays.stream(CalcConfigType.values()) + .filter(item -> Objects.equals(item.getCode(), code)) + .findFirst() + .orElse(null); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcStatus.java b/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcStatus.java new file mode 100644 index 0000000..49961fb --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/enumeration/CalcStatus.java @@ -0,0 +1,36 @@ +package com.thing.calculation.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** 物计算日志状态 */ +@Getter +@AllArgsConstructor +public enum CalcStatus { + /** 已计算 */ + CALCULATED(1), + + /** 未计算(已匹配完成) */ + UN_CALCULATED(2), + + /** 计算异常 */ + CALCULATE_EXCEPTION(3), + + /** 不匹配 */ + MISMATCH(4); + + private final int code; + + public static CalcStatus getByCode(Integer statusCode) { + return Arrays.stream(CalcStatus.values()) + .filter(status -> status.getCode() == statusCode) + .findFirst() + .orElse(null); + } + + public static boolean isCalculated(Integer status) { + return status == CALCULATED.getCode(); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/excel/CalcConfigExcel.java b/modules/calculation/src/main/java/com/thing/calculation/excel/CalcConfigExcel.java new file mode 100644 index 0000000..7d89db8 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/excel/CalcConfigExcel.java @@ -0,0 +1,99 @@ +package com.thing.calculation.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.calculation.dto.CalcSourceConfigDTO; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.enumeration.CalcConfigType; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 用于数据导入 + */ +@Data +public class CalcConfigExcel { + + @Excel(name = "计算类型", orderNum = "1") + private String calcType; + + @Excel(name = "结果物名称", orderNum = "2", width = 18) + private String targetThingName; + + @Excel(name = "结果物编码", orderNum = "3", width = 18) + private String targetThingCode; + + @Excel(name = "结果属性名称", orderNum = "4", width = 18) + private String targetAttrName; + + @Excel(name = "结果属性编码", orderNum = "5", width = 15) + private String targetAttrCode; + + @Excel(name = "计算公式", orderNum = "6", width = 18) + private String formula; + + @Excel(name = "计算物名称", orderNum = "7", width = 18) + private String sourceThingName; + + @Excel(name = "计算物编码", orderNum = "8", width = 18) + private String sourceThingCode; + + @Excel(name = "计算物属性名称", orderNum = "9", width = 18) + private String sourceAttrName; + + @Excel(name = "计算物属性编码", orderNum = "10", width = 15) + private String sourceAttrCode; + + @Excel(name = "计算物属性别名", orderNum = "11", width = 15) + private String sourceAttrAlias; + + public static List toConfigDTO(List excelDataList) { + if (excelDataList.isEmpty()) { + return Collections.emptyList(); + } + // 按计算类型、物编码、公式来唯一确定一个物计算配置 + Function groupKey = + e -> e.getCalcType() + ":" + e.getTargetThingCode() + ":" + e.getFormula(); + Map> configGroup = + excelDataList.stream().collect(Collectors.groupingBy(groupKey)); + + List targetConfigs = new ArrayList<>(configGroup.size()); + configGroup.forEach( + (k, v) -> { + CalcTargetConfigDTO targetConfigDTO = v.get(0).toTargetConfigDTO(); + targetConfigDTO.setSourceConfigs( + v.stream().map(CalcConfigExcel::toSourceConfigDTO).toList()); + targetConfigs.add(targetConfigDTO); + }); + return targetConfigs; + } + + private CalcTargetConfigDTO toTargetConfigDTO() { + CalcTargetConfigDTO targetConfig = new CalcTargetConfigDTO(); + targetConfig + .setConfigType(CalcConfigType.getCodeByName(calcType)) + .setTargetThingCode(targetThingCode) + .setTargetThingName(targetThingName) + .setTargetAttrName(targetAttrName) + .setTargetAttrCode(targetAttrCode) + .setFormula(formula); + return targetConfig; + } + + private CalcSourceConfigDTO toSourceConfigDTO() { + CalcSourceConfigDTO sourceConfig = new CalcSourceConfigDTO(); + sourceConfig + .setSourceThingCode(sourceThingCode) + .setSourceThingName(sourceThingName) + .setSourceAttrName(sourceAttrName) + .setSourceAttrCode(sourceAttrCode) + .setSourceAttrAlias(sourceAttrAlias); + return sourceConfig; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/excel/CalcLogExcel.java b/modules/calculation/src/main/java/com/thing/calculation/excel/CalcLogExcel.java new file mode 100644 index 0000000..3aa5407 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/excel/CalcLogExcel.java @@ -0,0 +1,61 @@ +package com.thing.calculation.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.calculation.dto.CalcLogDTO; +import com.thing.calculation.dto.CalcSourceConfigDTO; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.enumeration.CalcConfigType; + +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 异常日志excel + */ +@Data +public class CalcLogExcel { + + @Excel(name = "计算类型", orderNum = "1") + private String calcType; + + @Excel(name = "结果物名称", orderNum = "2", width = 18) + private String targetThingName; + + @Excel(name = "结果物编码", orderNum = "3", width = 18) + private String targetThingCode; + + @Excel(name = "结果属性名称", orderNum = "4", width = 18) + private String targetAttrName; + + @Excel(name = "结果属性编码", orderNum = "5", width = 15) + private String targetAttrCode; + + @Excel(name = "异常信息", orderNum = "6", width = 20) + private String errorInfo; + + @Excel(name = "计算生成事件", orderNum = "7", width = 18) + private String createTime; + + @Excel(name = "数据异常时间", orderNum = "8", width = 18) + private String dataTime; + + public static List convertFromDTO(List dtoList) { + return dtoList.stream().map(CalcLogExcel::convertFromDTO).collect(Collectors.toList()); + } + + private static CalcLogExcel convertFromDTO(CalcLogDTO dto) { + CalcLogExcel excel = ConvertUtils.sourceToTarget(dto, CalcLogExcel.class); + excel.setCreateTime(DateTimeUtils.timestamp2Str(dto.getCreateDate())); + excel.setDataTime(DateTimeUtils.timestamp2Str(dto.getTime())); + return excel; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/excel/CalculationTypeExcel.java b/modules/calculation/src/main/java/com/thing/calculation/excel/CalculationTypeExcel.java new file mode 100644 index 0000000..88c1706 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/excel/CalculationTypeExcel.java @@ -0,0 +1,44 @@ +package com.thing.calculation.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.enumeration.CalcConfigType; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** 用于导出映射类型excel: 数据映射,聚合映射 */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CalculationTypeExcel { + @Excel(name = "计算类型", orderNum = "1") + private String calcType; + + @Excel(name = "结果物名称", orderNum = "2", width = 18) + private String targetThingName; + + @Excel(name = "结果物编码", orderNum = "3", width = 18) + private String targetThingCode; + + @Excel(name = "结果属性名称", orderNum = "4", width = 18) + private String targetAttrName; + + @Excel(name = "结果属性编码", orderNum = "5", width = 18) + private String targetAttrCode; + + @Excel(name = "计算公式", orderNum = "6", width = 18) + private String formulaDescription; + + public static CalculationTypeExcel convertFromDto(CalcTargetConfigDTO targetConfig) { + return new CalculationTypeExcel( + CalcConfigType.getNameByCode(targetConfig.getConfigType()), + targetConfig.getTargetThingName(), + targetConfig.getTargetThingCode(), + targetConfig.getTargetAttrName(), + targetConfig.getTargetAttrCode(), + targetConfig.getFormulaDescription()); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/excel/MappingTypeExcel.java b/modules/calculation/src/main/java/com/thing/calculation/excel/MappingTypeExcel.java new file mode 100644 index 0000000..1b9e6f3 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/excel/MappingTypeExcel.java @@ -0,0 +1,56 @@ +package com.thing.calculation.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.enumeration.CalcConfigType; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.util.List; +import java.util.stream.Collectors; + +/** 用于导出映射类型excel: 指标计算,高级计算 */ +@Data +@Accessors(chain = true) +public class MappingTypeExcel { + @Excel(name = "计算类型", orderNum = "1") + private String calcType; + + @Excel(name = "结果物名称", orderNum = "2", width = 18) + private String targetThingName; + + @Excel(name = "结果物编码", orderNum = "3", width = 18) + private String targetThingCode; + + @Excel(name = "结果属性名称", orderNum = "4", width = 18) + private String targetAttrName; + + @Excel(name = "结果属性编码", orderNum = "5", width = 18) + private String targetAttrCode; + + @Excel(name = "计算物名称", orderNum = "6", width = 18) + private String sourceThingName; + + @Excel(name = "计算物编码", orderNum = "7", width = 18) + private String sourceThingCode; + + @Excel(name = "计算物属性名称", orderNum = "8", width = 18) + private String sourceAttrName; + + public static List convertFromDto(CalcTargetConfigDTO targetConfig) { + return targetConfig.getSourceConfigs().stream() + .map( + sourceConfig -> + new MappingTypeExcel() + .setCalcType(CalcConfigType.getNameByCode(targetConfig.getConfigType())) + .setTargetThingName(targetConfig.getTargetThingName()) + .setTargetThingCode(targetConfig.getTargetThingCode()) + .setTargetAttrName(targetConfig.getTargetAttrName()) + .setTargetAttrCode(targetConfig.getTargetAttrCode()) + .setSourceThingName(sourceConfig.getSourceThingName()) + .setSourceThingCode(sourceConfig.getSourceThingCode()) + .setSourceAttrName(sourceConfig.getSourceAttrName())) + .collect(Collectors.toList()); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/handler/CalcExecuteHandler.java b/modules/calculation/src/main/java/com/thing/calculation/handler/CalcExecuteHandler.java new file mode 100644 index 0000000..1d110ab --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/handler/CalcExecuteHandler.java @@ -0,0 +1,165 @@ +package com.thing.calculation.handler; + +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.dto.ExecuteCalcRequest; +import com.thing.calculation.dto.LogSourceInfoValue; +import com.thing.calculation.entity.CalcLogEntity; +import com.thing.calculation.enumeration.CalcConfigType; +import com.thing.calculation.enumeration.CalcStatus; +import com.thing.calculation.service.CalcLogService; +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.data.event.QueueConsumerEvent; +import com.thing.common.data.proto.QueueProto; +import com.thing.queue.util.Topics; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-05 + * @description 物计算处理器 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CalcExecuteHandler { + + private final CalcLogService calcLogService; + private final ApplicationEventPublisher publisher; + @Lazy @Resource private CalcTargetConfigService calcTargetConfigService; + + /** + * 执行计算的入口:
+ * 1. 查询日志
+ * 2. 检查更新日志
+ * 3. 针对可计算的日志进行计算
+ * 4. 计算结果处理:日志更新入库、结果对象消息推送
+ * + * @param request 计算请求 + */ + public void handleCalculate(ExecuteCalcRequest request) { + long calcStart = System.currentTimeMillis(); + calcLogService.clearExpiredCalculatedLog(); + + List targetConfigs = calcTargetConfigService.getAllEnabled(); + if (CollectionUtils.isEmpty(targetConfigs)) { + return; + } + + Map configMap = + targetConfigs.stream() + .collect(Collectors.toMap(CalcTargetConfigDTO::getId, Function.identity())); + + // 查询待计算状态的日志 + List logs = + calcLogService.getPendingLogs( + configMap.keySet(), request.getStartTs(), request.getEndTs()); + if (CollectionUtils.isEmpty(logs)) { + return; + } + + // 算出结果后批量推送 + List protoList = new ArrayList<>(); + logs.forEach( + log -> { + CalcTargetConfigDTO config = configMap.get(log.getCalcTargetConfigId()); + dispatchCalculate(log, config); + long calcEnd = System.currentTimeMillis(); + log.setUpdateDate(calcEnd); + if (!CalcStatus.isCalculated(log.getStatus()) || Objects.isNull(log.getResult())) { + return; + } + log.setDuration(calcEnd - calcStart); + QueueProto.TsKvProto tsKvProto = + QueueProto.TsKvProto.newBuilder() + .setThingCode(config.getTargetThingCode()) + .setKey(config.getTargetAttrCode()) + .setTs(log.getTime()) + .setVal(log.getResult()) + .build(); + protoList.add( + QueueProto.DataProto.newBuilder().setTskvProto(tsKvProto).build()); + }); + + publisher.publishEvent( + new QueueConsumerEvent(Topics.V1_TSKV_HISTORY.getValue(), protoList)); + + // 批量更新状态 + calcLogService.batchUpdate(logs); + } + + /** + * 分发计算,按不同计算类型分发给不同计算函数,并将执行结果更新到log中 + * + * @param calcLog 物计算日志 + * @param config 物计算配置 + */ + public void dispatchCalculate(CalcLogEntity calcLog, CalcTargetConfigDTO config) { + if (Objects.isNull(calcLog) || Objects.isNull(config)) { + return; + } + calcLog.increaseCalcCount(); + CalcConfigType configType = CalcConfigType.getByCode(config.getConfigType()); + switch (configType) { + case DATA_MAPPING, AGG_MAPPING -> doMapping(calcLog); + case IND_CALC, COMPLEX_CALC, SHARE_CALC -> doCalc(calcLog); + default -> {} + } + } + + /** 映射计算:无论是一个还是多个,统统加起来总是不会错的 */ + private void doMapping(CalcLogEntity calcLog) { + Map data = calcLog.parseSourceInfo(); + try { + BigDecimal totalVal = + data.values().stream() + .map(LogSourceInfoValue::convertVal) + .filter(Objects::nonNull) + .reduce(BigDecimal.ZERO, BigDecimal::add); + calcLog.setResult(totalVal.toString()); + calcLog.setStatus(CalcStatus.CALCULATED.getCode()); + } catch (Exception e) { + log.error("物计算:映射错误 => ", e); + calcLog.setStatus(CalcStatus.CALCULATE_EXCEPTION.getCode()); + calcLog.setErrorInfo(e.getMessage()); + } + } + + /** 公式计算 */ + private void doCalc(CalcLogEntity calcLog) { + Map data = calcLog.parseSourceInfo(); + try { + Map variableMap = new HashMap<>(); + data.forEach((k, v) -> variableMap.put(k, v.convertVal())); + Object result = FormulaUtil.executeFormula(calcLog.getFormula(), variableMap); + if (Objects.nonNull(result)) { + // 控制结果的小数位数 + String strValue = result.toString(); + if (NumberUtils.isCreatable(strValue)) { + BigDecimal value = new BigDecimal(strValue).setScale(6, RoundingMode.HALF_UP); + calcLog.setResult(value.toPlainString()); + } else { + calcLog.setResult(strValue); + } + calcLog.setStatus(CalcStatus.CALCULATED.getCode()); + } + } catch (Exception e) { + log.error("物计算:计算错误 => ", e); + calcLog.setStatus(CalcStatus.CALCULATE_EXCEPTION.getCode()); + calcLog.setErrorInfo(e.getMessage()); + } + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/handler/CalcLogSaveHandler.java b/modules/calculation/src/main/java/com/thing/calculation/handler/CalcLogSaveHandler.java new file mode 100644 index 0000000..cb53783 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/handler/CalcLogSaveHandler.java @@ -0,0 +1,305 @@ +package com.thing.calculation.handler; + + +import com.thing.calculation.dto.CalcSourceConfigDTO; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.entity.CalcLogEntity; +import com.thing.calculation.enumeration.CalcConfigType; +import com.thing.calculation.enumeration.CalcStatus; +import com.thing.calculation.service.CalcLogService; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.utils.IdGenerator; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-11 + * @description + */ +@Component +@RequiredArgsConstructor +public class CalcLogSaveHandler { + private final CalcLogService calcLogService; + + /** + * 处理日志保存 + * + * @param tsKvList 实时数据进来的tskv列表 + * @param allEnabledConfigs 所有开启物计算的配置 + * @param recalculate 是否重新计算 + */ + public void handleLogSave( + List tsKvList, + List allEnabledConfigs, + boolean recalculate) { + // 匹配物计算配置,成功匹配的数据转化为log + List calcLogs = matchAndConvert(tsKvList, allEnabledConfigs); + if (CollectionUtils.isEmpty(calcLogs)) { + return; + } + // 保存或更新,因为一个配置同一时刻理论上应当只有一条日志记录。同配置,同时刻的数据应当merge再更新 + doSaveOrUpdate(calcLogs, recalculate); + } + + /** + * 对每个tskv匹配物计算规则,满足条件的tskv转为log对象 + * + * @param tsKvList 实时数据进来的tskv列表 + * @param allEnabledConfigs 所有开启物计算的配置 + * @return 需要进行物计算的日志对象 + */ + private List matchAndConvert( + List tsKvList, List allEnabledConfigs) { + if (allEnabledConfigs.isEmpty()) return null; + + // 计算物配置列表 + List sourceConfigs = + allEnabledConfigs.stream() + .map(CalcTargetConfigDTO::getSourceConfigs) + .filter(Objects::nonNull) + .flatMap(List::stream) + .toList(); + + // targetConfigId与公式的映射关系 + Map configMap = + allEnabledConfigs.stream() + .collect(Collectors.toMap(CalcTargetConfigDTO::getId, Function.identity())); + + List logList = loopToGenerateLogs(tsKvList, sourceConfigs, configMap); + + // 生成的日志列表按同一配置,同一时间合并 + return mergeLogs(logList); + } + + /** + * 循环每个tskv,如果有匹配的物计算规则,则生成一批log对象 + * + * @param tsKvList 数据列表 + * @param sourceConfigs 计算物配置 + * @param configMap 配置self-map + * @return 物计算日志列表 + */ + private List loopToGenerateLogs( + List tsKvList, + List sourceConfigs, + Map configMap) { + // 计算物配置按物和属性依次分组 + Map>> thingAttrConfigGroup = + sourceConfigs.stream() + .collect( + Collectors.groupingBy( + CalcSourceConfigDTO::getSourceThingCode, + Collectors.groupingBy( + CalcSourceConfigDTO::getSourceAttrCode))); + + // 循环生成日志对象 + List logList = new ArrayList<>(); + for (TsKvDTO tskv : tsKvList) { + String thingCode = tskv.getThingCode(); + String attrCode = tskv.getAttrKey(); + if (!thingAttrConfigGroup.containsKey(thingCode)) { + continue; + } + Map> attrConfigMap = + thingAttrConfigGroup.get(thingCode); + if (!attrConfigMap.containsKey(attrCode)) { + continue; + } + // 一个物及其属性在多个sourceConfig中都存在说明:在不同配置中被用到了 + List sourceConfigList = attrConfigMap.get(attrCode); + List currentThingAttrLogs = + generateLogs(sourceConfigList, configMap, tskv); + if (!CollectionUtils.isEmpty(currentThingAttrLogs)) { + logList.addAll(currentThingAttrLogs); + } + } + return logList; + } + + /** + * 针对已匹配成功的tskv和sourceConfigList, 生成一批日志对象 + * + * @param configList 计算物配置列表 + * @param configMap 配置self-map + * @param tskv 数据对象 + * @return 日志对象列表 + */ + private List generateLogs( + List configList, + Map configMap, + TsKvDTO tskv) { + long now = System.currentTimeMillis(); + return configList.stream() + .map( + sourceConfig -> { + Long targetConfigId = sourceConfig.getCalcTargetConfigId(); + CalcTargetConfigDTO targetConfig = configMap.get(targetConfigId); + String formula = targetConfig.getFormula(); + CalcLogEntity calcLog = + new CalcLogEntity() + .setCalcTargetConfigId(targetConfigId) + .setFormula(formula) + .setTime(tskv.getTs()) + .appendSourceInfo(sourceConfig, tskv.getVal()) + .setConfigType(targetConfig.getConfigType()) + .setStatus(CalcStatus.MISMATCH.getCode()); + calcLog.setId(IdGenerator.nextId()) + .setTenantCode(sourceConfig.getTenantCode()) + .setCompanyId(sourceConfig.getCompanyId()) + .setDeptId(sourceConfig.getDeptId()) + .setCreator(0L) + .setCreateDate(now) + .setUpdater(0L) + .setUpdateDate(now); + return calcLog; + }) + .toList(); + } + + /** + * 对日志列表按配置和时间分组,同一个组内的日志应当merge成一条记录 + * + * @param logList 日志列表 + * @return 合并后的日志列表 + */ + private List mergeLogs(List logList) { + Map> sameConfigLogs = + logList.stream() + .collect( + Collectors.groupingBy( + e -> e.getCalcTargetConfigId() + ":" + e.getTime())); + logList.clear(); + sameConfigLogs.forEach( + (tag, logs) -> { + CalcLogEntity calcLogEntity = logs.get(0); + CalcLogEntity mergedLog = + logs.stream() + .reduce( + calcLogEntity, + (log, newLog) -> log.mergeLog(newLog, false)); + checkForUpdateStatus(mergedLog); + logList.add(mergedLog); + }); + return logList; + } + + /** + * 合并新生成的日志和数据库中已有的日志 + * + * @param newLogList 新生成的日志列表 + * @param dBlogList 数据库中已有的日志列表 + * @param recalculate 是否重新计算 + * @return true: 需要更新的log列表, false: 需要新增的log列表 + */ + private Map> mergeWithDbLogs( + List newLogList, List dBlogList, boolean recalculate) { + Map> result = new HashMap<>(2); + result.put(true, new ArrayList<>()); + result.put(false, new ArrayList<>()); + // 数据库中的日志列表按照配置、时间分组。一个配置,一个时间应当只有一条数据 + Map> dbLogGroup = + dBlogList.stream() + .collect( + Collectors.groupingBy( + CalcLogEntity::getCalcTargetConfigId, + Collectors.toMap( + CalcLogEntity::getTime, + Function.identity(), + (e1, e2) -> e1))); + + // 遍历新的日志列表,看哪些需要被合并,哪些需要新增 + newLogList.forEach( + log -> { + if (dbLogGroup.containsKey(log.getCalcTargetConfigId())) { + Map timeLogMap = + dbLogGroup.get(log.getCalcTargetConfigId()); + if (timeLogMap.containsKey(log.getTime())) { + CalcLogEntity dbLog = timeLogMap.get(log.getTime()); + dbLog.mergeLog(log, recalculate); + dbLog.setConfigType(log.getConfigType()); + if (prepareForCalculate(dbLog)) { + // 同一配置,同一时间,log更新 + checkForUpdateStatus(dbLog); + result.get(true).add(dbLog); + } + } else { + // 同一配置,不同时间,log新增 + result.get(false).add(log); + } + } else { + // 配置不同,log新增 + result.get(false).add(log); + } + }); + + return result; + } + + /** + * 保存或更新物计算日志 由于该方法调度频率较高,多JVM时容易出现并发写入,为了避免重复写入,采用insert update + * + * @param calcLogs 本次事件生成的新一批日志列表 + * @param recalculate 是否重新计算 + */ + private void doSaveOrUpdate(List calcLogs, boolean recalculate) { + Set configIds = + calcLogs.stream() + .map(CalcLogEntity::getCalcTargetConfigId) + .collect(Collectors.toSet()); + Set timeSet = + calcLogs.stream().map(CalcLogEntity::getTime).collect(Collectors.toSet()); + List dbLogs = calcLogService.listByConfigIdsAndTimeSet(configIds, timeSet); + if (!dbLogs.isEmpty()) { + // 一部分新增、一部分更新 + Map> logMap = + mergeWithDbLogs(calcLogs, dbLogs, recalculate); + List toUpdateLogs = logMap.get(true); + List toSaveLogs = logMap.get(false); + if (!CollectionUtils.isEmpty(toUpdateLogs)) { + calcLogService.batchUpdate(toUpdateLogs); + } + if (!CollectionUtils.isEmpty(toSaveLogs)) { + calcLogService.batchSaveOrUpdate(toSaveLogs); + } + } else { + calcLogService.batchSaveOrUpdate(calcLogs); + } + } + + /** + * 在实时数据消费存储计算日志场景下: 日志的初始化状态是未匹配,如果sourceInfo中涉及到的变量涵盖了公式中的变量,那么该日志的状态应当修改成未计算 + * 如果计算类型是聚合映射,那么也参与计算 + */ + private void checkForUpdateStatus(CalcLogEntity calcLog) { + Set missingVariables = calcLog.getMissingVariables(); + if (CollectionUtils.isEmpty(missingVariables) + || CalcConfigType.isAggMapping(calcLog.getConfigType())) { + calcLog.setStatus(CalcStatus.UN_CALCULATED.getCode()); + } + } + + /** + * 合并操作后,状态若依然是已计算、计算异常,那就表示这些log参与过计算,且sourceInfo没有改变 + * + * @param log 日志对象 + * @return + * true: 被修改过状态 -> 无论是否参与过计算,下一次总要尝试进行计算 + * false: 没被修改过状态 -> 参与过计算,且没必要再次计算 + */ + private boolean prepareForCalculate(CalcLogEntity log) { + Integer statusCode = log.getStatus(); + CalcStatus statusEnum = CalcStatus.getByCode(statusCode); + return switch (statusEnum) { + case MISMATCH, UN_CALCULATED -> true; + default -> false; + }; + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/initializer/CalcHistoryLogSyncInitializer.java b/modules/calculation/src/main/java/com/thing/calculation/initializer/CalcHistoryLogSyncInitializer.java new file mode 100644 index 0000000..df33599 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/initializer/CalcHistoryLogSyncInitializer.java @@ -0,0 +1,58 @@ +package com.thing.calculation.initializer; + +import com.thing.calculation.dto.ReCalcRequest; +import com.thing.calculation.service.CalcLogService; +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.core.initializer.AbstractAppInitializer; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +/** + * @author siyang + * @date 2024-03-12 + * @description 历史计算日志同步初始化器 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class CalcHistoryLogSyncInitializer extends AbstractAppInitializer { + + /** 容错时间: 5min */ + private static final Long FAULT_TOLERANT_TIME = 1000 * 60 * 5L; + + private final CalcLogService calcLogService; + private final CalcTargetConfigService calcTargetConfigService; + + @Override + public String getActionName() { + return "物计算断点日志同步"; + } + + @Override + public void executeInit() { + Long latestTime = calcLogService.getLatestLogTime(); + if (Objects.isNull(latestTime)) { + return; + } + List configIds = calcTargetConfigService.getEnabledIdList(); + if (configIds.isEmpty()) { + return; + } + Long startTs = latestTime - FAULT_TOLERANT_TIME; + Long endTs = System.currentTimeMillis(); + String startTime = DateTimeUtils.timeConvertStr(startTs); + String endTime = DateTimeUtils.timeConvertStr(endTs); + log.info("开始同步物计算断点日志,开始时间:{}, 结束时间:{}", startTime, endTime); + ReCalcRequest request = + new ReCalcRequest() + .setCalcTargetConfigIds(configIds) + .setStartTime(startTime) + .setEndTime(endTime); + calcTargetConfigService.reCalculate(request); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/listener/CalcLogSaveEventListener.java b/modules/calculation/src/main/java/com/thing/calculation/listener/CalcLogSaveEventListener.java new file mode 100644 index 0000000..e1010ca --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/listener/CalcLogSaveEventListener.java @@ -0,0 +1,61 @@ +package com.thing.calculation.listener; + + +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.handler.CalcLogSaveHandler; +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.event.CalcLogSaveEvent; +import com.thing.queue.util.Topics; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-04 + * @description 物计算日志存储事件监听 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class CalcLogSaveEventListener { + + private final CalcTargetConfigService calcTargetConfigService; + private final CalcLogSaveHandler calcLogSaveHandler; + + /** + * 从event.list中挑选出需要被存储(被计算)的对象 + * + * @param event 物计算日志存储事件对象 + */ + @EventListener(CalcLogSaveEvent.class) + public void handleEvent(CalcLogSaveEvent event) { + // 初步校验 + Topics topic = Topics.match(event.getSource().toString()); + if (topic != Topics.V1_TSKV_CALC_LOG_SAVE) { + return; + } + List tsKvList = + event.getList().stream() + .map(DataProto::getTskvProto) + .map(e -> new TsKvDTO(e.getThingCode(), e.getKey(), e.getTs(), e.getVal())) + .collect(Collectors.toList()); + if (CollectionUtils.isEmpty(tsKvList)) { + return; + } + + List allEnabledConfigs = calcTargetConfigService.getAllEnabled(); + + // 执行log存储 + calcLogSaveHandler.handleLogSave(tsKvList, allEnabledConfigs, false); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcLogMapper.java b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcLogMapper.java new file mode 100644 index 0000000..0194c12 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcLogMapper.java @@ -0,0 +1,20 @@ +package com.thing.calculation.mapper; + +import com.thing.calculation.entity.CalcLogEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; + +/** +* 物计算日志表 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Mapper +public interface CalcLogMapper extends PowerBaseMapper { + void batchSaveOrUpdate(@Param("logs") Collection logs); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcSourceConfigMapper.java b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcSourceConfigMapper.java new file mode 100644 index 0000000..1540517 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcSourceConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.calculation.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.calculation.entity.CalcSourceConfigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 计算物配置mapper +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Mapper +public interface CalcSourceConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcTargetConfigMapper.java b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcTargetConfigMapper.java new file mode 100644 index 0000000..f66a4a7 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/mapper/CalcTargetConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.calculation.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.calculation.entity.CalcTargetConfigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 物计算(目标物)配置 +* +* @author system system@lrd.com +* @since 5.1 2024-02-27 +*/ +@Mapper +public interface CalcTargetConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/CalcLogService.java b/modules/calculation/src/main/java/com/thing/calculation/service/CalcLogService.java new file mode 100644 index 0000000..09b622a --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/CalcLogService.java @@ -0,0 +1,40 @@ +package com.thing.calculation.service; + +import com.thing.calculation.dto.CalcLogDTO; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.entity.CalcLogEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 物计算日志表 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +public interface CalcLogService extends IBaseService { + + PageData handlePage(Map params); + + List listByConfigIdsAndTimeSet(Collection configIds, Set timeSet); + + void batchSaveOrUpdate(Collection logs); + + void clearExpiredCalculatedLog(); + + List getPendingLogs(Collection configIds, Long startTs, Long endTs); + + Long getLatestLogTime(); + + void exportExcel(Map params, HttpServletResponse response); + + void batchUpdate(List logs); + + void updateFormula(List configList, Long startTs, Long endTs); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/CalcSourceConfigService.java b/modules/calculation/src/main/java/com/thing/calculation/service/CalcSourceConfigService.java new file mode 100644 index 0000000..7941ca6 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/CalcSourceConfigService.java @@ -0,0 +1,23 @@ +package com.thing.calculation.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.calculation.entity.CalcSourceConfigEntity; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 计算物配置服务接口 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +public interface CalcSourceConfigService extends IBaseService { + + void deleteByTargetConfigIds(List targetConfigIds); + + List findByTargetIdAndAlias(Collection targetConfigIds, Set aliases); + + List findByTargetId(Long targetConfigId); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/CalcTargetConfigService.java b/modules/calculation/src/main/java/com/thing/calculation/service/CalcTargetConfigService.java new file mode 100644 index 0000000..ee5f1ee --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/CalcTargetConfigService.java @@ -0,0 +1,48 @@ +package com.thing.calculation.service; + +import com.thing.calculation.dto.*; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.calculation.entity.CalcTargetConfigEntity; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 物计算(目标物)配置 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +public interface CalcTargetConfigService extends IBaseService { + + PageData handlePage(Map params); + + CalcTargetConfigDTO getDetail(Long id); + + List getAllEnabled(); + + void handleSave(CalcTargetConfigDTO targetConfig); + + void handleBatchSave(List targetConfigs); + + void handleUpdate(CalcTargetConfigDTO targetConfig); + + void handleDelete(List targetConfigIds); + + void deleteSourceConfig(Long sourceConfigId); + + TestCalcResponse testCalc(TestCalcRequest request); + + void templateDownload(HttpServletResponse response); + + void importExcel(MultipartFile file); + + void exportExcel(Map params, HttpServletResponse response); + + void reCalculate(ReCalcRequest request); + + List getEnabledIdList(); +} \ No newline at end of file diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcLogServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcLogServiceImpl.java new file mode 100644 index 0000000..6389dd2 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcLogServiceImpl.java @@ -0,0 +1,186 @@ +package com.thing.calculation.service.impl; + +import static com.thing.calculation.entity.table.CalcLogEntityTableDef.CALC_LOG_ENTITY; +import static com.thing.calculation.entity.table.CalcTargetConfigEntityTableDef.CALC_TARGET_CONFIG_ENTITY; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import com.thing.calculation.dto.CalcLogDTO; +import com.thing.calculation.dto.CalcTargetConfigDTO; +import com.thing.calculation.entity.CalcLogEntity; +import com.thing.calculation.entity.CalcTargetConfigEntity; +import com.thing.calculation.enumeration.CalcStatus; +import com.thing.calculation.excel.CalcLogExcel; +import com.thing.calculation.mapper.CalcLogMapper; +import com.thing.calculation.service.CalcLogService; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; + +/** + * 物计算日志表 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Service +@RequiredArgsConstructor +public class CalcLogServiceImpl extends BaseServiceImpl implements CalcLogService { + + /** 日志过期时间跨度 */ + private static final Long TIMEOUT_INTERVAL = 1000 * 60 * 60 * 24 * 7L; + + /** 计算失败后,计算异常状态下的最大计算次数 */ + private static final Integer MAX_CALC_COUNT = 5; + + @Override + public QueryWrapper getWrapper(Map params) { + String thingKeywords = MapUtils.getString(params, "thingKeywords"); + String attrKeywords = MapUtils.getString(params, "attrKeywords"); + Integer configType = MapUtils.getInteger(params, "configType"); + Integer status = MapUtils.getInteger(params, "status"); + QueryWrapper queryWrapper = + QueryWrapper.create() + .select( + CALC_TARGET_CONFIG_ENTITY.TARGET_THING_NAME, + CALC_TARGET_CONFIG_ENTITY.TARGET_THING_CODE, + CALC_TARGET_CONFIG_ENTITY.TARGET_ATTR_NAME, + CALC_TARGET_CONFIG_ENTITY.TARGET_ATTR_CODE, + CALC_TARGET_CONFIG_ENTITY.CONFIG_TYPE, + CALC_LOG_ENTITY.ALL_COLUMNS) + .from(CALC_TARGET_CONFIG_ENTITY) + .innerJoin(CALC_LOG_ENTITY) + .on(CALC_TARGET_CONFIG_ENTITY.ID.eq(CALC_LOG_ENTITY.CALC_TARGET_CONFIG_ID)) + .eq(CalcLogEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(CalcTargetConfigEntity::getConfigType, configType, Objects.nonNull(configType)) + .eq(CalcLogEntity::getStatus, status, Objects.nonNull(status)); + if (StringUtils.isNotBlank(thingKeywords)) { + queryWrapper.and( + CALC_TARGET_CONFIG_ENTITY + .TARGET_THING_NAME + .like(thingKeywords) + .or(CALC_TARGET_CONFIG_ENTITY.TARGET_THING_CODE.like(thingKeywords))); + } + if (StringUtils.isNotBlank(attrKeywords)) { + queryWrapper.and( + CALC_TARGET_CONFIG_ENTITY + .TARGET_ATTR_NAME + .like(attrKeywords) + .or(CALC_TARGET_CONFIG_ENTITY.TARGET_ATTR_CODE.like(attrKeywords))); + } + queryWrapper.orderBy(CalcLogEntity::getCreateDate).desc(); + return queryWrapper; + } + + @Override + public PageData handlePage(Map params) { + return selectPage(params); + } + + @Override + public List listByConfigIdsAndTimeSet( + Collection configIds, Set timeSet) { + if (CollectionUtils.isEmpty(configIds)) { + return Collections.emptyList(); + } + return list( + QueryWrapper.create() + .in(CalcLogEntity::getCalcTargetConfigId, configIds) + .in(CalcLogEntity::getTime, timeSet)); + } + + @Override + public void batchSaveOrUpdate(Collection logs) { + mapper.batchSaveOrUpdate(logs); + } + + /** 清理已过期的计算记录(状态为已计算,且为两天前的计算日志) */ + @Override + public void clearExpiredCalculatedLog() { + mapper.deleteByQuery( + QueryWrapper.create() + .eq(CalcLogEntity::getStatus, CalcStatus.CALCULATED.getCode()) + .lt(CalcLogEntity::getCreateDate, System.currentTimeMillis() - TIMEOUT_INTERVAL)); + } + + /** + * 查询需要重新计算的日志 + * + * @param configIds 物计算配置ID列表 + * @param startTs time 起始时间 + * @param endTs time 结束时间 + * @return 需要重新计算的日志 + */ + @Override + public List getPendingLogs(Collection configIds, Long startTs, Long endTs) { + if (CollectionUtils.isEmpty(configIds)) { + return Collections.emptyList(); + } + return mapper.selectListByQuery( + QueryWrapper.create() + .in(CalcLogEntity::getCalcTargetConfigId, configIds) + .ge(CalcLogEntity::getTime, startTs, Objects.nonNull(startTs)) + .le(CalcLogEntity::getTime, endTs, Objects.nonNull(endTs)) + .eq(CalcLogEntity::getStatus, CalcStatus.UN_CALCULATED.getCode()) + .or( + CALC_LOG_ENTITY + .STATUS + .eq(CalcStatus.CALCULATE_EXCEPTION.getCode()) + .and(CALC_LOG_ENTITY.CALC_COUNT.le(MAX_CALC_COUNT)))); + } + + @Override + public Long getLatestLogTime() { + CalcLogEntity log = + mapper.selectOneByQuery( + QueryWrapper.create().orderBy(CalcLogEntity::getTime).desc().limit(1)); + return Objects.nonNull(log) ? log.getTime() : null; + } + + @Override + public void exportExcel(Map params, HttpServletResponse response) { + List logs = listAs(getWrapper(params), CalcLogDTO.class); + List excels = CalcLogExcel.convertFromDTO(logs); + ExcelUtils.exportExcel(excels, null, "物计算异常记录", CalcLogExcel.class, "物计算异常记录.xls", response); + } + + @Override + public void batchUpdate(List logs) { + // pgsql 对唯一键的更新会报错,去掉对其的set行为 + logs.forEach(e -> e.setCalcTargetConfigId(null).setTime(null)); + updateBatch(logs); + } + + @Override + public void updateFormula(List configList, Long startTs, Long endTs) { + configList.forEach( + config -> + UpdateChain.of(CalcLogEntity.class) + .set(CalcLogEntity::getFormula, config.getFormula()) + .where(CalcLogEntity::getCalcTargetConfigId).eq(config.getId()) + .and(CalcLogEntity::getTime).ge(startTs) + .and(CalcLogEntity::getTime).le(endTs) + .update()); + } + + private PageData selectPage(Map params) { + Page page = getPage(params, CalcLogDTO.class); + Page paginate = mapper.paginateAs(page, getWrapper(params), CalcLogDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcSourceConfigServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcSourceConfigServiceImpl.java new file mode 100644 index 0000000..afc08f9 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcSourceConfigServiceImpl.java @@ -0,0 +1,56 @@ +package com.thing.calculation.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.calculation.entity.CalcSourceConfigEntity; +import com.thing.calculation.mapper.CalcSourceConfigMapper; +import com.thing.calculation.service.CalcSourceConfigService; +import com.thing.common.orm.service.impl.BaseServiceImpl; + +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 计算物配置服务 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Service +public class CalcSourceConfigServiceImpl extends BaseServiceImpl implements CalcSourceConfigService { + + @Override + public QueryWrapper getWrapper(Map params){ + return new QueryWrapper(); + } + + @Override + public void deleteByTargetConfigIds(List targetConfigIds) { + if (CollectionUtils.isEmpty(targetConfigIds)) { + return; + } + mapper.deleteByQuery( + QueryWrapper.create() + .in(CalcSourceConfigEntity::getCalcTargetConfigId, targetConfigIds)); + } + + @Override + public List findByTargetIdAndAlias( + Collection targetConfigIds, Set aliases) { + return mapper.selectListByQuery( + QueryWrapper.create() + .in(CalcSourceConfigEntity::getCalcTargetConfigId, targetConfigIds) + .in(CalcSourceConfigEntity::getSourceAttrAlias, aliases)); + } + + @Override + public List findByTargetId(Long targetConfigId) { + return mapper.selectListByQuery( + QueryWrapper.create() + .eq(CalcSourceConfigEntity::getCalcTargetConfigId, targetConfigId)); + } +} diff --git a/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcTargetConfigServiceImpl.java b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcTargetConfigServiceImpl.java new file mode 100644 index 0000000..2c4e277 --- /dev/null +++ b/modules/calculation/src/main/java/com/thing/calculation/service/impl/CalcTargetConfigServiceImpl.java @@ -0,0 +1,408 @@ +package com.thing.calculation.service.impl; + +import static com.thing.calculation.entity.table.CalcTargetConfigEntityTableDef.CALC_TARGET_CONFIG_ENTITY; + +import cn.hutool.core.util.ObjectUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.calculation.dto.*; +import com.thing.calculation.entity.CalcSourceConfigEntity; +import com.thing.calculation.entity.CalcTargetConfigEntity; +import com.thing.calculation.entity.table.CalcTargetConfigEntityTableDef; +import com.thing.calculation.enumeration.CalcConfigType; +import com.thing.calculation.excel.CalcConfigExcel; +import com.thing.calculation.excel.CalculationTypeExcel; +import com.thing.calculation.excel.MappingTypeExcel; +import com.thing.calculation.handler.CalcExecuteHandler; +import com.thing.calculation.handler.CalcLogSaveHandler; +import com.thing.calculation.mapper.CalcTargetConfigMapper; +import com.thing.calculation.service.CalcLogService; +import com.thing.calculation.service.CalcSourceConfigService; +import com.thing.calculation.service.CalcTargetConfigService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.orm.utils.IdGenerator; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 物计算(目标物)配置 + * + * @author system system@lrd.com + * @since 5.1 2024-02-27 + */ +@Service +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +public class CalcTargetConfigServiceImpl + extends BaseServiceImpl + implements CalcTargetConfigService { + + private final TsKvService tsKvService; + private final CalcLogService calcLogService; + private final CalcExecuteHandler calcExecuteHandler; + private final CalcLogSaveHandler calcLogSaveHandler; + private final CalcSourceConfigService calcSourceConfigService; + + /** 变量池:A ~ Z */ + private static final List variablePool = new ArrayList<>(); + + static { + for (int asciiValue = 65; asciiValue <= 90; asciiValue++) { + char c = (char) asciiValue; + variablePool.add(String.valueOf(c)); + } + } + + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String targetThingKeyword = MapUtils.getString(params, "targetThingKeyword"); + String targetAttrKeyword = MapUtils.getString(params, "targetAttrKeyword"); + Integer configType = MapUtils.getInteger(params, "configType"); + + wrapper.eq(CalcTargetConfigEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(CalcTargetConfigEntity::getConfigType, configType, Objects.nonNull(configType)); + + if (StringUtils.isNotBlank(targetThingKeyword)) { + wrapper.and(CALC_TARGET_CONFIG_ENTITY.TARGET_THING_NAME.like(targetThingKeyword) + .or(CALC_TARGET_CONFIG_ENTITY.TARGET_THING_CODE.like(targetThingKeyword))); + } + + if (StringUtils.isNotBlank(targetAttrKeyword)) { + wrapper.and(CALC_TARGET_CONFIG_ENTITY.TARGET_ATTR_NAME.like(targetAttrKeyword) + .or(CALC_TARGET_CONFIG_ENTITY.TARGET_ATTR_CODE.like(targetAttrKeyword))); + } + return wrapper; + } + + @Override + public PageData handlePage(Map params) { + PageData page = getPageData(params, CalcTargetConfigDTO.class); + List list = page.getList(); + fillData(list); + return new PageData<>(list, page.getTotal()); + } + + @Override + public CalcTargetConfigDTO getDetail(Long id) { + CalcTargetConfigDTO targetConfig = getByIdAs(id, CalcTargetConfigDTO.class); + fillData(Collections.singletonList(targetConfig)); + return targetConfig; + } + + @Override + @Cacheable(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public List getAllEnabled() { + List list = + mapper.selectListByQueryAs( + QueryWrapper.create().eq(CalcTargetConfigEntity::getEnable, Boolean.TRUE), + CalcTargetConfigDTO.class); + fillData(list); + return list; + } + + @Override + @CacheEvict(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public void handleSave(CalcTargetConfigDTO targetConfig) { + generateDefaultInfo(targetConfig); + // 存储目标物配置 + saveDto(targetConfig); + // 存储计算物配置 + calcSourceConfigService.saveBatch( + ConvertUtils.sourceToTarget( + targetConfig.getSourceConfigs(), CalcSourceConfigEntity.class)); + } + + @Override + @CacheEvict(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public void handleBatchSave(List targetConfigs) { + if (CollectionUtils.isEmpty(targetConfigs)) { + return; + } + targetConfigs.forEach(this::generateDefaultInfo); + // 存储计算物配置 + List sourceConfigEntities = + targetConfigs.stream() + .map(CalcTargetConfigDTO::getSourceConfigs) + .flatMap(Collection::stream) + .map(e -> ConvertUtils.sourceToTarget(e, CalcSourceConfigEntity.class)) + .toList(); + if (CollectionUtils.isEmpty(sourceConfigEntities)) { + return; + } + calcSourceConfigService.saveOrUpdateBatch(sourceConfigEntities); + } + + /** + * 生成配置默认信息,手动生成id以建立目标物和计算物之间的关联 + * + * @param targetConfig 目标物配置 + */ + private void generateDefaultInfo(CalcTargetConfigDTO targetConfig) { + boolean flag = ObjectUtil.isEmpty(targetConfig.getId()); + // 目标物配置默认设置 + targetConfig.setId(flag? IdGenerator.nextId() : targetConfig.getId()); + targetConfig.setEnable(Optional.ofNullable(targetConfig.getEnable()).orElse(true)); + + // 目标物公式描述生成、计算物配置默认信息设置 + List sourceConfigs = targetConfig.getSourceConfigs(); + + // 如果是映射,则自动生成公式,方便统一处理 + if (CalcConfigType.isMapping(targetConfig.getConfigType())) { + autoGenerateFormula(targetConfig, sourceConfigs); + } else if (StringUtils.isBlank(targetConfig.getFormula())) { + throw new SysException("公式不能为空!"); + } + + // 组装变量与计算物属性对应关系,以及给予默认值后测试是否可计算 + Map variableMap = new HashMap<>(); + Map testValueMap = new HashMap<>(); + for (CalcSourceConfigDTO sourceConfig : sourceConfigs) { + sourceConfig.setCalcTargetConfigId(targetConfig.getId()); + variableMap.put( + sourceConfig.getSourceAttrAlias(), + sourceConfig.getSourceThingName() + "_" + sourceConfig.getSourceAttrName()); + testValueMap.put(sourceConfig.getSourceAttrAlias(), 1d); + } + + // 验证公式是否可计算 + String formula = targetConfig.getFormula(); + try { + FormulaUtil.executeFormula(formula, testValueMap); + } catch (Exception e) { + throw new SysException("公式填写不合法:" + e.getMessage()); + } + + targetConfig.setFormulaDescription(FormulaUtil.replaceVariable(formula, variableMap)); + //无id新增,有id 则是修改 + Consumer operation = flag ? this::saveDto : this::updateDto; + operation.accept(targetConfig); + } + + @Override + @CacheEvict(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public void handleUpdate(CalcTargetConfigDTO targetConfig) { + // 更新目标物配置 + updateDto(targetConfig); + + // 更新计算物配置 + calcSourceConfigService.saveOrUpdateBatch( + ConvertUtils.sourceToTarget( + targetConfig.getSourceConfigs(), CalcSourceConfigEntity.class)); + } + + @Override + @CacheEvict(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public void handleDelete(List targetConfigIds) { + // 删除计算物配置 + calcSourceConfigService.deleteByTargetConfigIds(targetConfigIds); + + // 删除目标物配置 + mapper.deleteBatchByIds(targetConfigIds); + } + + @Override + @CacheEvict(value = CacheNameEnum.THING_CALC_CONFIG, key = "'all'") + public void deleteSourceConfig(Long sourceConfigId) { + CalcSourceConfigEntity sourceConfig = calcSourceConfigService.getById(sourceConfigId); + CalcTargetConfigEntity targetConfig = getById(sourceConfig.getCalcTargetConfigId()); + calcSourceConfigService.getMapper().deleteById(sourceConfigId); + if (CalcConfigType.isAggMapping(targetConfig.getConfigType())) { + List sourceConfigs = + calcSourceConfigService.findByTargetId(targetConfig.getId()); + autoGenerateFormula(targetConfig, sourceConfigs); + updateById(targetConfig); + calcSourceConfigService.updateBatch(sourceConfigs); + } + } + + @Override + public TestCalcResponse testCalc(TestCalcRequest request) { + List tskvList = + tsKvService.findLatestByCodesAndAttrs( + request.obtainThingCodes(), request.obtainAttrCodes(), true); + if (CollectionUtils.isEmpty(tskvList)) { + throw new SysException("未查询到最新数据,建议切换物或属性后再尝试"); + } + Long time = tskvList.get(0).getTs(); + Map tskvMap = + tskvList.stream() + .collect( + Collectors.toMap( + e -> e.getThingCode() + ":" + e.getAttrKey(), + Function.identity())); + Map variableMap = new HashMap<>(); + Map metaDataMap = request.metaDataToMap(); + metaDataMap.forEach( + (groupTag, alias) -> { + TsKvDTO tsKv = tskvMap.get(groupTag); + variableMap.put( + alias, + BigDecimal.valueOf( + Double.parseDouble( + Optional.ofNullable(tsKv.getVal()).orElse("0")))); + }); + Object result = FormulaUtil.executeFormula(request.getFormula(), variableMap); + return new TestCalcResponse( + Objects.isNull(result) ? "" : result.toString(), + DateTimeUtils.timeConvertStr(time)); + } + + @Override + public void templateDownload(HttpServletResponse response) { + ExcelUtils.exportExcel( + Collections.emptyList(), + null, + "物计算配置", + CalcConfigExcel.class, + "物计算配置.xls", + response); + } + + @Override + public void importExcel(MultipartFile file) { + List calcConfigExcels = + ExcelUtils.importExcel(file, 0, 1, CalcConfigExcel.class); + List configDTOs = CalcConfigExcel.toConfigDTO(calcConfigExcels); + handleBatchSave(configDTOs); + } + + @Override + public void exportExcel(Map params, HttpServletResponse response) { + List sourceData = listAs(params, CalcTargetConfigDTO.class); + fillData(sourceData); + + Integer configTypeCode = MapUtils.getInteger(params, "configType"); + CalcConfigType configType = CalcConfigType.getByCode(configTypeCode); + switch (configType) { + case DATA_MAPPING, AGG_MAPPING -> { + List excelData = sourceData.stream().map(MappingTypeExcel::convertFromDto).flatMap(List::stream).collect(Collectors.toList()); + ExcelUtils.exportExcel(excelData, null, "物计算配置", MappingTypeExcel.class, "物计算配置.xls", response); + } + case IND_CALC, COMPLEX_CALC, SHARE_CALC -> { + List excelData = sourceData.stream().map(CalculationTypeExcel::convertFromDto).collect(Collectors.toList()); + ExcelUtils.exportExcel(excelData, null, "物计算配置", CalculationTypeExcel.class, "物计算配置.xls", response); + } + } + } + + @Override + public void reCalculate(ReCalcRequest request) { + List ids = request.getCalcTargetConfigIds(); + List configList = getListByIdsAs(ids, CalcTargetConfigDTO.class); + fillData(configList); + List sourceConfigs = + configList.stream() + .map(CalcTargetConfigDTO::getSourceConfigs) + .filter(Objects::nonNull) + .flatMap(List::stream) + .toList(); + Set thingCodes = new HashSet<>(); + Set attrCodes = new HashSet<>(); + sourceConfigs.forEach( + e -> { + thingCodes.add(e.getSourceThingCode()); + attrCodes.add(e.getSourceAttrCode()); + }); + + // 更新这段时间内log的公式 + calcLogService.updateFormula(configList, request.getStartTs(), request.getEndTs()); + + List tskvList = + tsKvService.findTsKvByCodesAndAttrs( + thingCodes, attrCodes, request.getStartTs(), request.getEndTs(), true); + calcLogSaveHandler.handleLogSave(tskvList, configList, true); + + // 立即执行计算 + calcExecuteHandler.handleCalculate(new ExecuteCalcRequest(request.getStartTime(), request.getEndTime())); + } + + @Override + public List getEnabledIdList() { + QueryWrapper queryWrapper = + QueryWrapper.create() + .select(CALC_TARGET_CONFIG_ENTITY.ID) + .eq(CalcTargetConfigEntity::getEnable, true); + List entities = mapper.selectListByQuery(queryWrapper); + return entities.stream().map(CalcTargetConfigEntity::getId).collect(Collectors.toList()); + } + + private void fillData(List targetConfigs) { + if (CollectionUtils.isEmpty(targetConfigs)) { + return; + } + List configIds = targetConfigs.stream().map(CalcTargetConfigDTO::getId).toList(); + + // 公式涉及到的所有计算物配置 + List sourceConfigs = + calcSourceConfigService.listAs( + QueryWrapper.create() + .in(CalcSourceConfigEntity::getCalcTargetConfigId, configIds), + CalcSourceConfigDTO.class); + + // 计算物配置按目标物配置id分组 + Map> sourceConfigGroup = + sourceConfigs.stream() + .collect(Collectors.groupingBy(CalcSourceConfigDTO::getCalcTargetConfigId)); + + // 填充配置中的计算物信息 + targetConfigs.forEach(e -> e.setSourceConfigs(sourceConfigGroup.get(e.getId()))); + } + + private void autoGenerateFormula( + CalcTargetConfigDTO targetConfig, List sourceConfigs) { + StringBuilder formulaBuilder = new StringBuilder(); + for (int i = 0; i < sourceConfigs.size(); i++) { + CalcSourceConfigDTO sourceConfig = sourceConfigs.get(i); + String alias = variablePool.get(i); + sourceConfig.setSourceAttrAlias(alias); + formulaBuilder.append(alias); + if (i < sourceConfigs.size() - 1) { + formulaBuilder.append("+"); + } + } + targetConfig.setFormula(formulaBuilder.toString()); + } + + private void autoGenerateFormula( + CalcTargetConfigEntity targetConfig, List sourceConfigs) { + StringBuilder formulaBuilder = new StringBuilder(); + for (int i = 0; i < sourceConfigs.size(); i++) { + CalcSourceConfigEntity sourceConfig = sourceConfigs.get(i); + String alias = variablePool.get(i); + sourceConfig.setSourceAttrAlias(alias); + formulaBuilder.append(alias); + if (i < sourceConfigs.size() - 1) { + formulaBuilder.append("+"); + } + } + targetConfig.setFormula(formulaBuilder.toString()); + } +} diff --git a/modules/calculation/src/main/resources/mapper/calc/CalcLogMapper.xml b/modules/calculation/src/main/resources/mapper/calc/CalcLogMapper.xml new file mode 100644 index 0000000..f7055ff --- /dev/null +++ b/modules/calculation/src/main/resources/mapper/calc/CalcLogMapper.xml @@ -0,0 +1,19 @@ + + + + + + + INSERT INTO calc_log + (id, calc_target_config_id,formula, source_info, status, time, result,duration,error_info, + tenant_code, company_id, dept_id, creator, create_date, updater, update_date) + VALUES + + (#{item.id},#{item.calcTargetConfigId},#{item.formula},#{item.sourceInfo},#{item.status},#{item.time},#{item.result},#{item.duration},#{item.errorInfo}, + #{item.tenantCode},#{item.companyId},#{item.deptId},#{item.creator},#{item.createDate},#{item.updater},#{item.updateDate}) + + ON CONFLICT (calc_target_config_id, time) + DO UPDATE SET create_date = excluded.create_date + + + \ No newline at end of file diff --git a/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaAttributeMapper.xml b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaAttributeMapper.xml new file mode 100644 index 0000000..72b3909 --- /dev/null +++ b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaAttributeMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaLogMapper.xml b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaLogMapper.xml new file mode 100644 index 0000000..0ea13aa --- /dev/null +++ b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaLogMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaSettingMapper.xml b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaSettingMapper.xml new file mode 100644 index 0000000..b37895e --- /dev/null +++ b/modules/calculation/src/main/resources/mapper/calculation/CalculationFormulaSettingMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-public/pom.xml b/modules/carbon-public/pom.xml new file mode 100644 index 0000000..f9eb22a --- /dev/null +++ b/modules/carbon-public/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + carbon-public + jar + ThingBI Server Modules carbon-public + + + UTF-8 + + + + + com.thing.modules + carbon-track + + + diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/CertificateApi.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/CertificateApi.java new file mode 100644 index 0000000..276bc4f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/CertificateApi.java @@ -0,0 +1,217 @@ +package com.thing.carbon.pub.api; + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.constant.ApiConstant; +import com.thing.carbon.pub.constant.PointsConstant; +import com.thing.carbon.pub.constant.ReportSourceTypeConstant; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.*; +import com.thing.carbontrack.certification.dto.CarbonStageValue; +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.certification.dto.SimpleProductInfo; +import com.thing.carbontrack.certification.entity.IotCarbonCertificateEntity; +import com.thing.carbontrack.certification.service.IotCarbonCertificateService; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/5/16 11:50 + * @description 国网数据服务接口——碳足迹证书 + */ +@RestController +@RequestMapping("v2/carbon/pub/certificate") +@RequiredArgsConstructor +public class CertificateApi { + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubPointsLogsService logsService; + private final CarbonPubSupplierService supplierService; + private final IotCarbonCertificateService certificateService; + private final IotCarbonProductionVarietyService productionService; + private final CarbonPubProductionModelService productionModelService; + private final CarbonPubProductionReportService reportService; + + /** + * 同步碳足迹证书 + */ + @PostMapping("sync") + public Result sync(@RequestBody IotCarbonCertificateDTO certificate, HttpServletRequest request) { + CarbonPubSupplierEntity supplier = + (CarbonPubSupplierEntity) request.getAttribute(ApiConstant.SUPPLIER); + Long tenantCode = supplier.getCode(); + + // saveOrUpdateProduct(certificate, tenantCode); + saveOrUpdateProductModel(certificate, tenantCode); + saveOrUpdateCertificate(certificate, tenantCode); + + boolean createSuccess = updateOrCreateReport(certificate, tenantCode); + if(!createSuccess){ + return new Result<>(); + } + + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + supplierService.plusPointOn(tenantCode, pointsRule.getFootprintCertifyPoints()); + logsService.save( + new CarbonPubPointsLogsEntity() + .setPoints(pointsRule.getFootprintCertifyPoints()) + .setType(PointsConstant.CERTIFICATE) + .setTenantCode(tenantCode) + .setCreateTime(System.currentTimeMillis())); + + return new Result<>(); + } + + private void saveOrUpdateProduct(IotCarbonCertificateDTO certificate, Long tenantCode){ + Long productId = certificate.getProductId(); + IotCarbonProductionVarietyEntity product = + productionService.getOne( + QueryWrapper.create() + .eq(IotCarbonProductionVarietyEntity::getId, productId) + .eq(IotCarbonProductionVarietyEntity::getTenantCode, tenantCode) + .limit(1)); + + SimpleProductInfo productInfo = certificate.getProductInfo(); + IotCarbonProductionVarietyEntity productEntity = productInfo.toProductEntity(tenantCode); + productEntity.setId(productId); + productEntity.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + + if (Objects.isNull(product)) { + productionService.save(productEntity); + } else { + productionService.updateById(productEntity); + } + } + + private void saveOrUpdateProductModel(IotCarbonCertificateDTO certificate, Long tenantCode){ + Long productId = certificate.getProductId(); + CarbonPubProductionModelEntity model = + productionModelService.getOne( + QueryWrapper.create() + .eq(CarbonPubProductionModelEntity::getProductId, productId) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode) + .limit(1)); + + SimpleProductInfo productInfo = certificate.getProductInfo(); + CarbonPubProductionModelEntity productEntity = convertProductModel(productInfo, tenantCode); + productEntity.setProductId(productId); + productEntity.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + + if (Objects.isNull(model)) { + productionModelService.save(productEntity); + } else { + productEntity.setId(model.getId()); + productionModelService.updateById(productEntity); + } + } + + + private void saveOrUpdateCertificate(IotCarbonCertificateDTO certificate, Long tenantCode) { + IotCarbonCertificateEntity entity = certificate.toEntity(); + entity.setShared(1); + entity.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + certificateService.saveOrUpdate(entity); + } + + private boolean updateOrCreateReport(IotCarbonCertificateDTO certificate, Long tenantCode) { + // 各阶段生命周期占比 + List stagePercentList = certificate.getStagePercentList(); + if (CollectionUtils.isEmpty(stagePercentList)) { + return false; + } + Map stagePercentMap = + stagePercentList.stream() + .collect( + Collectors.toMap( + CarbonStageValue::getName, CarbonStageValue::getValue)); + String stagePercentJson = JSONObject.toJSONString(stagePercentMap); + + // 查询历史记录 + SimpleProductInfo productInfo = certificate.getProductInfo(); + CarbonPubProductionReportEntity oldReport = + reportService.getOne( + QueryWrapper.create() + .eq(CarbonPubProductionReportEntity::getProductId, certificate.getProductId()) + .eq(CarbonPubProductionReportEntity::getBoundaryType, 3) + .eq(CarbonPubProductionReportEntity::getTenantCode, tenantCode) + .eq(CarbonPubProductionReportEntity::getBoundaryStart, new Date(certificate.getBoundaryStart())) + .eq(CarbonPubProductionReportEntity::getBoundaryEnd, new Date(certificate.getBoundaryEnd())) + .limit(1)); + + // 更新报告 + if(Objects.nonNull(oldReport)){ + oldReport.setTotalCarbon(certificate.getCarbonVal()); + oldReport.setStagePercentJson(stagePercentJson); + reportService.updateById(oldReport); + return false; + } + + // 生成报告编号 + String reportCodePrefix = "LRD-ECO-%s%s"; + LocalDateTime now = LocalDateTime.now(); + LocalDateTime validEndTime = now.plusYears(1).minusDays(1); + String timeStr = + now.getYear() + + addZeroPrefix(now.getMonthValue(), 2) + + addZeroPrefix(now.getDayOfMonth(), 2); + String reportCode = String.format(reportCodePrefix, timeStr, addZeroPrefix(1, 3)); + + // 创建报告 + CarbonPubProductionReportEntity report = + new CarbonPubProductionReportEntity() + .setProductId(certificate.getProductId()) + .setBoundary(productInfo.getBoundary()) + .setBoundaryType(3) + .setBoundaryStart(new Date(certificate.getBoundaryStart())) + .setBoundaryEnd(new Date(certificate.getBoundaryEnd())) + .setValidEndTime(DateTimeUtils.parse(validEndTime)) + .setTotalCarbon(certificate.getCarbonVal()) + .setCompanyName(certificate.getCompanyName()) + .setReportCode(reportCode) + .setSourceType(ReportSourceTypeConstant.CERTIFICATE_UPLOAD) + .setStagePercentJson(stagePercentJson); + report.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + + reportService.save(report); + return true; + } + + private String addZeroPrefix(Integer num, int length){ + return StringUtils.leftPad(num.toString(), length, "0"); + } + + private CarbonPubProductionModelEntity convertProductModel(SimpleProductInfo productInfo, Long tenantCode){ + CarbonPubProductionModelEntity model = new CarbonPubProductionModelEntity(); + model.setProductCode(productInfo.getProductCode()) + .setProductModel(productInfo.getProductModel()) + .setProductName(productInfo.getProductName()) + .setFunctionUnit(productInfo.getFunctionUnit()) + .setIndustryType(productInfo.getIndustryType()) + .setIndustryCategory(productInfo.getProductCategory()) + .setProductType(productInfo.getProductType()); + model.setTenantCode(tenantCode).setCompanyId(tenantCode).setDeptId(tenantCode); + return model; + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/MaterialFactorApi.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/MaterialFactorApi.java new file mode 100644 index 0000000..1a6ab45 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/MaterialFactorApi.java @@ -0,0 +1,79 @@ +package com.thing.carbon.pub.api; + +import com.thing.carbon.pub.constant.ApiConstant; +import com.thing.carbon.pub.constant.CarbonFactorConstant; +import com.thing.carbon.pub.dto.CarbonPubMaterialFactorApiDTO; +import com.thing.carbon.pub.dto.CarbonPubMaterialFactorDTO; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.CarbonPubMaterialFactorService; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +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; + +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/16 11:50 + * @description 国网数据服务接口——碳排因子 + */ +@RestController +@RequestMapping("v2/carbon/pub/material/factor") +@RequiredArgsConstructor +public class MaterialFactorApi { + + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubMaterialFactorService factorService; + + /** + * 碳排因子分页查询 + * 参数定义参考{@link com.thing.carbon.pub.controller.CarbonPubMaterialFactorController#page} + */ + @GetMapping("page") + public Result> factorPage(@RequestParam Map params, HttpServletRequest request) { + CarbonPubSupplierEntity supplier = (CarbonPubSupplierEntity) request.getAttribute(ApiConstant.SUPPLIER); + Integer points = supplier.getPoints(); + PageData page = factorService.getPageData(params, CarbonPubMaterialFactorApiDTO.class); + // 非详情接口不给看碳排因子值 + page.getList() + .forEach( + e -> { + e.setFactor(null); + e.setPoints(points); + if (CarbonFactorConstant.CHANGE_SOURCE_NAME) { + e.setSourceName(CarbonFactorConstant.SOURCE_NAME); + } + }); + return new Result>().ok(page); + } + + /** + * 查看碳排因子详情 + */ + @GetMapping("detail") + public Result factorDetail(@RequestParam Map params, HttpServletRequest request) { + String materialFactorId = MapUtils.getString(params, "materialFactorId"); + + CarbonPubSupplierEntity supplier = (CarbonPubSupplierEntity)request.getAttribute(ApiConstant.SUPPLIER); + Long tenantCode = supplier.getCode(); + + CarbonPubMaterialFactorDTO dto = factorService.handleDetail(materialFactorId, tenantCode); + CarbonPubMaterialFactorApiDTO factor = ConvertUtils.sourceToTarget(dto, CarbonPubMaterialFactorApiDTO.class); + if (CarbonFactorConstant.CHANGE_SOURCE_NAME) { + factor.setSourceName(CarbonFactorConstant.SOURCE_NAME); + } + + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + factor.setPoints(supplier.getPoints() - pointsRule.getFactorUsePoints()); + return new Result().ok(factor); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionModelApi.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionModelApi.java new file mode 100644 index 0000000..19ed453 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionModelApi.java @@ -0,0 +1,150 @@ +package com.thing.carbon.pub.api; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.constant.ApiConstant; +import com.thing.carbon.pub.constant.PointsConstant; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionModelDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.CarbonPubPointsLogsService; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.carbon.pub.service.CarbonPubProductionModelService; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/16 14:44 + * @description 国网数据服务接口——产品工艺模型 + */ +@RestController +@RequestMapping("v2/carbon/pub/production/model") +@RequiredArgsConstructor +public class ProductionModelApi { + + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubPointsLogsService logsService; + private final CarbonPubSupplierService supplierService; + private final CarbonPubProductionModelService modelService; + private final IotCarbonProductionVarietyService productionVarietyService; + private final IotCarbonBomService bomService; + private final IotCarbonProcessStepsService processStepsService; + + /** + * 获取工艺模型的所有一级行业分类 + */ + @GetMapping("industry/types") + public Result> modelIndustryTypes(){ + return new Result>().ok(modelService.getIndustryTypes()); + } + + /** + * 产品工艺模型二级分类查询 + * 参考{@link com.thing.carbon.pub.controller.CarbonPubProductionModelController#types} + */ + @GetMapping("industry/categories") + public Result> modelTypes(@RequestParam Map params){ + List types = modelService.getIndustryCategories(params); + return new Result>().ok(types); + } + + + /** + * 产品工艺模型分页查询 + * 参考{@link com.thing.carbon.pub.controller.CarbonPubProductionModelController#page} + */ + @GetMapping("page") + public Result> modelPage(@RequestParam Map params){ + params.putIfAbsent("orderField", "create_date"); + params.put("showInCommunity", 1); + PageData page = modelService.getSimplePageData(params); + return new Result>().ok(page); + } + + /** + * 产品工艺模型详情 + * 参考{@link com.thing.carbon.pub.controller.CarbonPubProductionModelController#get} + */ + @GetMapping("detail") + public Result modelDetail(@RequestParam Map params, HttpServletRequest request){ + Long id = MapUtils.getLong(params, "id"); + String code = MapUtils.getString(params, "code"); + String name = MapUtils.getString(params, "name"); + CarbonPubProductionModelEntity entity = + modelService.getOne( + QueryWrapper.create() + .eq(CarbonPubProductionModelEntity::getId, id, Objects::nonNull) + .eq(CarbonPubProductionModelEntity::getProductCode, code, Objects::nonNull) + .eq(CarbonPubProductionModelEntity::getProductName, name, Objects::nonNull) + .limit(1)); + if (Objects.isNull(entity)) { + return new Result<>(); + } + + CarbonPubSupplierEntity supplier = (CarbonPubSupplierEntity)request.getAttribute(ApiConstant.SUPPLIER); + CarbonPubProductionModelDTO detail = modelService.handleDetail(entity, supplier.getCode()); + modelService.plusDownloadCount(entity.getId()); + return new Result().ok(detail); + } + + /** + * 上传产品工艺模型 + * 参考{@link com.thing.carbon.pub.controller.CarbonPubProductionModelController#save} + */ + @PostMapping("upload") + public Result modelUpload(@RequestBody CarbonPubProductionModelDTO dto, HttpServletRequest request){ + CarbonPubSupplierEntity supplier = (CarbonPubSupplierEntity)request.getAttribute(ApiConstant.SUPPLIER); + Long tenantCode = supplier.getCode(); + + CarbonPubProductionModelEntity oldModel = modelService.getByProductId(dto.getProductId(), tenantCode); + CarbonPubProductionModelEntity newModel = + ConvertUtils.sourceToTarget(dto, CarbonPubProductionModelEntity.class) + .setShowInCommunity(1); + if (Objects.isNull(oldModel)) { + newModel.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + modelService.syncModel(newModel, tenantCode); + modelService.save(newModel); + // 首次上传积分++ + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + supplierService.plusPointOn(tenantCode, pointsRule.getModelSharedPoints()); + + // 积分记录 + logsService.save( + new CarbonPubPointsLogsEntity() + .setPoints(pointsRule.getModelSharedPoints()) + .setType(PointsConstant.SHARE_MODEL) + .setTenantCode(tenantCode) + .setCreateTime(System.currentTimeMillis())); + } else { + Long productId = productionVarietyService.getIdByCodeName(dto.getProductCode(), dto.getProductName(), tenantCode); + + newModel.setTenantCode(tenantCode) + .setDeptId(tenantCode) + .setCompanyId(tenantCode) + .setId(oldModel.getId()); + modelService.updateById(newModel); + + productionVarietyService.updateWithJson(newModel.getProductJson(), productId, tenantCode); + bomService.saveOrUpdateWithJson(newModel.getMaterialJson(), productId); + processStepsService.saveOrUpdateWithJson(newModel.getProcessJson(), productId); + } + return new Result<>(); + } + +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionResultApi.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionResultApi.java new file mode 100644 index 0000000..2f4a17e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/api/ProductionResultApi.java @@ -0,0 +1,120 @@ +package com.thing.carbon.pub.api; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.constant.ApiConstant; +import com.thing.carbon.pub.constant.PointsConstant; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionResultDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.CarbonPubPointsLogsService; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/5/16 14:44 + * @description 国网数据服务接口——产品工艺模型 + */ +@RestController +@RequestMapping("v2/carbon/pub/production/result") +@RequiredArgsConstructor +public class ProductionResultApi { + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubPointsLogsService logsService; + private final CarbonPubSupplierService supplierService; + private final CarbonPubProductionResultService resultService; + + /** 同步生产结果 */ + @PostMapping("sync") + public Result syncProductResult(@RequestBody List dtoList, HttpServletRequest request) { + if (CollectionUtils.isEmpty(dtoList)) { + return new Result<>(); + } + + // 获取当前企业在国网侧系统中的租户编码 + CarbonPubSupplierEntity supplier = + (CarbonPubSupplierEntity) request.getAttribute(ApiConstant.SUPPLIER); + Long tenantCode = supplier.getCode(); + + Set oriIds = dtoList.stream().map(CarbonPubProductionResultDTO::getOriId).collect(Collectors.toSet()); + Set productIds = dtoList.stream().map(CarbonPubProductionResultDTO::getProductId).collect(Collectors.toSet()); + + // 基于产品id和原始记录id判断是否存在,存在更新,不存在插入 + List oldList = + resultService.list( + QueryWrapper.create() + .in(CarbonPubProductionResultEntity::getOriId, oriIds) + .in(CarbonPubProductionResultEntity::getProductId, productIds) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode)); + + // 已存在的数据按原始记录id和产品id分组 + Map uniqueTagMap = + oldList.stream() + .collect( + Collectors.toMap( + e -> e.getOriId() + "_" + e.getProductId(), + Function.identity())); + + // 新上传的数据也按原始记录id和产品id分组,true:数据库中已存在; false:数据库中不存在 + dtoList.stream() + .collect( + Collectors.groupingBy( + e -> uniqueTagMap.containsKey(e.getOriId() + "_" + e.getProductId()))) + .forEach((exist, groupList) -> { + List entities = ConvertUtils.sourceToTarget(groupList, CarbonPubProductionResultEntity.class); + entities.forEach(e -> e.setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode)); + if(exist) { + List updateList = + entities.stream() + .map( + e -> { + CarbonPubProductionResultEntity oldOne = uniqueTagMap.get(e.getOriId() + "_" + e.getProductId()); + CarbonPubProductionResultEntity newOne = ConvertUtils.sourceToTarget(e, CarbonPubProductionResultEntity.class); + newOne.setId(oldOne.getId()).setTenantCode(tenantCode).setDeptId(tenantCode).setCompanyId(tenantCode); + return newOne; + }) + .toList(); + resultService.updateBatch(updateList); + } else { + resultService.saveBatch(entities); + CarbonPubProductionResultEntity first = entities.get(0); + // 积分++ + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + boolean shareResult = Objects.equals(first.getResultType(), "1"); + supplierService.plusPointOn( + tenantCode, + shareResult + ? pointsRule.getResultSharedPoints() * entities.size() + : pointsRule.getProcessSharedPoints() * entities.size()); + + // 积分记录 + logsService.save( + new CarbonPubPointsLogsEntity() + .setPoints(shareResult ? pointsRule.getResultSharedPoints() : pointsRule.getProcessSharedPoints()) + .setType(shareResult ? PointsConstant.SHARE_RESULT : PointsConstant.SHARE_PROCESS) + .setTenantCode(tenantCode) + .setCreateTime(System.currentTimeMillis())); + } + }); + return new Result<>(); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ApiConstant.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ApiConstant.java new file mode 100644 index 0000000..1b2f8e3 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ApiConstant.java @@ -0,0 +1,12 @@ +package com.thing.carbon.pub.constant; + +/** + * @author siyang + * @date 2024/5/21 15:29 + */ +public interface ApiConstant { + String ACCESS_TOKEN = "accessToken"; + String TIMESTAMP = "timestamp"; + String SIGN = "sign"; + String SUPPLIER = "supplier"; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/CarbonFactorConstant.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/CarbonFactorConstant.java new file mode 100644 index 0000000..37cf9a3 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/CarbonFactorConstant.java @@ -0,0 +1,11 @@ +package com.thing.carbon.pub.constant; + +/** + * @author siyang + * @date 2024/7/22 09:15 + * @description 碳排因子常量 + */ +public interface CarbonFactorConstant { + Boolean CHANGE_SOURCE_NAME = true; + String SOURCE_NAME = "碳排因子库"; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/PointsConstant.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/PointsConstant.java new file mode 100644 index 0000000..fcd0cda --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/PointsConstant.java @@ -0,0 +1,15 @@ +package com.thing.carbon.pub.constant; + +/** + * @author siyang + * @date 2024/6/17 17:25 + * @description + */ +public interface PointsConstant { + String SHARE_MODEL = "模型共享"; + String MODEL_CLICK = "模型点击"; + String SHARE_RESULT = "结果数据共享"; + String SHARE_PROCESS = "过程数据共享"; + String CERTIFICATE = "碳足迹认证"; + String FACTOR_USE = "消费碳足迹因子"; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ReportSourceTypeConstant.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ReportSourceTypeConstant.java new file mode 100644 index 0000000..249f023 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/constant/ReportSourceTypeConstant.java @@ -0,0 +1,12 @@ +package com.thing.carbon.pub.constant; + +/** + * @author siyang + * @date 2024/7/10 15:52 + * @description + */ +public interface ReportSourceTypeConstant { + String FILL_IN = "填报"; + String COLLECT = "实采"; + String CERTIFICATE_UPLOAD = "证书上传"; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubCertificateController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubCertificateController.java new file mode 100644 index 0000000..87b0bbd --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubCertificateController.java @@ -0,0 +1,88 @@ +package com.thing.carbon.pub.controller;//package com.thing.carbon.pub.controller; +// +//import com.thing.common.core.annotation.LogOperation; +//import com.thing.common.core.constants.Constant; +//import com.thing.common.core.validator.AssertUtils; +//import com.thing.common.core.validator.ValidatorUtils; +//import com.thing.common.core.validator.group.DefaultGroup; +//import com.thing.common.core.validator.group.UpdateGroup; +//import com.thing.common.core.web.response.PageData; +//import com.thing.common.core.web.response.Result; +//import com.thing.carbon.pub.dto.CarbonPubCertificateDTO; +//import com.thing.carbon.pub.service.CarbonPubCertificateService; +// +//import io.swagger.v3.oas.annotations.Operation; +//import io.swagger.v3.oas.annotations.Parameter; +//import io.swagger.v3.oas.annotations.Parameters; +//import io.swagger.v3.oas.annotations.tags.Tag; +//import lombok.RequiredArgsConstructor; +//import org.springframework.web.bind.annotation.*; +// +//import java.util.List; +//import java.util.Map; +// +///** +//* 碳足迹证书 +//* +//* @author sys dev@lrd.com +//* @since 5.1 2024-05-23 +//*/ +//@RestController +//@RequestMapping("v2/carbon/internal/certificate") +//@Tag(name="碳足迹证书-国网侧") +//@RequiredArgsConstructor +//public class CarbonPubCertificateController { +// +// private final CarbonPubCertificateService carbonPubCertificateService; +// +// @GetMapping("page") +// @Operation(summary="分页") +// @Parameters({ +// @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , +// @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , +// @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , +// @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), +// @Parameter(name = "productName", description = "产品名称"), +// @Parameter(name = "certificationOrg", description = "认证机构"), +// }) +// public Result> page(@Parameter(hidden = true) @RequestParam Map params){ +// PageData page = carbonPubCertificateService.handlePage(params); +// return new Result>().ok(page); +// } +// +// @GetMapping("{id}") +// @Operation(summary="信息") +// public Result get(@PathVariable("id") Long id){ +// CarbonPubCertificateDTO data = carbonPubCertificateService.handleDetail(id); +// return new Result().ok(data); +// } +// +// @PostMapping +// @Operation(summary="保存") +// @LogOperation("保存") +// public Result save(@RequestBody CarbonPubCertificateDTO productionCertificate){ +// carbonPubCertificateService.handleSave(productionCertificate); +// return new Result<>(); +// } +// +// @PutMapping +// @Operation(summary="修改") +// @LogOperation("修改") +// public Result update(@RequestBody CarbonPubCertificateDTO dto){ +// //效验数据 +// ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); +// carbonPubCertificateService.handleUpdate(dto); +// return new Result<>(); +// } +// +// @DeleteMapping +// @Operation(summary="删除") +// @LogOperation("删除") +// public Result delete(@RequestBody Long[] ids){ +// //效验数据 +// AssertUtils.isArrayEmpty(ids, "id"); +// carbonPubCertificateService.handleDelete(List.of(ids)); +// return new Result<>(); +// } +// +//} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubEngEffController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubEngEffController.java new file mode 100644 index 0000000..f8bb313 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubEngEffController.java @@ -0,0 +1,105 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubEngEffDTO; +import com.thing.carbon.pub.service.CarbonPubEngEffService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 设备能效标准 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-06-11 +*/ +@RestController +@RequestMapping("v2/carbon/internal/eng/eff") +@Tag(name="设备能效标准") +@RequiredArgsConstructor +public class CarbonPubEngEffController { + + private final CarbonPubEngEffService carbonPubEngEffService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "keywords", description = "搜索关键字"), + @Parameter(name = "productType", description = "产品类别"), + @Parameter(name = "productName", description = "产品名称"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = carbonPubEngEffService.getPageData(params, CarbonPubEngEffDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CarbonPubEngEffDTO data = carbonPubEngEffService.getByIdAs(id, CarbonPubEngEffDTO.class); + return new Result().ok(data); + } + + @GetMapping("history/records") + @Operation(summary="各字段历史记录") + @Parameters({ + @Parameter(name = "field", description = "product_type(产品类别), product_name(产品名称), eff_kpi(能效指标); unit(单位); top_category(一级分类); sub_category(二级分类); category_info(分类详情)", required = true) , + @Parameter(name = "productType", description = "产品类型") , + }) + public Result> targetFieldHistoryRecords(@Parameter(hidden = true) @RequestParam Map params){ + String field = MapUtils.getString(params, "field"); + String productType = MapUtils.getString(params, "productType"); + List list = carbonPubEngEffService.getTargetFieldHistoryRecords(field, productType); + return new Result>().ok(list); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubEngEffDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubEngEffService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubEngEffDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubEngEffService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubEngEffService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubFootprintLibController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubFootprintLibController.java new file mode 100644 index 0000000..bdfce17 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubFootprintLibController.java @@ -0,0 +1,65 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubFootprintInfo; +import com.thing.carbon.pub.service.CarbonPubFootprintLibService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +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; + +import java.util.Map; +import java.util.Set; + +/** + * @author siyang + * @date 2024/5/27 10:26 + * @description 产品碳足迹库控制器 + */ +@RestController +@RequestMapping("v2/carbon/internal/footprint/lib") +@Tag(name = "产品碳足迹库") +@RequiredArgsConstructor +public class CarbonPubFootprintLibController { + + private final CarbonPubFootprintLibService carbonPubFootprintLibService; + + @GetMapping("industry/types") + @Operation(summary = "行业大类列表") + public Result> industryTypes() { + Set types = carbonPubFootprintLibService.getIndustryTypes(); + return new Result>().ok(types); + } + + @GetMapping("industry/categories") + @Operation(summary = "行业子类列表") + @Parameters({@Parameter(name = "industryType", description = "行业分类")}) + public Result> industryCategories(@Parameter(hidden = true) @RequestParam Map params) { + Set types = carbonPubFootprintLibService.getIndustryCategories(params); + return new Result>().ok(types); + } + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "keywords", description = "关键词"), + @Parameter(name = "industryType", description = "行业一级分类"), + @Parameter(name = "industryCategory", description = "行业二级分类"), + @Parameter(name = "sourceType", description = "碳足迹数据源:填报、实采、证书上传"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = carbonPubFootprintLibService.handlePage(params); + return new Result>().ok(page); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubMaterialFactorController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubMaterialFactorController.java new file mode 100644 index 0000000..7fffaf9 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubMaterialFactorController.java @@ -0,0 +1,107 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubMaterialFactorDTO; +import com.thing.carbon.pub.service.CarbonPubMaterialFactorService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Set; + +/** +* 碳排因子库 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-11 +*/ +@RestController +@RequestMapping("v2/carbon/internal/material/factor") +@Tag(name="碳排因子库") +@RequiredArgsConstructor +public class CarbonPubMaterialFactorController { + private final CarbonPubMaterialFactorService carbonPubMaterialFactorService; + + @GetMapping("history/records") + @Operation(summary="各字段历史记录") + @Parameters({ + @Parameter(name = "field", description = "source_name(数据源); geo_full_cn(地区); industry_cn(行业类型)", required = true) , + }) + public Result> targetFieldHistoryRecords(@Parameter(hidden = true) @RequestParam Map params){ + String field = MapUtils.getString(params, "field"); + Set list = carbonPubMaterialFactorService.targetFieldHistoryRecords(field); + return new Result>().ok(list); + } + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productName", description = "产品名称,支持中英文模糊搜索"), + @Parameter(name = "year", description = "年份"), + @Parameter(name = "sourceName", description = "数据源名称"), + @Parameter(name = "geoFullCn", description = "地理位置"), + @Parameter(name = "industryCn", description = "行业"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + params.putIfAbsent("orderField", "hot_score"); + PageData page = carbonPubMaterialFactorService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") String id){ + Long tenantCode = UserContext.getRealTenantCode(); + CarbonPubMaterialFactorDTO data = carbonPubMaterialFactorService.handleDetail(id, tenantCode); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubMaterialFactorDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubMaterialFactorService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubMaterialFactorDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubMaterialFactorService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubMaterialFactorService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsLogsController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsLogsController.java new file mode 100644 index 0000000..756bb6e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsLogsController.java @@ -0,0 +1,88 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubPointsLogsDTO; +import com.thing.carbon.pub.service.CarbonPubPointsLogsService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 积分增减记录 +* +* @author xc +* @since 3.0 2024-06-17 +*/ +@RestController +@RequestMapping("v2/carbon/internal/points/log") +@Tag(name="积分增减记录") +@RequiredArgsConstructor +public class CarbonPubPointsLogsController { + + private final CarbonPubPointsLogsService carbonPubPointsLogsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode", description = "企业编码", required = true) + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = carbonPubPointsLogsService.getPageData(params, CarbonPubPointsLogsDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CarbonPubPointsLogsDTO data = carbonPubPointsLogsService.getByIdAs(id, CarbonPubPointsLogsDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubPointsLogsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubPointsLogsService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubPointsLogsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubPointsLogsService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubPointsLogsService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsRuleController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsRuleController.java new file mode 100644 index 0000000..f37b960 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubPointsRuleController.java @@ -0,0 +1,68 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** +* 碳足迹积分规则, 只有一条 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@RestController +@RequestMapping("v2/carbon/internal/points/rule") +@Tag(name="碳足迹积分规则") +@RequiredArgsConstructor +public class CarbonPubPointsRuleController { + + private final CarbonPubPointsRuleService carbonPubPointsRuleService; + + @GetMapping + @Operation(summary="信息") + public Result get(){ + CarbonPubPointsRuleDTO rule = carbonPubPointsRuleService.getFirst(); + return new Result().ok(rule); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubPointsRuleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubPointsRuleService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubPointsRuleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubPointsRuleService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubPointsRuleService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionModelController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionModelController.java new file mode 100644 index 0000000..16b8970 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionModelController.java @@ -0,0 +1,171 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubProductionIdName; +import com.thing.carbon.pub.dto.CarbonPubProductionModelDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.service.CarbonPubProductionModelService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** +* 产品详情 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@RestController +@RequestMapping("v2/carbon/internal/production/model") +@Tag(name="产品工艺模型-国网侧") +@RequiredArgsConstructor +public class CarbonPubProductionModelController { + + private final CarbonPubProductionModelService carbonPubProductionModelService; + + @GetMapping("industry/types") + @Operation(summary="行业一级分类列表") + public Result> industryTypes(){ + List types = carbonPubProductionModelService.getIndustryTypes(); + return new Result>().ok(types); + } + + @GetMapping("industry/categories") + @Operation(summary="行业二级分类列表") + @Parameters({ + @Parameter(name = "industryTpe", description = "行业一级分类") + }) + public Result> types(@Parameter(hidden = true) @RequestParam Map params){ + List types = carbonPubProductionModelService.getIndustryCategories(params); + return new Result>().ok(types); + } + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "keywords", description = "关键词"), + @Parameter(name = "industryType", description = "一级行业分类"), + @Parameter(name = "industryCategory", description = "二级行业分类"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + params.putIfAbsent("orderField", "create_date"); + params.put("showInCommunity", 1); + PageData page = carbonPubProductionModelService.getSimplePageData(params); + return new Result>().ok(page); + } + + @GetMapping("page/mine") + @Operation(summary="我的分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "industryCategory", description = "行业分类"), + @Parameter(name = "productType", description = "产品类型") + }) + public Result> myPage(@Parameter(hidden = true) @RequestParam Map params){ + params.putIfAbsent("orderField", "create_date"); + Boolean isAdmin = UserContext.isAdmin(); + if(!isAdmin){ + params.put("tenantCode", UserContext.getTenantCode()); + } + PageData page = carbonPubProductionModelService.getSimplePageData(params); + return new Result>().ok(page); + } + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + Long tenantCode = UserContext.getTenantCode(); + CarbonPubProductionModelEntity entity = carbonPubProductionModelService.getById(id); + CarbonPubProductionModelDTO data = carbonPubProductionModelService.handleDetail(entity, tenantCode); + return new Result().ok(data); + } + + @GetMapping("simple/list") + @Operation(summary="简要信息") + public Result> simpleList(){ + Long tenantCode = UserContext.getRealTenantCode(); + List data = carbonPubProductionModelService.getSimpleList(tenantCode); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubProductionModelDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + CarbonPubProductionModelEntity entity = ConvertUtils.sourceToTarget(dto, CarbonPubProductionModelEntity.class); + entity.setShowInCommunity(0); + carbonPubProductionModelService.save(entity); + return new Result<>(); + } + + @PostMapping("download") + @Operation(summary="下载(添加到我的收藏)") + @LogOperation("下载(添加到我的收藏)") + public Result download(@RequestBody CarbonPubProductionModelDTO dto){ + if(Objects.isNull(dto.getId())){ + throw new SysException("下载错误,请稍后重试"); + } + carbonPubProductionModelService.download(dto.getId()); + return new Result<>(); + } + + @PostMapping("copy") + @Operation(summary="复制") + @LogOperation("复制") + public Result copy(@RequestBody CarbonPubProductionModelDTO dto){ + if(Objects.isNull(dto.getId())){ + throw new SysException("无法复制"); + } + carbonPubProductionModelService.copy(dto.getId()); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubProductionModelDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubProductionModelService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubProductionModelService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportConfigController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportConfigController.java new file mode 100644 index 0000000..843662b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportConfigController.java @@ -0,0 +1,124 @@ +package com.thing.carbon.pub.controller; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.carbon.pub.service.CarbonPubProductionReportConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.service.SysTenantDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; +import java.util.Objects; + +/** +* 碳足迹报告配置 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-05-27 +*/ +@RestController +@RequestMapping("v2/carbon/internal/production/report/config") +@Tag(name="碳足迹报告配置【公共服务侧】") +@RequiredArgsConstructor +public class CarbonPubProductionReportConfigController { + + private final CarbonPubProductionReportConfigService carbonPubProductionReportConfigService; + private final SysTenantDetailService sysTenantDetailService; + + @Value("${carbon.pub.tenantTag}") + private String pubTenantTag; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = carbonPubProductionReportConfigService.getPageData(params, CarbonPubProductionReportConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("detail") + @Operation(summary="详情") + @Parameters({ + @Parameter(name = "id", description = "id") , + @Parameter(name = "productId", description = "产品id,与tenantCode同时使用") , + @Parameter(name = "boundaryType", description = "数据边界类型:1-自然月;2-自然年;3-自定义"), + @Parameter(name = "tenantCode", description = "租户编码,与productId同时使用") + }) + public Result detail(@Parameter(hidden = true) @RequestParam Map params){ + Long id = MapUtils.getLong(params, "id"); + if(Objects.nonNull(id)){ + CarbonPubProductionReportConfigDTO data = carbonPubProductionReportConfigService.getByIdAs(id, CarbonPubProductionReportConfigDTO.class); + return new Result().ok(data); + } + Long productId = MapUtils.getLong(params, "productId"); + Integer boundaryType = MapUtils.getInteger(params, "boundaryType"); + Long tenantCode = MapUtils.getLong(params, "tenantCode"); + CarbonPubProductionReportConfigDTO data = + carbonPubProductionReportConfigService.getOneAs( + QueryWrapper.create() + .eq(CarbonPubProductionReportConfigEntity::getProductId, productId) + .eq(CarbonPubProductionReportConfigEntity::getTenantCode, tenantCode) + .eq(CarbonPubProductionReportConfigEntity::getBoundaryType, boundaryType, Objects::nonNull), + CarbonPubProductionReportConfigDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubProductionReportConfigDTO dto){ + checkTenantCode(dto.getTenantCode()); + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubProductionReportConfigService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubProductionReportConfigDTO dto){ + checkTenantCode(dto.getTenantCode()); + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubProductionReportConfigService.updateDto(dto); + return new Result<>(); + } + + private void checkTenantCode(Long tenantCode) { + Boolean admin = UserContext.isAdmin(); + Long currentTenantCode = UserContext.getRealTenantCode(); + SysTenantDetailEntity tenant = sysTenantDetailService.getById(currentTenantCode); + // 不是管理员,也不是公共服务侧租户,那就只能修改自己的内容 + if (!admin || !tenant.getName().contains(pubTenantTag)) { + if (!Objects.equals(tenantCode, currentTenantCode)) { + throw new SysException("权限不足"); + } + } + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportController.java new file mode 100644 index 0000000..9d5638d --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionReportController.java @@ -0,0 +1,128 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionReportDTO; +import com.thing.carbon.pub.service.CarbonPubProductionReportService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 产品碳足迹报告 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@RestController +@RequestMapping("v2/carbon/internal/production/report") +@Tag(name="产品碳足迹报告【公共服务侧】") +@RequiredArgsConstructor +public class CarbonPubProductionReportController { + + private final CarbonPubProductionReportService carbonPubProductionReportService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productIds", description = "产品id列表,按逗号分隔"), + @Parameter(name = "start", description = "报告日期范围开始: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "end", description = "报告日期范围结束: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "tenantCode", description = "租户编码"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = carbonPubProductionReportService.getPageData(params, CarbonPubProductionReportDTO.class); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productIds", description = "产品id列表,按逗号分隔"), + @Parameter(name = "start", description = "报告日期范围开始: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "end", description = "报告日期范围结束: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "tenantCode", description = "租户编码"), + }) + public Result> list(@Parameter(hidden = true) @RequestParam Map params){ + List list = carbonPubProductionReportService.listAs(params, CarbonPubProductionReportDTO.class); + return new Result>().ok(list); + } + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + Map params = new HashMap<>(1); + params.put("id", id); + List reports = carbonPubProductionReportService.listAs(params, CarbonPubProductionReportDTO.class); + CarbonPubProductionReportDTO data = reports.get(0); + data.refreshCarbon(); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubProductionReportDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonPubProductionReportService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubProductionReportDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubProductionReportService.handleUpdate(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubProductionReportService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("generate/view") + @Operation(summary="生成报告预览") + @LogOperation("生成报告预览") + public Result generateReport(@RequestBody CarbonPubProductionReportConfigDTO config){ + CarbonPubProductionReportDTO data = carbonPubProductionReportService.generateReport(config); + String reportCode = + carbonPubProductionReportService.generateReportCode( + null, data.getBoundaryStart(), data.getBoundaryEnd()); + data.setReportCode(reportCode); + return new Result().ok(data); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionResultController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionResultController.java new file mode 100644 index 0000000..76a15c0 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubProductionResultController.java @@ -0,0 +1,174 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.*; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.common.core.enumeration.CarbonLifecycleEnum; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +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; + +import java.util.*; + +/** +* 生产记录核算结果 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@RestController +@RequestMapping("v2/carbon/internal/production/result") +@Tag(name="生产记录核算结果-国网侧") +@RequiredArgsConstructor +public class CarbonPubProductionResultController { + + private final CarbonPubProductionResultService carbonPubProductionResultService; + + @GetMapping("simple/result") + @Operation(summary="批次产品碳足迹-简单汇总") + @Parameters({ + @Parameter(name = "productIds", description = "产品Id列表,按逗号分隔", required = true), + @Parameter(name = "start", description = "开始时间:yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "end", description = "结束时间: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "boundary", description = "核算边界:1-摇篮到大门,2-摇篮到坟墓"), + @Parameter(name = "resultType", description = "结果类型:1-结果数据 2-过程数据"), + @Parameter(name = "tenantCodes", description = "租户编码"), + }) + public Result> getSimpleAggResult(@Parameter(hidden = true) @RequestParam Map params){ + String tenantCodes = MapUtils.getString(params, "tenantCodes"); + if (StringUtils.isBlank(tenantCodes)) { + throw new IllegalArgumentException("租户编码不能为空"); + } + List tenantCodeList = + Arrays.stream(tenantCodes.split(",")).map(s -> Long.parseLong(s.trim())).toList(); + + String productIdStr = MapUtils.getString(params, "productIds"); + if (StringUtils.isBlank(productIdStr)) { + return new Result>().ok(Collections.emptyList()); + } + String[] productIdArr = productIdStr.split(","); + List productIds = new ArrayList<>(); + for (String s : productIdArr) { + productIds.add(Long.parseLong(s.trim())); + } + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + String boundary = MapUtils.getString(params, "boundary"); + String resultType = MapUtils.getString(params, "resultType"); + List res = + carbonPubProductionResultService.getSimpleAggResult( + productIds, start, end, boundary, resultType, tenantCodeList); + return new Result>().ok(res); + } + + @GetMapping("single/lot/detail") + @Operation(summary="批次产品碳足迹-单批次各生命周期数据") + @Parameters({ + @Parameter(name = "productId", description = "产品Id", required = true), + @Parameter(name = "prCode", description = "生产编号", required = true), + @Parameter(name = "dataType", description = "展示数据类型:0-基础信息, 1-原材料采购与运输, 2-生产制造, 3-产品运输, 4-产品使用, 5-废弃处理"), + @Parameter(name = "tenantCode", description = "租户编码", required = true), + }) + public Result getSingleLotDetail(@Parameter(hidden = true) @RequestParam Map params){ + Long productId = MapUtils.getLong(params, "productId"); + String prCode = MapUtils.getString(params, "prCode"); + Integer dataType = Optional.ofNullable(MapUtils.getInteger(params, "dataType")).orElse(0); + Long tenantCode = MapUtils.getLong(params, "tenantCode"); + + if (Objects.isNull(productId) || Objects.isNull(prCode) || Objects.isNull(tenantCode)) { + throw new IllegalArgumentException("缺少参数"); + } + + if (Objects.equals(dataType, 0)) { + CarbonPubLibRecordBaseInfo res = carbonPubProductionResultService.getDetailOfBaseInfo(productId, prCode, tenantCode); + return new Result<>().ok(res); + } + CarbonLifecycleEnum carbonLifecycle = CarbonLifecycleEnum.match(dataType); + switch (carbonLifecycle) { + case MATERIAL_PURCHASE_TRANSPORT -> { + CarbonPubLibRecordMPT res = carbonPubProductionResultService.getDetailOfMPT(productId, prCode, null, null, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_MANUFACTURE -> { + CarbonPubLibRecordPM res = carbonPubProductionResultService.getDetailOfPM(productId, prCode, null, null, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_TRANSPORT -> { + CarbonPubLibRecordPT res = carbonPubProductionResultService.getDetailOfPT(productId, prCode, null, null, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_USAGE -> { + CarbonPubLibRecordPU res = carbonPubProductionResultService.getDetailOfPU(productId, prCode, null, null, tenantCode); + return new Result<>().ok(res); + } + case DISPOSE -> { + CarbonPubLibRecordDispose res = carbonPubProductionResultService.getDetailOfDispose(productId, prCode, null, null, tenantCode); + return new Result<>().ok(res); + } + default -> { + return null; + } + } + } + + @GetMapping("batch/lot/detail") + @Operation(summary="批次产品碳足迹-多批次各生命周期数据") + @Parameters({ + @Parameter(name = "productId", description = "产品Id", required = true), + @Parameter(name = "start", description = "开始时间"), + @Parameter(name = "end", description = "结束时间"), + @Parameter(name = "dataType", description = "展示数据类型:0-基础信息, 1-原材料采购与运输, 2-生产制造, 3-产品运输, 4-产品使用, 5-废弃处理", required = true), + @Parameter(name = "tenantCode", description = "租户编码"), + }) + public Result getBatchLotDetail(@Parameter(hidden = true) @RequestParam Map params){ + Long productId = MapUtils.getLong(params, "productId"); + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + Integer dataType = Optional.ofNullable(MapUtils.getInteger(params, "dataType")).orElse(0); + Long tenantCode = MapUtils.getLong(params, "tenantCode"); + + if (Objects.isNull(productId) || Objects.isNull(tenantCode)) { + throw new IllegalArgumentException("缺少参数"); + } + + if (Objects.equals(dataType, 0)) { + CarbonPubLibRecordBaseInfoOnYear res = carbonPubProductionResultService.getDetailOfBaseInfoOnYear(productId, tenantCode); + return new Result<>().ok(res); + } + CarbonLifecycleEnum carbonLifecycle = CarbonLifecycleEnum.match(dataType); + switch (carbonLifecycle) { + case MATERIAL_PURCHASE_TRANSPORT -> { + CarbonPubLibRecordMPT res = carbonPubProductionResultService.getDetailOfMPT(productId, null, start, end, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_MANUFACTURE -> { + CarbonPubLibRecordPM res = carbonPubProductionResultService.getDetailOfPM(productId, null, start, end, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_TRANSPORT -> { + CarbonPubLibRecordPT res = carbonPubProductionResultService.getDetailOfPT(productId, null, start, end, tenantCode); + return new Result<>().ok(res); + } + case PRODUCT_USAGE -> { + CarbonPubLibRecordPU res = carbonPubProductionResultService.getDetailOfPU(productId, null, start, end, tenantCode); + return new Result<>().ok(res); + } + case DISPOSE -> { + CarbonPubLibRecordDispose res = carbonPubProductionResultService.getDetailOfDispose(productId, null, start, end, tenantCode); + return new Result<>().ok(res); + } + default -> { + return null; + } + } + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubSupplierController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubSupplierController.java new file mode 100644 index 0000000..2c9f90f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/CarbonPubSupplierController.java @@ -0,0 +1,130 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.CarbonPubSupplierDTO; +import com.thing.carbon.pub.dto.CarbonPubSupplierDetail; +import com.thing.carbon.pub.dto.CarbonPubSupplierProduct; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysRegionDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** +* 产品碳足迹供应商 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@RestController +@RequestMapping("v2/carbon/internal/supplier") +@Tag(name="产品碳足迹供应商") +@RequiredArgsConstructor +public class CarbonPubSupplierController { + + private final CarbonPubSupplierService carbonPubSupplierService; + + @GetMapping("region/list") + @Operation(summary="获取地区列表") + public Result> getRegionList(){ + List list = carbonPubSupplierService.getRegionList(); + return new Result>().ok(list); + } + + @GetMapping("product/types") + @Operation(summary="获取产品列表") + public Result> getProductTypes(){ + Set list = carbonPubSupplierService.getProductTypes(null); + return new Result>().ok(list); + } + + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productType", description = "产品类型"), + @Parameter(name = "name", description = "供应商名称(模糊查询)"), + @Parameter(name = "regionId", description = "地区id"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = carbonPubSupplierService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("detail/summary") + @Operation(summary="汇总信息") + @Parameters({ + @Parameter(name = "id", description = "供应商id", required = true), + }) + public Result detailSummary(@RequestParam Map params){ + CarbonPubSupplierDetail data = carbonPubSupplierService.handleDetailSummary(params); + return new Result().ok(data); + } + + @GetMapping("detail/product") + @Operation(summary="产品信息") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = "id", description = "供应商id", required = true), + }) + public Result> detailProduct(@Parameter(hidden = true) @RequestParam Map params){ + PageData data = carbonPubSupplierService.handleDetailProduct(params); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPubSupplierDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + // 默认碳足迹积分 + if (Objects.isNull(dto.getPoints())) { + dto.setPoints(100); + } + carbonPubSupplierService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPubSupplierDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonPubSupplierService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonPubSupplierService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/IotCarbonEnterpriseAuthController.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/IotCarbonEnterpriseAuthController.java new file mode 100644 index 0000000..5d307a9 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/controller/IotCarbonEnterpriseAuthController.java @@ -0,0 +1,104 @@ +package com.thing.carbon.pub.controller; + +import com.thing.carbon.pub.dto.IotCarbonEnterpriseAuthDTO; +import com.thing.carbon.pub.service.IotCarbonEnterpriseAuthService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 企业注册 +* +* @author xc +* @since 3.0 2024-07-09 +*/ +@RestController +@RequestMapping("v2/enterprisee/iotcarbonenterpriseauth") +@Tag(name="企业注册") +@RequiredArgsConstructor +public class IotCarbonEnterpriseAuthController { + + private final IotCarbonEnterpriseAuthService iotCarbonEnterpriseAuthService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "status", description = "状态"), + @Parameter(name = "tenantName", description = "企业名称") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonEnterpriseAuthService.getPageData(params, IotCarbonEnterpriseAuthDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonEnterpriseAuthDTO data = iotCarbonEnterpriseAuthService.getByIdAs(id, IotCarbonEnterpriseAuthDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonEnterpriseAuthDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setUpdateTime(System.currentTimeMillis()); + return iotCarbonEnterpriseAuthService.saveIotCarbonEnterpriseAuthDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonEnterpriseAuthDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + dto.setUpdateTime(System.currentTimeMillis()); + iotCarbonEnterpriseAuthService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonEnterpriseAuthService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("examine") + @Operation(summary="审核") + public Result examine(@RequestBody IotCarbonEnterpriseAuthDTO dto){ + dto.setUpdateTime(System.currentTimeMillis()); + if(dto.getStatus().equals("2")){ + //审核通过 + iotCarbonEnterpriseAuthService.doSaveTenant(dto); + }else { + //审核不通过 + iotCarbonEnterpriseAuthService.sendMsg(dto); + } + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubCertificateDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubCertificateDTO.java new file mode 100644 index 0000000..1566dd8 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubCertificateDTO.java @@ -0,0 +1,48 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 碳足迹证书 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-23 +*/ +@Data +@Schema(description = "碳足迹证书") +public class CarbonPubCertificateDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "数据时间边界开始") + private Long boundaryStart; + @Schema(description = "数据时间边界截止") + private Long boundaryEnd; + @Schema(description = "认证机构") + private String certificationOrg; + @Schema(description = "核查标准") + private String verificationStd; + @Schema(description = "产品碳足迹值,单位kce") + private BigDecimal carbonVal; + @Schema(description = "生命周期各阶段占比json") + private String stagePercentJson; + @Schema(description = "证书url") + private String certificateUrl; + @Schema(description = "发证日期") + private Long certificateStart; + @Schema(description = "证书到期时间") + private Long certificateEnd; + + @Schema(description = "产品信息json") + private String productJson; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubEngEffDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubEngEffDTO.java new file mode 100644 index 0000000..4c76f9a --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubEngEffDTO.java @@ -0,0 +1,49 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 设备能效标准 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-06-11 +*/ +@Data +@Schema(description = "设备能效标准") +public class CarbonPubEngEffDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品类别") + private String productType; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "能效指标") + private String effKpi; + @Schema(description = "单位") + private String unit; + @Schema(description = "一级分类") + private String topCategory; + @Schema(description = "二级分类") + private String subCategory; + @Schema(description = "分类详情") + private String categoryInfo; + @Schema(description = "先进水平") + private String advLevel; + @Schema(description = "节能水平") + private String engSavingLevel; + @Schema(description = "准入水平") + private String admissionLevel; + @Schema(description = "参考标准") + private String referenceStd; + @Schema(description = "设备图片") + private String deviceUrl; + @Schema(description = "设备备注") + private String remark; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubFootprintInfo.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubFootprintInfo.java new file mode 100644 index 0000000..c08fb96 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubFootprintInfo.java @@ -0,0 +1,58 @@ +package com.thing.carbon.pub.dto; + + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/27 10:19 + * @description 产品碳足迹基础信息 + */ +@Data +@Schema(description = "碳足迹信息") +public class CarbonPubFootprintInfo { + @Schema(description = "报告id") + private String reportId; + + @Schema(description = "企业名称") + private String companyName; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "功能单位") + private String functionUnit; + + @Schema(description = "碳足迹值") + private String totalCarbon; + + @Schema(description = "数据边界类型:1-自然月;2-自然年;3-自定义") + private Integer boundaryType; + + @Schema(description = "是否展示结果:0-不展示,1-展示") + private Integer showResult; + + @Schema(description = "发布时间") + private Long createDate; + + @Schema(description = "有效期截止日") + private Long validEndTime; + + + + @JsonIgnore + private String productJson; + + public void refreshCarbon() { + if (Objects.equals(showResult, 0)) { + setTotalCarbon(null); + } + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfo.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfo.java new file mode 100644 index 0000000..81cb82d --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfo.java @@ -0,0 +1,54 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-基础信息 + */ +@Data +@Schema(description = "产品碳足迹库-记录-基础信息") +public class CarbonPubLibRecordBaseInfo { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品编号(料号)") + private String productCode; + + @Schema(description = "工单编号") + private String prCode; + + @Schema(description = "生产工时") + private Integer prDur; + + @Schema(description = "完工时间") + private String finishTime; + + @Schema(description = "批次产量") + private Integer num; + + @Schema(description = "产品碳足迹总值") + private BigDecimal carbonTotal; + + @Schema(description = "产品碳足迹各生命周期的值") + private Map carbonLifeCycleMap; + + + public Integer getPrDur() { + return Optional.ofNullable(prDur).orElse(0); + } + + public Integer getNum() { + return Optional.ofNullable(num).orElse(0); + } + + public BigDecimal getCarbonTotal() { + return Optional.ofNullable(carbonTotal).orElse(BigDecimal.ZERO); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfoOnYear.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfoOnYear.java new file mode 100644 index 0000000..8b6ad60 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordBaseInfoOnYear.java @@ -0,0 +1,71 @@ +package com.thing.carbon.pub.dto; + +import com.thing.carbontrack.pub.dto.ProductJsonBean; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-基础信息 + */ +@Data +@Schema(description = "产品碳足迹库-记录-基础信息") +public class CarbonPubLibRecordBaseInfoOnYear { + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "所属行业") + private String industryType; + + @Schema(description = "产品分类") + private String productType; + + @Schema(description = "产品尺寸") + private String productionSize; + + @Schema(description = "产品重量") + private BigDecimal productionWeight; + + @Schema(description = "功能单位") + private String functionUnit; + + private String productUrl; + + @Schema(description = "核算边界:1.摇篮到大门,2摇篮到坟墓") + private String boundary; + + @Schema(description = "今年平均产品碳足迹") + private Map carbonAvgMap; + + @Schema(description = "今年最大产品碳足迹") + private Map carbonMaxMap; + + @Schema(description = "今年最小产品碳足迹") + private Map carbonMinMap; + + public static CarbonPubLibRecordBaseInfoOnYear init(ProductJsonBean production) { + CarbonPubLibRecordBaseInfoOnYear res = new CarbonPubLibRecordBaseInfoOnYear(); + res.setProductName(production.getName()); + res.setIndustryType(production.getIndustry()); + res.setProductType(production.getIndustrySub()); + res.setProductionSize(production.getPSize()); + res.setProductionWeight(production.getPWeight()); + res.setFunctionUnit(production.getFUnit()); + res.setBoundary(production.getBoundary()); + res.setProductUrl(production.getUrl()); + return res; + } + + public static CarbonPubLibRecordBaseInfoOnYear init(ProductJsonBean production, Long productId) { + CarbonPubLibRecordBaseInfoOnYear info = init(production); + info.setProductId(productId); + return info; + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordDispose.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordDispose.java new file mode 100644 index 0000000..38bf64b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordDispose.java @@ -0,0 +1,23 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-废弃处理 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema(description = "产品碳足迹库-记录-废弃处理") +public class CarbonPubLibRecordDispose { + + @Schema(description = "基础信息列表") + List baseInfoList; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordMPT.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordMPT.java new file mode 100644 index 0000000..5820421 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordMPT.java @@ -0,0 +1,22 @@ +package com.thing.carbon.pub.dto; + +import com.thing.carbontrack.productionResult.dto.MptDetail; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-原材料获取与运输 + */ +@Data +@Schema(description = "产品碳足迹库-记录-原材料获取与运输") +public class CarbonPubLibRecordMPT { + @Schema(description = "基础信息") + private List baseInfoList; + + @Schema(description = "详情") + private List details; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPM.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPM.java new file mode 100644 index 0000000..487a802 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPM.java @@ -0,0 +1,27 @@ +package com.thing.carbon.pub.dto; + +import com.thing.carbontrack.productionResult.dto.PIndirectDetail; +import com.thing.carbontrack.productionResult.dto.PmProcessDetail; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-生产制造 + */ +@Data +@Schema(description = "产品碳足迹库-记录-生产制造") +public class CarbonPubLibRecordPM { + + @Schema(description = "基础信息列表") + private List baseInfoList; + + @Schema(description = "生产消耗详情") + private List productDetails; + + @Schema(description = "公摊消耗详情") + private List shareDetails; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPT.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPT.java new file mode 100644 index 0000000..9eb9b15 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPT.java @@ -0,0 +1,19 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-产品运输 + */ +@Data +@Schema(description = "产品碳足迹库-记录-产品运输") +public class CarbonPubLibRecordPT { + @Schema(description = "基础信息列表") + List baseInfoList; + +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPU.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPU.java new file mode 100644 index 0000000..2af9628 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordPU.java @@ -0,0 +1,19 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/29 15:08 + * @description 产品碳足迹库-记录-产品使用 + */ +@Data +@Schema(description = "产品碳足迹库-记录-产品使用") +public class CarbonPubLibRecordPU { + + @Schema(description = "基础信息列表") + List baseInfoList; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordR.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordR.java new file mode 100644 index 0000000..fe8b11d --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordR.java @@ -0,0 +1,199 @@ +package com.thing.carbon.pub.dto; + +import com.alibaba.fastjson.JSONArray; +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.common.core.utils.AggUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/6/5 14:51 + * @description 产品碳足迹库-记录-生产核算结果 + */ +@Data +@NoArgsConstructor +@SuppressWarnings("Duplicates") +@Schema(description = "产品碳足迹库-记录-生产核算结果") +public class CarbonPubLibRecordR { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品编码") + private String productCode; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "产品重量") + private BigDecimal productWeight; + + @Schema(description = "产品功能单位") + private String functionUnit; + + @Schema(description = "产品重量单位") + private String weightUnit; + + @Schema(description = "工单号") + private String prCode; + + @Schema(description = "生产时长,单位:h") + private Integer prDur; + + @Schema(description = "生产数量") + private Integer num; + + @Schema(description = "成品(良品)数量") + private Integer finalNum; + + @Schema(description = "开工时间: yyyy—MM-dd") + private String startTime; + + @Schema(description = "完工时间: yyyy—MM-dd") + private String finishTime; + + @Schema(description = "能源品种") + private String evName; + + @Schema(description = "能源品种id|运输方式id") + private Long evId; + + @Schema(description = "碳排类型") + private String carbonType; + + @Schema(description = "碳排放 kgCO2e") + private BigDecimal carbon; + + @Schema(description = "平均碳排放 kgCO2e") + private BigDecimal carbonAvg; + + @Schema(description = "能耗,注:在carbonType=1时,该字段表示运输碳排") + private BigDecimal usage; + + @Schema(description = "能耗平均用量,注:在carbonType=1时,该字段表示平均运输碳排") + private BigDecimal usageAvg; + + @Schema(description = "目前仅用于产品运输页,用于标识合并时是否聚合数据(前端逻辑)") + private Long groupId; + + @Schema(description = "使用年限") + private Integer life; + + @Schema(description = "核算详情") + private JSONArray details; + + /* ---------------------- 废弃运输 ---------------------- */ + @Schema(description = "废弃运输类型id") + private Long fqEvId; + + @Schema(description = "废弃运输类型名称") + private String fqEvName; + + @Schema(description = "废弃运输里程") + private BigDecimal fqUsage; + + @Schema(description = "废弃运输里程平均值") + private BigDecimal fqUsageAvg; + + @Schema(description = "废弃运输碳排") + private BigDecimal fqCarbon; + + @Schema(description = "废弃运输碳排平均值") + private BigDecimal fqCarbonAvg; + + public CarbonPubLibRecordR(CarbonPubProductionResultEntity result) { + if (Objects.nonNull(result)) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + this.productName = result.getProductName(); + this.productCode = result.getProductCode(); + this.productModel = result.getProductModel(); + this.productWeight = result.getProductWeight(); + this.functionUnit = result.getFunctionUnit(); + this.weightUnit = result.getWeightUnit(); + this.prCode = result.getPrCode(); + this.prDur = result.getPrDur(); + this.num = result.getNum(); + this.finalNum = result.getFinalNum(); + this.startTime = sdf.format(result.getStartTime()); + this.finishTime = sdf.format(result.getFinishTime()); + this.evId = result.getEvId(); + this.evName = result.getEvName(); + this.carbonType = result.getCarbonType(); + this.carbon = result.getCarbon(); + this.carbonAvg = result.getCarbonAvg(); + this.usage = result.getUsage(); + this.usageAvg = result.getUsageAvg(); + this.groupId = result.getGroupId(); + this.life = result.getLife(); + this.fqEvId = result.getFqEvId(); + this.fqEvName = result.getFqEvName(); + this.fqUsage = result.getFqUsage(); + this.fqUsageAvg = result.getFqUsageAvg(); + this.fqCarbon = result.getFqCarbon(); + this.fqCarbonAvg = result.getFqCarbonAvg(); + if (StringUtils.isNotBlank(result.getJson())) { + this.details = JSONArray.parseArray(result.getJson()); + } + } + } + + public CarbonPubLibRecordR add(CarbonPubLibRecordR other){ + if (!Objects.equals(getProductCode(), other.getProductCode())) { + return this; + } + setNum(AggUtil.sum(getNum(), other.getNum())); + setFinalNum(AggUtil.sum(getFinalNum(), other.getFinalNum())); + setPrDur(AggUtil.sum(getPrDur(), other.getPrDur())); + setCarbon(AggUtil.sum(getCarbon(), other.getCarbon())); + setUsage(AggUtil.sum(getUsage(), other.getUsage())); + setFqUsage(AggUtil.sum(getFqUsage(), other.getFqUsage())); + setFqCarbon(AggUtil.sum(getFqCarbon(), other.getFqCarbon())); + getDetails().addAll(other.getDetails()); + return this; + } + + public CarbonPubLibRecordR refreshAvg() { + refreshCarbonAvg(); + refreshFqCarbonAvg(); + refreshUsageAvg(); + refreshFqUsageAvg(); + return this; + } + + public CarbonPubLibRecordR refreshCarbonAvg() { + setCarbonAvg(divide(getCarbon(), BigDecimal.valueOf(getFinalNum()))); + return this; + } + + public CarbonPubLibRecordR refreshUsageAvg() { + setUsageAvg(divide(getUsage(), BigDecimal.valueOf(getFinalNum()))); + return this; + } + + public void refreshFqCarbonAvg() { + setFqCarbonAvg(divide(getFqCarbon(), BigDecimal.valueOf(getFinalNum()))); + } + + public void refreshFqUsageAvg() { + setFqUsageAvg(divide(getFqUsage(), BigDecimal.valueOf(getFinalNum()))); + } + + public CarbonPubLibRecordR clearDetail() { + setDetails(null); + return this; + } + + private static BigDecimal divide(BigDecimal value, BigDecimal count) { + if (Objects.isNull(value)) { + return null; + } + return value.divide(count, 2, RoundingMode.HALF_UP); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordSimpleAgg.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordSimpleAgg.java new file mode 100644 index 0000000..6f12198 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubLibRecordSimpleAgg.java @@ -0,0 +1,100 @@ +package com.thing.carbon.pub.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/6/5 14:51 + * @description 产品碳足迹库-简单聚合结果 + */ +@Data +@NoArgsConstructor +@Schema(description = "产品碳足迹库-简单聚合结果") +public class CarbonPubLibRecordSimpleAgg { + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "产品功能单位") + private String functionUnit; + + @Schema(description = "工单号") + private String prCode; + + @Schema(description = "完工时间: yyyy-MM-dd HH:mm:ss") + private String finishTime; + + @Schema(description = "生产数量") + private Integer num; + + @Schema(description = "核算边界") + private String boundary; + + @Schema(description = "数据类型") + private String resultType; + + @Schema(description = "碳足迹值,kgCO2e") + private BigDecimal carbon; + + public CarbonPubLibRecordSimpleAgg(List resultDTOList) { + CarbonPubProductionResultDTO resultDTO = resultDTOList.get(0); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + this.productId = resultDTO.getProductId(); + this.productName = resultDTO.getProductName(); + this.productModel = resultDTO.getProductModel(); + this.functionUnit = resultDTO.getFunctionUnit(); + this.prCode = resultDTO.getPrCode(); + this.finishTime = sdf.format(resultDTO.getFinishTime()); + this.num = resultDTO.getFinalNum(); + this.boundary = resultDTO.getBoundary(); + this.resultType = resultDTO.getResultType(); + this.carbon = calculateCarbon(resultDTOList); + } + + @SuppressWarnings("Duplicates") + private BigDecimal calculateCarbon(List resultDTOList) { + if(CollectionUtils.isEmpty(resultDTOList)){ + return BigDecimal.ZERO; + } + Map carbonTypeMap = + resultDTOList.stream() + .collect( + Collectors.toMap( + CarbonPubProductionResultDTO::getCarbonType, + Function.identity(), + (v1, v2) -> v1)); + + CarbonPubProductionResultDTO resultOfType1 = Optional.ofNullable(carbonTypeMap.get("1")).orElse(new CarbonPubProductionResultDTO()); + CarbonPubProductionResultDTO resultOfType2 = Optional.ofNullable(carbonTypeMap.get("2")).orElse(new CarbonPubProductionResultDTO()); + CarbonPubProductionResultDTO resultOfType3 = Optional.ofNullable(carbonTypeMap.get("3")).orElse(new CarbonPubProductionResultDTO()); + CarbonPubProductionResultDTO resultOfType4 = Optional.ofNullable(carbonTypeMap.get("4")).orElse(new CarbonPubProductionResultDTO()); + CarbonPubProductionResultDTO resultOfType5 = Optional.ofNullable(carbonTypeMap.get("5")).orElse(new CarbonPubProductionResultDTO()); + CarbonPubProductionResultDTO resultOfType6 = Optional.ofNullable(carbonTypeMap.get("6")).orElse(new CarbonPubProductionResultDTO()); + + return resultOfType1.getCarbonAvg() + .add(resultOfType1.getUsageAvg()) + .add(resultOfType2.getCarbonAvg()) + .add(resultOfType3.getCarbonAvg()) + .add(resultOfType4.getCarbonAvg()) + .add(resultOfType5.getCarbonAvg()) + .add(resultOfType6.getCarbonAvg()) + .add(resultOfType6.getFqCarbonAvg()); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorApiDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorApiDTO.java new file mode 100644 index 0000000..4ae5ed9 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorApiDTO.java @@ -0,0 +1,14 @@ +package com.thing.carbon.pub.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** 碳排因子库api接口返回对象 */ +@Data +@EqualsAndHashCode(callSuper = true) +public class CarbonPubMaterialFactorApiDTO extends CarbonPubMaterialFactorDTO { + @Serial private static final long serialVersionUID = 573873391021835356L; + private Integer points; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorDTO.java new file mode 100644 index 0000000..13abcf0 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorDTO.java @@ -0,0 +1,50 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 碳排因子库 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-11 +*/ +@Data +@Schema(description = "碳排因子库") +public class CarbonPubMaterialFactorDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private String id; + @Schema(description = "地理位置简称") + private String geoShort; + @Schema(description = "地理位置全称") + private String geoFull; + @Schema(description = "地理位置全程(中文)") + private String geoFullCn; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "产品名称(中文)") + private String productNameCn; + @Schema(description = "产品单位") + private String productUnit; + @Schema(description = "碳排因子") + private BigDecimal factor; + @Schema(description = "参考单元") + private Integer refCount; + @Schema(description = "碳排因子库名称") + private String sourceName; + @Schema(description = "开始年份") + private String startYear; + @Schema(description = "截止年份") + private String endYear; + @Schema(description = "行业") + private String industry; + @Schema(description = "行业(中文)") + private String industryCn; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorUseDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorUseDTO.java new file mode 100644 index 0000000..576703b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubMaterialFactorUseDTO.java @@ -0,0 +1,29 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 原材料碳排因子库使用记录 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-17 +*/ +@Data +@Schema(description = "原材料碳排因子库使用记录") +public class CarbonPubMaterialFactorUseDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "碳排因子库主键") + private String materialFactorId; + @Schema(description = "使用该因子的企业租户编码") + private Long tenantCode; + @Schema(description = "碳排因子下载时间") + private Long useTime; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsLogsDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsLogsDTO.java new file mode 100644 index 0000000..300899e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsLogsDTO.java @@ -0,0 +1,32 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 积分增减记录 +* +* @author xc +* @since 3.0 2024-06-17 +*/ +@Data +@Schema(description = "积分增减记录") +public class CarbonPubPointsLogsDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键id") + private Long id; + @Schema(description = "类型,中文描述") + private String type; + @Schema(description = "增减积分值") + private Integer points; + @Schema(description = "供应商编码") + private Long tenantCode; + @Schema(description = "创建时间") + private Long createTime; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsRuleDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsRuleDTO.java new file mode 100644 index 0000000..2a52f98 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubPointsRuleDTO.java @@ -0,0 +1,35 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 碳足迹积分规则 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Data +@Schema(description = "碳足迹积分规则") +public class CarbonPubPointsRuleDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "模型共享积分") + private Integer modelSharedPoints; + @Schema(description = "模型点击积分") + private Integer modelClickPoints; + @Schema(description = "结果数据共享积分") + private Integer resultSharedPoints; + @Schema(description = "过程数据共享积分") + private Integer processSharedPoints; + @Schema(description = "碳足迹认证积分") + private Integer footprintCertifyPoints; + @Schema(description = "碳足迹因子首次使用消耗积分") + private Integer factorUsePoints; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionIdName.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionIdName.java new file mode 100644 index 0000000..f8064ee --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionIdName.java @@ -0,0 +1,19 @@ +package com.thing.carbon.pub.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author siyang + * @date 2024/6/11 11:48 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CarbonPubProductionIdName { + private Long productId; + private String productName; + private Long tenantCode; + private String companyName; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionModelDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionModelDTO.java new file mode 100644 index 0000000..6d10548 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionModelDTO.java @@ -0,0 +1,56 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 产品详情 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Data +@Schema(description = "产品详情") +public class CarbonPubProductionModelDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "产品编码") + private String productCode; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "产品型号") + private String productModel; + @Schema(description = "产品图片") + private String url; + @Schema(description = "功能单位") + private String functionUnit; + @Schema(description = "产品基本信息json") + private String productJson; + @Schema(description = "原料信息json") + private String materialJson; + @Schema(description = "产品工艺工序json") + private String processJson; + @Schema(description = "工序预览图url") + private String stepsUrl; + @Schema(description = "工序图的坐标json") + private String stepsJson; + @Schema(description = "行业一级分类,企业详情的industry_type") + private String industryType; + @Schema(description = "行业二级分类,企业详情的industry_category") + private String industryCategory; + @Schema(description = "产品类型") + private String productType; + @Schema(description = "下载次数") + private Integer downloadCount; + @Schema(description = "发布时间") + private Long createDate; + @Schema(description = "最后更新时间") + private Long updateDate; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportConfigDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportConfigDTO.java new file mode 100644 index 0000000..a85655b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportConfigDTO.java @@ -0,0 +1,36 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 碳足迹报告配置 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-05-27 +*/ +@Data +@Schema(description = "碳足迹报告配置") +public class CarbonPubProductionReportConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "企业租户编码") + private Long tenantCode; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "数据边界类型:1-自然月;2-自然年;3-自定义") + private Integer boundaryType; + @Schema(description = "数据边界开始时间:只有自定义边界才有值") + private Long boundaryStart; + @Schema(description = "数据边界结束时间:只有自定义边界才有值") + private Long boundaryEnd; + @Schema(description = "是否展示结果") + private Integer showResult; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportDTO.java new file mode 100644 index 0000000..2918129 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionReportDTO.java @@ -0,0 +1,70 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Objects; + +/** +* 产品碳足迹报告 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-05-28 +*/ +@Data +@Schema(description = "产品碳足迹报告") +public class CarbonPubProductionReportDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "产品模型") + private String productModel; + @Schema(description = "碳足报告配置id") + private Long configId; + @Schema(description = "功能单位") + private String functionUnit; + @Schema(description = "系统边界类型:1-摇篮到大门,2-摇篮到坟墓") + private String boundary; + @Schema(description = "数据边界类型:1-自然月;2-自然年;3-自定义") + private Integer boundaryType; + @Schema(description = "数据边界开始时间") + private Date boundaryStart; + @Schema(description = "数据边界结束时间") + private Date boundaryEnd; + @Schema(description = "碳足迹值") + private BigDecimal totalCarbon; + @Schema(description = "各阶段碳足迹占比") + private String stagePercentJson; + @Schema(description = "其他扩展数据") + private String extra; + @Schema(description = "发布时间") + private Long createDate; + @Schema(description = "报告有效期") + private Long validEndTime; + @Schema(description = "是否在碳足迹库展示结果:0-不展示,1-展示") + private Integer showResult; + @Schema(description = "企业名称") + private String companyName; + @Schema(description = "证书编号") + private String reportCode; + @Schema(description = "报告来源类型:填报、实采、证书上传") + private String sourceType; + @Schema(description = "企业编码") + private Long tenantCode; + + public void refreshCarbon() { + if (Objects.equals(showResult, 0)) { + setTotalCarbon(null); + } + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultDTO.java new file mode 100644 index 0000000..c4507d8 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultDTO.java @@ -0,0 +1,129 @@ +package com.thing.carbon.pub.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Optional; + +/** +* 生产记录核算结果 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Data +@Schema(description = "生产记录核算结果") +public class CarbonPubProductionResultDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @Schema(description = "数据主键id") + private Long id; + + @Schema(description = "原始数据id") + private Long oriId; + + @Schema(description = "产品id") + private Long productId; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "产品编码") + private String productCode; + @Schema(description = "产品型号") + private String productModel; + @Schema(description = "产品重量") + private BigDecimal productWeight; + @Schema(description = "功能单位") + private String functionUnit; + @Schema(description = "重量单位") + private String weightUnit; + + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "生产耗时,单位:h") + private Integer prDur; + @Schema(description = "生产数量") + private Integer num; + @Schema(description = "成品数量") + private Integer finalNum; + @Schema(description = "开工时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date startTime; + @Schema(description = "完工时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") + private Date finishTime; + + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源品种名称") + private String evName; + @Schema(description = "能耗总用量") + private BigDecimal usage; + @Schema(description = "能耗平均用量") + private BigDecimal usageAvg; + @Schema(description = "总能耗") + private BigDecimal tce; + + @Schema(description = "总碳排") + private BigDecimal carbon; + @Schema(description = "平均碳排") + private BigDecimal carbonAvg; + + + @Schema(description = "碳排类型 1 原料 2 原料运输 3 产品生产 4 产品公摊/间接 5 产品运输 6 产品使用 7 产品废弃处理") + private String carbonType; + @Schema(description = "同步数据类型 1-结果数据 2-过程数据") + private String resultType; + @Schema(description = "系统边界: 1-摇篮到大门,2-摇篮到坟墓") + private String boundary; + + @Schema(description = "详情json,一个类型,一个能源,形成一个详情json") + private String json; + + @Schema(description = "产品运输的时候") + private Long groupId; + @Schema(description = "废弃运输里程") + private BigDecimal fqUsage; + @Schema(description = "废弃运输碳排") + private BigDecimal fqCarbon; + @Schema(description = "废弃运输里程平均值") + private BigDecimal fqUsageAvg; + @Schema(description = "废弃运输碳排平均值") + private BigDecimal fqCarbonAvg; + @Schema(description = "废弃运输类型id") + private Long fqEvId; + @Schema(description = "废弃运输类型名称") + private String fqEvName; + @Schema(description = "使用年限") + private Integer life; + @Schema(description = "数据来源类型: 1-实采, 2-填报") + private String sourceType; + + public BigDecimal getCarbonAvg() { + return Optional.ofNullable(carbonAvg).orElse(BigDecimal.ZERO); + } + + public BigDecimal getUsageAvg() { + return Optional.ofNullable(usageAvg).orElse(BigDecimal.ZERO); + } + + public BigDecimal getFqCarbonAvg() { + return Optional.ofNullable(fqCarbonAvg).orElse(BigDecimal.ZERO); + } + + public Integer getPrDur() { + return Optional.ofNullable(prDur).orElse(0); + } + + public Integer getNum() { + return Optional.ofNullable(num).orElse(0); + } + + public BigDecimal getCarbon() { + return Optional.ofNullable(carbon).orElse(BigDecimal.ZERO); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultReport.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultReport.java new file mode 100644 index 0000000..593364b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubProductionResultReport.java @@ -0,0 +1,74 @@ +package com.thing.carbon.pub.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +import static java.math.RoundingMode.HALF_UP; + +/** + * @author siyang + * @date 2024/5/31 09:28 + * @description 数据库查询结果:从result表聚合出的report数据 + */ +@Data +public class CarbonPubProductionResultReport { + + /** 系统边界类型:1-摇篮到大门,2-摇篮到坟墓 */ + private String boundary; + + /** 碳足迹总值 */ + private BigDecimal totalCarbon; + + /** 碳足迹:原料获取阶段 */ + private BigDecimal mptCarbon; + + /** 碳足迹:生产制造阶段 */ + private BigDecimal pmCarbon; + + /** 碳足迹:产品运输阶段 */ + private BigDecimal ptCarbon; + + /** 碳足迹:产品使用阶段 */ + private BigDecimal puCarbon; + + /** 碳足迹:废弃处理阶段 */ + private BigDecimal disposeCarbon; + + /** 企业名称 */ + private String companyName; + + /** 数据源类型 */ + private String sourceType; + + public BigDecimal summingCarbon() { + BigDecimal totalCarbon = + mptCarbon.add(pmCarbon).add(ptCarbon).add(puCarbon).add(disposeCarbon); + setTotalCarbon(totalCarbon.setScale(4, HALF_UP)); + return this.totalCarbon; + } + + public BigDecimal calcMptPercent() { + return calcPercent(mptCarbon); + } + + public BigDecimal calcPmPercent() { + return calcPercent(pmCarbon); + } + + public BigDecimal calcPtPercent() { + return calcPercent(ptCarbon); + } + + public BigDecimal calcPuPercent() { + return calcPercent(puCarbon); + } + + public BigDecimal calcDisposePercent() { + return calcPercent(disposeCarbon); + } + + private BigDecimal calcPercent(BigDecimal carbonVal) { + return carbonVal.divide(totalCarbon, 4, HALF_UP).multiply(BigDecimal.valueOf(100)); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDTO.java new file mode 100644 index 0000000..86a339f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDTO.java @@ -0,0 +1,51 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 产品碳足迹供应商 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Data +@Schema(description = "产品碳足迹供应商") +public class CarbonPubSupplierDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "供应商名称") + private String name; + @Schema(description = "供应商编码(tenant_code)") + private Long code; + @Schema(description = "供应商令牌") + private String accessToken; + @Schema(description = "地区id(sys_region表id)") + private Long regionId; + @Schema(description = "产品分类,按逗号分隔") + private String productType; + @Schema(description = "剩余积分") + private Integer points; + @Schema(description = "碳足迹记录数量") + private Long recordsCount; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDetail.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDetail.java new file mode 100644 index 0000000..d31d055 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierDetail.java @@ -0,0 +1,32 @@ +package com.thing.carbon.pub.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Set; + +/** + * @author siyang + * @date 2024/6/18 10:11 + * @description 供应商碳足迹详情 + */ +@Data +@Schema(description = "供应商碳足迹详情") +public class CarbonPubSupplierDetail { + @Schema(description = "企业名称") + private String companyName; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "产品类型") + private Set productTypes; + + @Schema(description = "平均碳足迹") + private BigDecimal avgCarbon; + + @Schema(description = "碳足迹记录数量") + private Long recordsCount; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierProduct.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierProduct.java new file mode 100644 index 0000000..177c018 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/CarbonPubSupplierProduct.java @@ -0,0 +1,36 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024/6/18 10:11 + * @description 供应商产品信息碳足迹详情 + */ +@Data +@Schema(description = "供应商产品信息") +public class CarbonPubSupplierProduct { + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品类型") + private String productModel; + + @Schema(description = "功能单位") + private String functionUnit; + + @Schema(description = "核算边界") + private String boundary; + + @Schema(description = "当前产品碳足迹记录数量") + private Long recordsCount; + + @Schema(description = "当前产品平均碳足迹") + private BigDecimal avgCarbon; +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/IotCarbonEnterpriseAuthDTO.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/IotCarbonEnterpriseAuthDTO.java new file mode 100644 index 0000000..6ed3c1e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/IotCarbonEnterpriseAuthDTO.java @@ -0,0 +1,56 @@ +package com.thing.carbon.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 企业注册 +* +* @author xc +* @since 3.0 2024-07-09 +*/ +@Data +@Schema(description = "企业注册") +public class IotCarbonEnterpriseAuthDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "用户账号") + private String username; + @Schema(description = "用户密码") + private String password; + @Schema(description = "企业名称") + private String tenantName; + @Schema(description = "行业门类编码") + private String industryCategory; + @Schema(description = "行业大类编码") + private String industryType; + @Schema(description = "区域编码") + private String regionCode; + @Schema(description = "详细地址") + private String address; + @Schema(description = "经纬度") + private String lngLat; + @Schema(description = "统一社会信用编码") + private String corporationCode; + @Schema(description = "营业执照扫描件") + private String businessLicense; + @Schema(description = "联系人") + private String contact; + @Schema(description = "联系电话") + private String contactNumber; + @Schema(description = "企业邮箱") + private String email; + @Schema(description = "审核状态 1待审核,2审核通过,3审核不通过") + private String status; + @Schema(description = "审核意见") + private String opinion; + @Schema(description = "更新时间") + private Long updateTime; + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateRequest.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateRequest.java new file mode 100644 index 0000000..a3873b0 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateRequest.java @@ -0,0 +1,81 @@ +package com.thing.carbon.pub.dto; + +import com.thing.common.core.rest.dto.ApiRequest; +import com.thing.common.core.utils.EncryptUtils; +import com.thing.common.core.utils.RandomUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/11 15:03 + * @description 百度翻译请求 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class TranslateRequest extends ApiRequest { + + @Serial private static final long serialVersionUID = -2149110530712711761L; + + private String url = "http://api.fanyi.baidu.com/api/trans/vip/translate"; + + private String appId = "20240510002047650"; + + private String apiSecret = "oN16vuXmn5dYAMuZ4nlm"; + + /** 需要翻译的字符:单次请求长度控制在 6000 bytes以内 */ + private String query; + + /** 原语言:en */ + private String from; + + /** 翻译语言:zh */ + private String to; + + /** + * 签名:
+ * Step1. 拼接字符串1:
+ * 拼接appid=2015063000000001+q=apple+salt=1435660288+密钥=12345678得到字符串1:“2015063000000001apple143566028812345678” + *
+ * Step2. 计算签名:(对字符串1做MD5加密)
+ * sign=MD5(2015063000000001apple143566028812345678),得到sign=f89f9594663708c1605f3d736d01d2d4
+ */ + private String sign; + + public TranslateRequest(String query, String from, String to) { + this.query = query; + this.from = from; + this.to = to; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + // 不要修改map参数添加的顺序 + Map params = new LinkedHashMap<>(); + params.put("appid", appId); + params.put("q", query); + params.put("salt", RandomUtils.getRandomString()); + params.put("sign", calculateSign(params)); + params.put("from", from); + params.put("to", to); + return params; + } + + private String calculateSign(Map requestMap) { + StringBuilder params = new StringBuilder(); + requestMap.forEach((k, v) -> params.append(v)); + params.append(apiSecret); + return EncryptUtils.md5Encrypt(params.toString()).toLowerCase(); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateResponse.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateResponse.java new file mode 100644 index 0000000..2fd5596 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/dto/TranslateResponse.java @@ -0,0 +1,42 @@ +package com.thing.carbon.pub.dto; + +import com.thing.common.core.rest.dto.ApiResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.util.List; + +/** + * @author siyang + * @date 2024/5/11 15:03 + * @description 百度翻译请求响应 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TranslateResponse extends ApiResponse { + + @Serial private static final long serialVersionUID = 4069046173619534611L; + + private String from; + private String to; + private List trans_result; + + private String error_code; + private String error_msg; + + @Data + public static class Result { + /** 原数据 */ + private String src; + + /** 翻译结果 */ + private String dst; + } + + @Override + public boolean failed() { + return StringUtils.isNotBlank(error_code); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubCertificateEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubCertificateEntity.java new file mode 100644 index 0000000..2d8eef7 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubCertificateEntity.java @@ -0,0 +1,67 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_certificate") +public class CarbonPubCertificateEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品id + */ + private Long productId; + /** + * 数据时间边界开始 + */ + private Long boundaryStart; + /** + * 数据时间边界截止 + */ + private Long boundaryEnd; + /** + * 认证机构 + */ + private String certificationOrg; + /** + * 核查标准 + */ + private String verificationStd; + /** + * 产品碳足迹值,单位kce + */ + private BigDecimal carbonVal; + /** + * 生命周期各阶段占比json + */ + private String stagePercentJson; + /** + * 证书url + */ + private String certificateUrl; + /** + * 发证日期 + */ + private Long certificateStart; + /** + * 证书到期时间 + */ + private Long certificateEnd; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubEngEffEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubEngEffEntity.java new file mode 100644 index 0000000..6562e67 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubEngEffEntity.java @@ -0,0 +1,78 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 设备能效标准 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-06-11 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_eng_eff") +public class CarbonPubEngEffEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品类别 + */ + private String productType; + /** + * 产品类别 + */ + private String productName; + /** + * 能效指标 + */ + private String effKpi; + /** + * 单位 + */ + private String unit; + /** + * 一级分类 + */ + private String topCategory; + /** + * 二级分类 + */ + private String subCategory; + /** + * 分类详情 + */ + private String categoryInfo; + /** + * 先进水平 + */ + private String advLevel; + /** + * 节能水平 + */ + private String engSavingLevel; + /** + * 准入水平 + */ + private String admissionLevel; + /** + * 参考标准 + */ + private String referenceStd; + /** + * 设备图片 + */ + private String deviceUrl; + /** + * 设备备注 + */ + private String remark; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorEntity.java new file mode 100644 index 0000000..135b06c --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorEntity.java @@ -0,0 +1,85 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 碳排因子库 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-11 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_material_factor") +public class CarbonPubMaterialFactorEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Id + private String id; + /** + * 地理位置简称 + */ + private String geoShort; + /** + * 地理位置全称 + */ + private String geoFull; + /** + * 地理位置全程(中文) + */ + private String geoFullCn; + /** + * 产品名称 + */ + private String productName; + /** + * 产品名称中文 + */ + private String productNameCn; + /** + * 产品单位 + */ + private String productUnit; + /** + * 碳排因子 + */ + private BigDecimal factor; + /** + * 参考单元 + */ + private Integer refCount; + /** + * 排放因子库名称 + */ + private String sourceName; + /** + * 因子热度:单独查询次数 + */ + private Integer hotScore; + /** + * 开始年份 + */ + private String startYear; + /** + * 截止年份 + */ + private String endYear; + /** + * 行业 + */ + private String industry; + /** + * 行业(中文) + */ + private String industryCn; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorUseEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorUseEntity.java new file mode 100644 index 0000000..1be1266 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubMaterialFactorUseEntity.java @@ -0,0 +1,40 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 原材料碳排因子库使用记录 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-17 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_material_factor_use") +public class CarbonPubMaterialFactorUseEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 碳排因子库主键 + */ + private String materialFactorId; + /** + * 使用该因子的企业租户编码 + */ + private Long tenantCode; + /** + * 碳排因子下载时间 + */ + private Long useTime; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsLogsEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsLogsEntity.java new file mode 100644 index 0000000..1a8b4cb --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsLogsEntity.java @@ -0,0 +1,47 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 积分增减记录 + * + * @author xc + * @since 3.0 2024-06-17 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_points_logs") +public class CarbonPubPointsLogsEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @Id + private Long id; + /** + * 类型,中文描述 + */ + private String type; + /** + * 增减积分值 + */ + private Integer points; + /** + * 供应商编码 + */ + private Long tenantCode; + /** + * 创建时间 + */ + private Long createTime; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsRuleEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsRuleEntity.java new file mode 100644 index 0000000..354de63 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubPointsRuleEntity.java @@ -0,0 +1,52 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 碳足迹积分规则 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_points_rule") +public class CarbonPubPointsRuleEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 模型共享积分 + */ + private Integer modelSharedPoints; + /** + * 模型点击积分 + */ + private Integer modelClickPoints; + /** + * 结果数据共享积分 + */ + private Integer resultSharedPoints; + /** + * 过程数据共享积分 + */ + private Integer processSharedPoints; + /** + * 碳足迹认证积分 + */ + private Integer footprintCertifyPoints; + /** + * 碳足迹因子首次使用消耗积分 + */ + private Integer factorUsePoints; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionModelEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionModelEntity.java new file mode 100644 index 0000000..6d419ef --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionModelEntity.java @@ -0,0 +1,89 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 产品详情 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_production_model") +public class CarbonPubProductionModelEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 产品id + */ + private Long productId; + /** + * 产品编码 + */ + private String productCode; + /** + * 产品名称 + */ + private String productName; + /** + * 产品型号 + */ + private String productModel; + /** + * 产品图片 + */ + private String url; + /** + * 功能单位 + */ + private String functionUnit; + /** + * 产品基本信息json + */ + private String productJson; + /** + * 原料信息json + */ + private String materialJson; + /** + * 产品工艺工序json + */ + private String processJson; + /** + * 工序预览图url + */ + private String stepsUrl; + /** + * 工序图的坐标json + */ + private String stepsJson; + /** + * 行业一级分类,企业详情的industry_type + */ + private String industryType; + /** + * 行业二级分类,企业详情的industry_category + */ + private String industryCategory; + /** + * 产品类型 + */ + private String productType; + /** + * 下载次数 + */ + private Integer downloadCount; + /** + * 模型是否在社区展示:0-不展示,1-展示 + */ + private Integer showInCommunity; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportConfigEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportConfigEntity.java new file mode 100644 index 0000000..2ca7b1e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportConfigEntity.java @@ -0,0 +1,55 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 碳足迹报告配置 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-05-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_production_report_config") +public class CarbonPubProductionReportConfigEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id + private Long id; + /** + * 企业租户编码 + */ + private Long tenantCode; + /** + * 产品id + */ + private Long productId; + /** + * 数据边界类型:1-自然月;2-自然年;3-自定义 + */ + private Integer boundaryType; + /** + * 数据边界开始时间:只有自定义边界才有值 + */ + private Long boundaryStart; + /** + * 数据边界结束时间:只有自定义边界才有值 + */ + private Long boundaryEnd; + /** + * 是否展示结果 + */ + private Integer showResult; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportEntity.java new file mode 100644 index 0000000..2da3df8 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionReportEntity.java @@ -0,0 +1,82 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 产品碳足迹报告 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-05-28 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_production_report") +public class CarbonPubProductionReportEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品id + */ + private Long productId; + /** + * 碳足报告配置id + */ + private Long configId; + /** + * 系统边界类型:1-摇篮到大门,2-摇篮到坟墓 + */ + private String boundary; + /** + * 数据边界类型:1-自然月;2-自然年;3-自定义 + */ + private Integer boundaryType; + /** + * 数据边界开始时间 + */ + private Date boundaryStart; + /** + * 数据边界结束时间 + */ + private Date boundaryEnd; + /** + * 报告有效期 + */ + private Long validEndTime; + /** + * 碳足迹总值 + */ + private BigDecimal totalCarbon; + /** + * 各阶段碳足迹占比 + */ + private String stagePercentJson; + /** + * 企业名称 + */ + private String companyName; + /** + * 证书编号 + */ + private String reportCode; + /** + * 报告来源类型:填报、实采、证书上传 + */ + private String sourceType; + /** + * 是否在碳足迹库展示结果:0-不展示,1-展示 + */ + private Integer showResult; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionResultEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionResultEntity.java new file mode 100644 index 0000000..0ff6f35 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubProductionResultEntity.java @@ -0,0 +1,173 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 生产记录核算结果 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_production_result") +public class CarbonPubProductionResultEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 原始数据id + */ + private Long oriId; + + /** + * 产品id + */ + private Long productId; + /** + * 产品名称 + */ + private String productName; + /** + * 产品编码 + */ + private String productCode; + /** + * 产品型号 + */ + private String productModel; + /** + * 产品重量 + */ + private BigDecimal productWeight; + /** + * 功能单位 + */ + private String functionUnit; + /** + * 产品重量计量单位 + */ + private String weightUnit; + /** + * 生产记录编码 + */ + private String prCode; + /** + * 生产耗时,单位:h + */ + private Integer prDur; + /** + * 生产数量 + */ + private Integer num; + /** + * 成品数量 + */ + private Integer finalNum; + /** + * 能源品种id + */ + private Long evId; + /** + * 能源品种名称 + */ + private String evName; + /** + * 能耗总用量 + */ + private BigDecimal usage; + /** + * 能耗平均用量 + */ + private BigDecimal usageAvg; + /** + * 总碳排 + */ + private BigDecimal carbon; + /** + * 平均碳排 + */ + private BigDecimal carbonAvg; + /** + * 总能耗 + */ + private BigDecimal tce; + /** + * 碳排类型 + * 1 原料 + * 2 原料运输 + * 3 产品生产 + * 4 产品公摊/间接 + * 5 产品运输 + * 6 产品使用 + * 7 产品废弃处理 + */ + private String carbonType; + /** + * 结果类型:1-结果数据 2-过程数据 + */ + private String resultType; + /** + * 系统边界: 1-摇篮到大门,2-摇篮到坟墓 + */ + private String boundary; + /** + * 开始日期 + */ + private Date startTime; + /** + * 完工日期 + */ + private Date finishTime; + /** + * 详情json,一个类型,一个能源,形成一个详情json + */ + private String json; + + /** + * 产品运输的时候,提交的数据组id 同一组数据 数量只聚合一次 + */ + private Long groupId; + /** + * 废弃运输里程 + */ + private BigDecimal fqUsage; + /** + * 废弃运输碳排 + */ + private BigDecimal fqCarbon; + /** + * 废弃运输里程平均值,不平均,= 废弃运输里程 + */ + private BigDecimal fqUsageAvg; + /** + * 废弃运输碳排平均值 + */ + private BigDecimal fqCarbonAvg; + /** + * 废弃运输类型id + */ + private Long fqEvId; + /** + * 废弃运输类型名称 + */ + private String fqEvName; + /** + * 使用年限 + */ + private Integer life; + /** + * 数据来源类型: 1-实采, 2-填报 + */ + private String sourceType; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubSupplierEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubSupplierEntity.java new file mode 100644 index 0000000..f0a9c73 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/CarbonPubSupplierEntity.java @@ -0,0 +1,53 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 产品碳足迹供应商 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_pub_supplier") +public class CarbonPubSupplierEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + + /** + * 供应商名称 + */ + private String name; + /** + * 供应商编码(tenant_code) + */ + private Long code; + /** + * 供应商令牌 + */ + private String accessToken; + /** + * 地区id(sys_region表id) + */ + private Long regionId; + /** + * 产品分类,按逗号分隔 + */ + private String productType; + /** + * 剩余积分 + */ + private Integer points; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/IotCarbonEnterpriseAuthEntity.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/IotCarbonEnterpriseAuthEntity.java new file mode 100644 index 0000000..0ccde43 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/entity/IotCarbonEnterpriseAuthEntity.java @@ -0,0 +1,95 @@ +package com.thing.carbon.pub.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 企业注册 + * + * @author xc + * @since 3.0 2024-07-09 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_enterprise_auth") +public class IotCarbonEnterpriseAuthEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 用户账号 + */ + private String username; + /** + * 用户密码 + */ + private String password; + /** + * 企业名称 + */ + private String tenantName; + /** + * 行业门类 + */ + private String industryCategory; + /** + * 行业大类 + */ + private String industryType; + /** + * 区域编码 + */ + private String regionCode; + /** + * 详细地址 + */ + private String address; + /** + * 经纬度 + */ + private String lngLat; + /** + * 统一社会信用编码 + */ + private String corporationCode; + /** + * 营业执照扫描件 + */ + private String businessLicense; + /** + * 联系人 + */ + private String contact; + /** + * 联系电话 + */ + private String contactNumber; + /** + * 企业邮箱 + */ + private String email; + /** + * 审核状态 + */ + private String status; + /** + * 审核意见 + */ + private String opinion; + + @Schema(description = "更新时间") + private Long updateTime; +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilter.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilter.java new file mode 100644 index 0000000..6a5c8b2 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilter.java @@ -0,0 +1,166 @@ +package com.thing.carbon.pub.filter; + +import com.alibaba.fastjson.JSONObject; +import com.thing.carbon.pub.constant.ApiConstant; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.common.core.enumeration.RsaKeyType; +import com.thing.common.core.utils.EncryptUtils; +import com.thing.common.core.web.response.Result; +import jakarta.servlet.*; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.web.servlet.ShiroHttpServletRequest; +import org.apache.shiro.web.servlet.ShiroHttpServletResponse; + +import java.nio.charset.StandardCharsets; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/13 15:02 + * @description 国网接口请求过滤器 + */ +@Slf4j +@RequiredArgsConstructor +public class CarbonPubFilter implements Filter { + + private final String apiSecret; + + private final CarbonPubSupplierService carbonPubSupplierService; + + private final Result missingTokenError = new Result().error(400, "Invalid parameter:缺少accessToken"); + private final Result invalidTokenError = new Result().error(400, "Invalid parameter:非法token"); + private final Result missingTsError = new Result().error(400, "Invalid parameter: 缺少timestamp"); + private final Result invalidTsError = new Result().error(400, "Invalid parameter: 请求已过期"); + private final Result missingSignError = new Result().error(400, "Invalid parameter: 缺少sign"); + private final Result unAuthedError = new Result().error(401, "签名验证失败"); + + @Override + @SneakyThrows + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) { + CarbonPubRequestWrapper requestWrapper = new CarbonPubRequestWrapper((ShiroHttpServletRequest) request); + CarbonPubResponseWrapper responseWrapper = new CarbonPubResponseWrapper((ShiroHttpServletResponse) response); + String accessToken = request.getParameter(ApiConstant.ACCESS_TOKEN); + String timestamp = request.getParameter(ApiConstant.TIMESTAMP); + String sign = request.getParameter(ApiConstant.SIGN); + + // 校验 + if (!valid(requestWrapper, response, accessToken, timestamp, sign)) { + return; + } + + // post请求需要对请求体解密 + if (isPost(requestWrapper)) { + String requestBody = requestWrapper.getBody(); + String decryptedBody = decryptRequest(requestBody); + requestWrapper.setBody(decryptedBody); + } + + chain.doFilter(requestWrapper, responseWrapper); + + // 响应结果处理 + byte[] content = responseWrapper.getContent(); + if (content.length == 0) { + return; + } + String originalResponseData = new String(content, StandardCharsets.UTF_8); + ServletOutputStream out = response.getOutputStream(); + + // post请求和错误的响应信息不需要加密 + String finalResponse = + isPost(requestWrapper) || isFailed(originalResponseData) + ? originalResponseData + : encryptResponse(originalResponseData); + out.write(finalResponse.getBytes(StandardCharsets.UTF_8)); + out.flush(); + out.close(); + } + + private boolean valid( + CarbonPubRequestWrapper requestWrapper, + ServletResponse response, + String accessToken, + String timestamp, + String sign) { + // 1. token验证 + if (StringUtils.isBlank(accessToken)) { + writeResponse(response, missingTokenError); + return false; + } else { + CarbonPubSupplierEntity supplier = carbonPubSupplierService.getByAccessToken(accessToken); + if (Objects.isNull(supplier)) { + writeResponse(response, invalidTokenError); + return false; + } + requestWrapper.setAttribute(ApiConstant.SUPPLIER, supplier); + } + + // 2. 时间戳验证 + if (StringUtils.isBlank(timestamp)) { + writeResponse(response, missingTsError); + return false; + } else { + long now = System.currentTimeMillis(); + long requestTime = Long.parseLong(timestamp); + long oneDayDurationMillis = 24 * 60 * 60 * 1000L; + if (Math.abs(now - requestTime) > oneDayDurationMillis) { + writeResponse(response, invalidTsError); + return false; + } + } + + // 3. 签名验证 + if (StringUtils.isBlank(sign)) { + writeResponse(response, missingSignError); + return false; + } else { + String secretStr = accessToken + "." + apiSecret + "." + timestamp; + String calcSign = EncryptUtils.md5Encrypt(secretStr).toUpperCase(); + if(!Objects.equals(sign, calcSign)){ + writeResponse(response, unAuthedError); + return false; + } + } + return true; + } + + @SneakyThrows + private void writeResponse(ServletResponse response, Result error) { + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write(JSONObject.toJSONString(error)); + response.flushBuffer(); + } + + private String decryptRequest(String requestBody) { + JSONObject jsonBody = JSONObject.parseObject(requestBody); + String encryptedAesKey = jsonBody.getString("encryptedAesKey"); + String data = jsonBody.getString("data"); + String pemPrivateRsaKey = EncryptUtils.getPemRsaKeyFromResource(RsaKeyType.PRIVATE); + String aesKey = EncryptUtils.rsaDecrypt(encryptedAesKey, pemPrivateRsaKey); + return EncryptUtils.aesDecrypt(data, aesKey); + } + + private String encryptResponse(String originalResponseData) { + String aesKey = EncryptUtils.createAesKey(192); + String encryptContent = EncryptUtils.aesEncrypt(originalResponseData, aesKey); + String pemPublicRsaKey = EncryptUtils.getPemRsaKeyFromResource(RsaKeyType.PUBLIC); + String encryptedAesKey = EncryptUtils.rsaEncrypt(aesKey, pemPublicRsaKey); + JSONObject json = new JSONObject(); + json.put("encryptedAesKey", encryptedAesKey); + json.put("data", encryptContent); + return json.toString(); + } + + private boolean isFailed(String response){ + JSONObject responseJson = JSONObject.parseObject(response); + return !Objects.equals(responseJson.getInteger("code"), 0); + } + + private boolean isPost(HttpServletRequest request) { + return request.getMethod().equalsIgnoreCase("POST"); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilterConfig.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilterConfig.java new file mode 100644 index 0000000..893ecb1 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubFilterConfig.java @@ -0,0 +1,33 @@ +package com.thing.carbon.pub.filter; + +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author siyang + * @date 2024/5/13 16:25 + * @description 国网侧请求过滤器配置 + */ +@Configuration +@RequiredArgsConstructor +public class CarbonPubFilterConfig { + + @Value("${carbon.api.secret}") + private String apiSecret; + + private final CarbonPubSupplierService carbonPubSupplierService; + + @Bean + public FilterRegistrationBean carbonPubFilter() { + FilterRegistrationBean filterRegistration = new FilterRegistrationBean<>(); + filterRegistration.setFilter(new CarbonPubFilter(apiSecret, carbonPubSupplierService)); + filterRegistration.setName("carbonPubFilter"); + filterRegistration.addUrlPatterns("/v2/carbon/pub/*"); + filterRegistration.setOrder(Integer.MAX_VALUE - 50); + return filterRegistration; + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubRequestWrapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubRequestWrapper.java new file mode 100644 index 0000000..14cc957 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubRequestWrapper.java @@ -0,0 +1,102 @@ +package com.thing.carbon.pub.filter; + +import jakarta.servlet.ReadListener; +import jakarta.servlet.ServletInputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletRequestWrapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.util.Map; + +@Slf4j +public class CarbonPubRequestWrapper extends HttpServletRequestWrapper { + + private String requestBody; + + public CarbonPubRequestWrapper(HttpServletRequest request) { + super(request); + requestBody = getBodyString(request); + } + + @Override + public ServletInputStream getInputStream() { + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(requestBody.getBytes(StandardCharsets.UTF_8)); + return new ServletInputStream() { + @Override + public boolean isReady() { + return false; + } + + @Override + public int readLine(byte[] b, int off, int len) throws IOException { + return super.readLine(b, off, len); + } + + @Override + public boolean isFinished() { + return false; + } + + @Override + public void setReadListener(ReadListener readListener) {} + + @Override + public int read() { + return byteArrayInputStream.read(); + } + }; + } + + @Override + public Map getParameterMap() { + Map parameterMap = super.getParameterMap(); + parameterMap.forEach( + (name, values) -> { + if (values.length > 0) { + for (int i = 0; i < values.length; i++) { + if (values[i] == null) { + continue; + } + values[i] = URLDecoder.decode(values[i], StandardCharsets.UTF_8); + } + } + }); + return parameterMap; + } + + @Override + public BufferedReader getReader() { + return new BufferedReader(new InputStreamReader(this.getInputStream())); + } + + public String getBody() { + return this.requestBody; + } + + public void setBody(String body) { + this.requestBody = body; + } + + /** + * 获取请求Body + * + * @param request request + * @return String + */ + public String getBodyString(final ServletRequest request) { + try { + return IOUtils.toString(request.getInputStream(), StandardCharsets.UTF_8); + } catch (IOException e) { + log.error("", e); + throw new RuntimeException(e); + } + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubResponseWrapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubResponseWrapper.java new file mode 100644 index 0000000..40ff593 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/filter/CarbonPubResponseWrapper.java @@ -0,0 +1,65 @@ +package com.thing.carbon.pub.filter; + +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.WriteListener; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletResponseWrapper; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class CarbonPubResponseWrapper extends HttpServletResponseWrapper { + private final ByteArrayOutputStream buffer; + + private final ServletOutputStream out; + + /** + * Constructs a response adaptor wrapping the given response. + * + * @param response The response to be wrapped + * @throws IllegalArgumentException if the response is null + */ + public CarbonPubResponseWrapper(HttpServletResponse response) { + super(response); + buffer = new ByteArrayOutputStream(); + out = new WrapperOutputStream(buffer); + } + + @Override + public ServletOutputStream getOutputStream() { + return out; + } + + @Override + public void flushBuffer() throws IOException { + if (out != null) { + out.flush(); + } + } + + public byte[] getContent() throws IOException { + flushBuffer(); + return buffer.toByteArray(); + } + + static class WrapperOutputStream extends ServletOutputStream { + private final ByteArrayOutputStream bos; + + public WrapperOutputStream(ByteArrayOutputStream bos) { + this.bos = bos; + } + + @Override + public void write(int b) { + bos.write(b); + } + + @Override + public boolean isReady() { + return false; + } + + @Override + public void setWriteListener(WriteListener writeListener) {} + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/listener/SupplierCreateListener.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/listener/SupplierCreateListener.java new file mode 100644 index 0000000..73afdbf --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/listener/SupplierCreateListener.java @@ -0,0 +1,41 @@ +package com.thing.carbon.pub.listener; + +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.common.core.utils.RandomUtils; +import com.thing.event.TenantDetailSavedEvent; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * @author siyang + * @date 2024/6/3 09:49 + * @description + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class SupplierCreateListener { + + private final CarbonPubSupplierService supplierService; + + @EventListener(TenantDetailSavedEvent.class) + public void onTenantSavedEvent(TenantDetailSavedEvent event) { + SysTenantDetailEntity tenantDetail = event.getTenantDetail(); + if (Objects.isNull(tenantDetail)) { + return; + } + CarbonPubSupplierEntity supplier = new CarbonPubSupplierEntity(); + supplier.setName(tenantDetail.getName()) + .setCode(tenantDetail.getId()) + .setAccessToken(RandomUtils.getRandomString(10)) + .setPoints(100) + .setRegionId(Long.parseLong(tenantDetail.getRegionCode())); + supplierService.save(supplier); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubCertificateMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubCertificateMapper.java new file mode 100644 index 0000000..7196489 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubCertificateMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubCertificateEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 碳足迹证书 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-23 +*/ +@Mapper +public interface CarbonPubCertificateMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubEngEffMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubEngEffMapper.java new file mode 100644 index 0000000..878d295 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubEngEffMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubEngEffEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 设备能效标准 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-06-11 +*/ +@Mapper +public interface CarbonPubEngEffMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorMapper.java new file mode 100644 index 0000000..a63cb48 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 碳排因子库 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-11 +*/ +@Mapper +public interface CarbonPubMaterialFactorMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorUseMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorUseMapper.java new file mode 100644 index 0000000..63901eb --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubMaterialFactorUseMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorUseEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 原材料碳排因子库使用记录 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-17 +*/ +@Mapper +public interface CarbonPubMaterialFactorUseMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsLogsMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsLogsMapper.java new file mode 100644 index 0000000..07e0a04 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsLogsMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 积分增减记录 +* +* @author xc +* @since 3.0 2024-06-17 +*/ +@Mapper +public interface CarbonPubPointsLogsMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsRuleMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsRuleMapper.java new file mode 100644 index 0000000..14abd1e --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubPointsRuleMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubPointsRuleEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 碳足迹积分规则 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Mapper +public interface CarbonPubPointsRuleMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionModelMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionModelMapper.java new file mode 100644 index 0000000..880bc24 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionModelMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 产品详情 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Mapper +public interface CarbonPubProductionModelMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportConfigMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportConfigMapper.java new file mode 100644 index 0000000..d9baccf --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 碳足迹报告配置 +* +* @author sys 2337720667@qq.com +* @since 5.1 2024-05-27 +*/ +@Mapper +public interface CarbonPubProductionReportConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportMapper.java new file mode 100644 index 0000000..8fc1160 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionReportMapper.java @@ -0,0 +1,21 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 产品碳足迹报告 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Mapper +public interface CarbonPubProductionReportMapper extends PowerBaseMapper { + + List getAllModels(@Param("tenantCode") Long tenantCode); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionResultMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionResultMapper.java new file mode 100644 index 0000000..2ff400c --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubProductionResultMapper.java @@ -0,0 +1,14 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 生产记录核算结果 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Mapper +public interface CarbonPubProductionResultMapper extends PowerBaseMapper {} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubSupplierMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubSupplierMapper.java new file mode 100644 index 0000000..bbac798 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/CarbonPubSupplierMapper.java @@ -0,0 +1,31 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.dto.CarbonPubSupplierDTO; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** +* 产品碳足迹供应商 +* +* @author sys dev@lrd.com +* @since 5.1 2024-05-15 +*/ +@Mapper +public interface CarbonPubSupplierMapper extends PowerBaseMapper { + + List selectBy(@Param("params") Map params); + + long countBy(@Param("params") Map params); + + BigDecimal avgCarbonOfAllProduct( + @Param("tenantCode") Long tenantCode, + @Param("start") Date start, + @Param("end") Date end); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/IotCarbonEnterpriseAuthMapper.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/IotCarbonEnterpriseAuthMapper.java new file mode 100644 index 0000000..129009f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/mapper/IotCarbonEnterpriseAuthMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbon.pub.mapper; + +import com.thing.carbon.pub.entity.IotCarbonEnterpriseAuthEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 企业注册 +* +* @author xc +* @since 3.0 2024-07-09 +*/ +@Mapper +public interface IotCarbonEnterpriseAuthMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubCertificateService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubCertificateService.java new file mode 100644 index 0000000..5de750d --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubCertificateService.java @@ -0,0 +1,28 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubCertificateDTO; +import com.thing.carbon.pub.entity.CarbonPubCertificateEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +public interface CarbonPubCertificateService extends IBaseService { + + PageData handlePage(Map params); + + CarbonPubCertificateDTO handleDetail(Long id); + + void handleSave(CarbonPubCertificateDTO dto); + + void handleUpdate(CarbonPubCertificateDTO dto); + + void handleDelete(List ids); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubEngEffService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubEngEffService.java new file mode 100644 index 0000000..c9d99b8 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubEngEffService.java @@ -0,0 +1,17 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.entity.CarbonPubEngEffEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; + +/** + * 设备能效标准 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-06-11 + */ +public interface CarbonPubEngEffService extends IBaseService { + + List getTargetFieldHistoryRecords(String field, String productType); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubFootprintLibService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubFootprintLibService.java new file mode 100644 index 0000000..d6a823c --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubFootprintLibService.java @@ -0,0 +1,21 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubFootprintInfo; +import com.thing.common.core.web.response.PageData; + +import java.util.Map; +import java.util.Set; + +/** + * @author siyang + * @date 2024/5/27 10:30 + * @description 产品碳足迹库服务接口 + */ +public interface CarbonPubFootprintLibService { + + Set getIndustryTypes(); + + Set getIndustryCategories(Map params); + + PageData handlePage(Map params); +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorService.java new file mode 100644 index 0000000..f3f4e11 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorService.java @@ -0,0 +1,28 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubMaterialFactorDTO; +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.Map; +import java.util.Set; + +/** + * 碳排因子库 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-11 + */ +public interface CarbonPubMaterialFactorService extends IBaseService { + + Set targetFieldHistoryRecords(String field); + + void translate(); + + void plusHotScore(String id); + + CarbonPubMaterialFactorDTO handleDetail(String id, Long tenantCode); + + PageData handlePage(Map params); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorUseService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorUseService.java new file mode 100644 index 0000000..0a6f896 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubMaterialFactorUseService.java @@ -0,0 +1,15 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorUseEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 原材料碳排因子库使用记录 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-17 + */ +public interface CarbonPubMaterialFactorUseService extends IBaseService { + + CarbonPubMaterialFactorUseEntity getByFactorId(String materialFactorId, Long tenantCode); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsLogsService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsLogsService.java new file mode 100644 index 0000000..283dbd5 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsLogsService.java @@ -0,0 +1,14 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 积分增减记录 + * + * @author xc + * @since 3.0 2024-06-17 + */ +public interface CarbonPubPointsLogsService extends IBaseService { + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsRuleService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsRuleService.java new file mode 100644 index 0000000..ab36e60 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubPointsRuleService.java @@ -0,0 +1,17 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsRuleEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 碳足迹积分规则 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +public interface CarbonPubPointsRuleService extends IBaseService { + + CarbonPubPointsRuleDTO getFirst(); + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionModelService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionModelService.java new file mode 100644 index 0000000..dad59a5 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionModelService.java @@ -0,0 +1,44 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubProductionIdName; +import com.thing.carbon.pub.dto.CarbonPubProductionModelDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 产品详情 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +public interface CarbonPubProductionModelService extends IBaseService { + + List getIndustryTypes(); + + List getIndustryCategories(Map params); + + PageData getSimplePageData(Map params); + + CarbonPubProductionModelDTO handleDetail(CarbonPubProductionModelEntity entity, Long tenantCode); + + void download(Long id); + + void copy(Long id); + + CarbonPubProductionModelEntity getByProductId(Long productId, Long tenantCode); + + List listSimpleInfoByProductIds(Set productIds, Long tenantCode); + + CarbonPubProductionModelEntity createByProductJson(String productJson, Long tenantCode); + + List getSimpleList(Long tenantCode); + + void syncModel(CarbonPubProductionModelEntity entity, Long tenantCode); + + void plusDownloadCount(Long id); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportConfigService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportConfigService.java new file mode 100644 index 0000000..616f6cc --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportConfigService.java @@ -0,0 +1,18 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 碳足迹报告配置 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-05-27 + */ +public interface CarbonPubProductionReportConfigService extends IBaseService { + + CarbonPubProductionReportConfigDTO getByProductIdAndTenantCode(Long productId, Long tenantCode); + + void handleSave(CarbonPubProductionReportConfigDTO dto); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportService.java new file mode 100644 index 0000000..0f0bf06 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionReportService.java @@ -0,0 +1,32 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionReportDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.common.orm.service.IBaseService; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Date; +import java.util.List; + +/** + * 产品碳足迹报告 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +public interface CarbonPubProductionReportService extends IBaseService { + + CarbonPubProductionReportDTO generateReport(CarbonPubProductionReportConfigDTO config); + + List getAllModels(Long tenantCode); + + Pair getBoundary(CarbonPubProductionReportConfigDTO config); + + void handleSave(CarbonPubProductionReportDTO dto); + + void handleUpdate(CarbonPubProductionReportDTO dto); + + String generateReportCode(Long configId, Date boundaryStart, Date boundaryEnd); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionResultService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionResultService.java new file mode 100644 index 0000000..758f524 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubProductionResultService.java @@ -0,0 +1,78 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.*; +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 生产记录核算结果 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +public interface CarbonPubProductionResultService extends IBaseService { + + PageData handlePage(Map params); + + CarbonPubProductionReportDTO generateReport(Long productId, Integer showResult, Integer boundaryType, Date start, Date end, Long tenantCode); + + List getSimpleAggResult(List productIds, String start, String end, String boundary, String dataType, List tenantCodeList); + + /** + * 查询碳足迹产品基础信息 + * + * @param productId 产品id + * @param prCode 生产记录code + * @return 基础信息 + */ + CarbonPubLibRecordBaseInfo getDetailOfBaseInfo(Long productId, String prCode, Long tenantCode); + + CarbonPubLibRecordBaseInfoOnYear getDetailOfBaseInfoOnYear(Long productId, Long tenantCode); + + List getDetailOfBaseInfoOnYear(List productIds, Long tenantCode); + + /** + * 查询碳足迹原料采购与运输详情 + * @param productId 产品id + * @param prCode 生产记录code + * @return 原料采购与运输详情 + */ + CarbonPubLibRecordMPT getDetailOfMPT(Long productId, String prCode, String start, String end, Long tenantCode); + + /** + * 查询碳足迹生产制造详情 + * @param productId 产品id + * @param prCode 生产记录code + * @return 生产制造详情 + */ + CarbonPubLibRecordPM getDetailOfPM(Long productId, String prCode, String start, String end, Long tenantCode); + + /** + * 查询碳足迹产品运输详情 + * @param productId 产品id + * @param prCode 生产记录code + * @return 产品运输详情 + */ + CarbonPubLibRecordPT getDetailOfPT(Long productId, String prCode, String start, String end, Long tenantCode); + + /** + * 查询碳足迹产品使用详情 + * @param productId 产品id + * @param prCode 生产记录code + * @return 产品使用详情 + */ + CarbonPubLibRecordPU getDetailOfPU(Long productId, String prCode, String start, String end, Long tenantCode); + + /** + * 查询碳足迹废弃处理详情 + * @param productId 产品id + * @param prCode 生产记录code + * @return 废弃处理详情 + */ + CarbonPubLibRecordDispose getDetailOfDispose(Long productId, String prCode, String start, String end, Long tenantCode); +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubSupplierService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubSupplierService.java new file mode 100644 index 0000000..0cee3eb --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/CarbonPubSupplierService.java @@ -0,0 +1,37 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.CarbonPubSupplierDTO; +import com.thing.carbon.pub.dto.CarbonPubSupplierDetail; +import com.thing.carbon.pub.dto.CarbonPubSupplierProduct; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysRegionDTO; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 产品碳足迹供应商 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +public interface CarbonPubSupplierService extends IBaseService { + void plusPointOn(Long code, Integer points); + void minusPointOn(Long code, Integer points); + CarbonPubSupplierEntity getByAccessToken(String accessToken); + + List getRegionList(); + + Set getProductTypes(Long tenantCode); + + PageData handlePage(Map params); + + CarbonPubSupplierDetail handleDetailSummary(Map params); + + PageData handleDetailProduct(Map params); + + CarbonPubSupplierEntity getByCode(Long tenantCode); +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/IotCarbonEnterpriseAuthService.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/IotCarbonEnterpriseAuthService.java new file mode 100644 index 0000000..14590fb --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/IotCarbonEnterpriseAuthService.java @@ -0,0 +1,21 @@ +package com.thing.carbon.pub.service; + +import com.thing.carbon.pub.dto.IotCarbonEnterpriseAuthDTO; +import com.thing.carbon.pub.entity.IotCarbonEnterpriseAuthEntity; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; + +/** + * 企业注册 + * + * @author xc + * @since 3.0 2024-07-09 + */ +public interface IotCarbonEnterpriseAuthService extends IBaseService { + + void doSaveTenant(IotCarbonEnterpriseAuthDTO dto); + + void sendMsg(IotCarbonEnterpriseAuthDTO dto); + + Result saveIotCarbonEnterpriseAuthDTO(IotCarbonEnterpriseAuthDTO dto); +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubCertificateServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubCertificateServiceImpl.java new file mode 100644 index 0000000..7ef18fd --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubCertificateServiceImpl.java @@ -0,0 +1,138 @@ +package com.thing.carbon.pub.service.impl; + +import cn.hutool.core.map.MapUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubCertificateDTO; +import com.thing.carbon.pub.entity.CarbonPubCertificateEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.mapper.CarbonPubCertificateMapper; +import com.thing.carbon.pub.service.CarbonPubCertificateService; +import com.thing.carbon.pub.service.CarbonPubProductionModelService; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +import static com.thing.carbon.pub.entity.table.CarbonPubCertificateEntityTableDef.CARBON_PUB_CERTIFICATE_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionModelEntityTableDef.CARBON_PUB_PRODUCTION_MODEL_ENTITY; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Service +@RequiredArgsConstructor +public class CarbonPubCertificateServiceImpl extends BaseServiceImpl implements CarbonPubCertificateService { + private final CarbonPubProductionModelService modelService; + private final CarbonPubProductionResultService resultService; + + @Override + public QueryWrapper getWrapper(Map params){ + String productName = MapUtil.getStr(params, "productName"); + String certificationOrg = MapUtil.getStr(params, "certificationOrg"); + + QueryWrapper wrapper = + new QueryWrapper() + .like(CarbonPubProductionModelEntity::getProductJson, productName, StringUtils.isNoneBlank(productName)) + .like(CarbonPubCertificateEntity::getCertificationOrg, certificationOrg, StringUtils.isNoneBlank(certificationOrg)); + + return wrapper; + } + + + @Override + public PageData handlePage(Map params) { + Page page = getPage(params, CarbonPubCertificateDTO.class); + QueryWrapper queryWrapper = + getWrapper(params) + .select(CARBON_PUB_CERTIFICATE_ENTITY.ALL_COLUMNS, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_JSON) + .from(CARBON_PUB_CERTIFICATE_ENTITY) + .innerJoin(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .on(CARBON_PUB_CERTIFICATE_ENTITY.PRODUCT_ID.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID) + .and(CARBON_PUB_CERTIFICATE_ENTITY.TENANT_CODE.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE))); + Page paginate = + mapper.paginateAs(page, queryWrapper, CarbonPubCertificateDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public CarbonPubCertificateDTO handleDetail(Long id) { + CarbonPubCertificateDTO certificate = getByIdAs(id, CarbonPubCertificateDTO.class); + if (certificate != null) { + Long productId = certificate.getProductId(); + CarbonPubProductionModelEntity model = modelService.getByProductId(productId, UserContext.getTenantCode()); + certificate.setProductJson(model.getProductJson()); + } + return certificate; + } + + /** + * 存储三方数据:
+ * 1. 产品工艺模型,主要是其中的产品信息字段
+ * 2. 证书信息
+ * 3. 碳足迹核算结果
+ */ + @Override + public void handleSave(CarbonPubCertificateDTO dto) { + Long tenantCode = UserContext.getTenantCode(); + // 存储产品模型 + String productJson = dto.getProductJson(); + CarbonPubProductionModelEntity model = modelService.createByProductJson(productJson, tenantCode); + + // 存储证书信息 + CarbonPubCertificateEntity certificateEntity = ConvertUtils.sourceToTarget(dto, CarbonPubCertificateEntity.class); + certificateEntity.setProductId(model.getProductId()); + certificateEntity.setTenantCode(tenantCode).setCompanyId(tenantCode).setDeptId(tenantCode); + save(certificateEntity); + + // 存储碳足迹核算结果 + // todo ... + + } + + @Override + public void handleUpdate(CarbonPubCertificateDTO dto) { + Long tenantCode = UserContext.getTenantCode(); + CarbonPubProductionModelEntity model = modelService.getByProductId(dto.getProductId(), tenantCode); + // 更新产品模型 + String productJson = dto.getProductJson(); + model.setProductJson(productJson); + modelService.updateById(model); + + // 更新证书信息 + updateDto(dto); + + // 更新碳足迹核算结果 + // todo ... + } + + @Override + public void handleDelete(List ids) { + Long tenantCode = UserContext.getTenantCode(); + List entities = mapper.selectListByIds(ids); + Set productIds = + entities.stream() + .map(CarbonPubCertificateEntity::getProductId) + .collect(Collectors.toSet()); + + mapper.deleteBatchByIds(ids); + + // 删除产品信息 ... 暂时不删除 + + // 删除碳足迹核算结果 ... 暂时不删除 + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubEngEffServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubEngEffServiceImpl.java new file mode 100644 index 0000000..6884b2b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubEngEffServiceImpl.java @@ -0,0 +1,69 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.entity.CarbonPubEngEffEntity; +import com.thing.carbon.pub.mapper.CarbonPubEngEffMapper; +import com.thing.carbon.pub.service.CarbonPubEngEffService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.mybatisflex.core.query.QueryMethods.distinct; + +/** + * 设备能效标准 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-06-11 + */ +@Service +public class CarbonPubEngEffServiceImpl extends BaseServiceImpl implements CarbonPubEngEffService { + + @Override + public QueryWrapper getWrapper(Map params){ + String keywords = MapUtils.getString(params, "keywords"); + String productType = MapUtils.getString(params, "productType"); + String productName = MapUtils.getString(params, "productName"); + return new QueryWrapper() + .like(CarbonPubEngEffEntity::getProductName, keywords, StringUtils::isNotBlank) + .eq(CarbonPubEngEffEntity::getProductType, productType, StringUtils::isNotBlank) + .eq(CarbonPubEngEffEntity::getProductName, productName, StringUtils::isNotBlank); + } + + @Override + public List getTargetFieldHistoryRecords(String field, String productType) { + List list = + list( + QueryWrapper.create() + .select(distinct(new QueryColumn(field))) + .eq(CarbonPubEngEffEntity::getProductType, productType, StringUtils::isNotBlank)); + if(CollectionUtils.isEmpty(list)){ + return Collections.emptyList(); + } + return list.stream() + .filter(Objects::nonNull) + .map( + e -> + switch (field) { + case "product_type" -> e.getProductType(); + case "product_name" -> e.getProductName(); + case "eff_kpi" -> e.getEffKpi(); + case "unit" -> e.getUnit(); + case "top_category" -> e.getTopCategory(); + case "sub_category" -> e.getSubCategory(); + case "category_info" -> e.getCategoryInfo(); + default -> + throw new IllegalStateException( + "UpSupport Field: " + field); + }) + .toList(); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubFootprintLibServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubFootprintLibServiceImpl.java new file mode 100644 index 0000000..802f8ab --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubFootprintLibServiceImpl.java @@ -0,0 +1,102 @@ +package com.thing.carbon.pub.service.impl; + +import static com.thing.carbon.pub.entity.table.CarbonPubProductionModelEntityTableDef.CARBON_PUB_PRODUCTION_MODEL_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionReportConfigEntityTableDef.CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionReportEntityTableDef.CARBON_PUB_PRODUCTION_REPORT_ENTITY; +import static com.thing.sys.tenant.entity.table.SysTenantDetailEntityTableDef.SYS_TENANT_DETAIL_ENTITY; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubFootprintInfo; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import com.thing.carbon.pub.service.CarbonPubFootprintLibService; +import com.thing.carbon.pub.service.CarbonPubProductionReportService; +import com.thing.common.core.web.response.PageData; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/5/27 10:31 + * @description 产品碳足迹服务实现 + */ +@Service +@RequiredArgsConstructor +public class CarbonPubFootprintLibServiceImpl implements CarbonPubFootprintLibService { + private final CarbonPubProductionReportService reportService; + + @Override + public Set getIndustryTypes() { + List models = reportService.getAllModels(null); + return models.stream() + .filter(Objects::nonNull) + .map(CarbonPubProductionModelEntity::getIndustryType) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + @Override + public Set getIndustryCategories(Map params) { + String industryType = MapUtils.getString(params, "industryType"); + List models = reportService.getAllModels(null); + return models.stream() + .filter(Objects::nonNull) + .filter( + model -> + StringUtils.isBlank(industryType) + || model.getIndustryType().equals(industryType)) + .map(CarbonPubProductionModelEntity::getIndustryCategory) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toSet()); + } + + @Override + public PageData handlePage(Map params) { + String keywords = MapUtils.getString(params, "keywords"); + String industryType = MapUtils.getString(params, "industryType"); + String industryCategory = MapUtils.getString(params, "industryCategory"); + String sourceType = MapUtils.getString(params, "sourceType"); + QueryWrapper wrapper = + QueryWrapper.create() + .select( + SYS_TENANT_DETAIL_ENTITY.NAME.as(CarbonPubFootprintInfo::getCompanyName), + CARBON_PUB_PRODUCTION_REPORT_ENTITY.SHOW_RESULT, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_MODEL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.FUNCTION_UNIT, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_JSON, + CARBON_PUB_PRODUCTION_REPORT_ENTITY.ID.as(CarbonPubFootprintInfo::getReportId), + CARBON_PUB_PRODUCTION_REPORT_ENTITY.TOTAL_CARBON, + CARBON_PUB_PRODUCTION_REPORT_ENTITY.BOUNDARY_TYPE, + CARBON_PUB_PRODUCTION_REPORT_ENTITY.CREATE_DATE, + CARBON_PUB_PRODUCTION_REPORT_ENTITY.VALID_END_TIME) + .from(CARBON_PUB_PRODUCTION_REPORT_ENTITY) + .innerJoin(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .on(CARBON_PUB_PRODUCTION_REPORT_ENTITY.PRODUCT_ID.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID) + .and(CARBON_PUB_PRODUCTION_REPORT_ENTITY.TENANT_CODE.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE))) + .leftJoin(SYS_TENANT_DETAIL_ENTITY) + .on(SYS_TENANT_DETAIL_ENTITY.ID.eq(CARBON_PUB_PRODUCTION_REPORT_ENTITY.TENANT_CODE)) + .leftJoin(CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY) + .on(CARBON_PUB_PRODUCTION_REPORT_ENTITY.PRODUCT_ID.eq(CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY.PRODUCT_ID) + .and(CARBON_PUB_PRODUCTION_REPORT_ENTITY.TENANT_CODE.eq(CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY.TENANT_CODE))) + .eq(CarbonPubProductionReportEntity::getSourceType, sourceType, StringUtils.isNotBlank(sourceType)) + .like(CarbonPubProductionModelEntity::getProductName, keywords, StringUtils.isNotBlank(keywords)) + .eq(CarbonPubProductionModelEntity::getIndustryCategory, industryCategory, StringUtils.isNotBlank(industryCategory)) + .eq(CarbonPubProductionModelEntity::getIndustryType, industryType, StringUtils.isNotBlank(industryType)); + Page page = reportService.getPage(params, CarbonPubFootprintInfo.class); + Page paginate = reportService.getMapper().paginateAs(page, wrapper, CarbonPubFootprintInfo.class); + paginate.getRecords().forEach(CarbonPubFootprintInfo::refreshCarbon); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorServiceImpl.java new file mode 100644 index 0000000..65ca713 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorServiceImpl.java @@ -0,0 +1,237 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import com.thing.carbon.pub.constant.CarbonFactorConstant; +import com.thing.carbon.pub.constant.PointsConstant; +import com.thing.carbon.pub.dto.*; +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorEntity; +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorUseEntity; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.mapper.CarbonPubMaterialFactorMapper; +import com.thing.carbon.pub.service.*; +import com.thing.common.core.rest.service.ApiService; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.service.SysTenantDetailService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +import java.util.*; + +import static com.mybatisflex.core.query.QueryMethods.distinct; + +/** + * 碳排因子库 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-11 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CarbonPubMaterialFactorServiceImpl extends BaseServiceImpl implements CarbonPubMaterialFactorService { + private final ApiService apiService; + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubPointsLogsService logsService; + private final CarbonPubSupplierService supplierService; + private final CarbonPubMaterialFactorUseService factorUseService; + private final SysTenantDetailService sysTenantDetailService; + + @Value("${carbon.pub.tenantTag}") + private String pubTenantTag; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String productName = (String)params.get("productName"); + String geoFullCn = (String)params.get("geoFullCn"); + String sourceName = MapUtils.getString(params, "sourceName"); + String industryCn = MapUtils.getString(params, "industryCn"); + String year = MapUtils.getString(params, "year"); + // 产品名称模糊搜索,支持中英文 + if (StringUtils.isNotBlank(productName)) { + wrapper.like(CarbonPubMaterialFactorEntity::getProductName, productName) + .or(CarbonPubMaterialFactorEntity::getProductNameCn) + .like(productName); + } + + wrapper.eq( + CarbonPubMaterialFactorEntity::getSourceName, + sourceName, + !CarbonFactorConstant.CHANGE_SOURCE_NAME + && StringUtils.isNotBlank(sourceName)) + .eq(CarbonPubMaterialFactorEntity::getGeoFullCn, geoFullCn, StringUtils::isNotBlank) + .like(CarbonPubMaterialFactorEntity::getIndustryCn, industryCn, StringUtils::isNotBlank) + .le(CarbonPubMaterialFactorEntity::getStartYear, year, StringUtils::isNotBlank) + .ge(CarbonPubMaterialFactorEntity::getEndYear, year, StringUtils::isNotBlank); + + return wrapper; + } + + @Override + public void plusHotScore(String id) { + UpdateChain.of(CarbonPubMaterialFactorEntity.class) + .setRaw(CarbonPubMaterialFactorEntity::getHotScore, "hot_score + 1") + .where(CarbonPubMaterialFactorEntity::getId).eq(id) + .update(); + } + + @Override + public CarbonPubMaterialFactorDTO handleDetail(String id, Long tenantCode) { + CarbonPubMaterialFactorDTO factor = getByIdAs(id, CarbonPubMaterialFactorDTO.class); + SysTenantDetailEntity tenant = sysTenantDetailService.getById(tenantCode); + if (tenant.getName().contains(pubTenantTag)) { + return factor; + } + + // 当前租户是否已消费过该碳排因子 + CarbonPubMaterialFactorUseEntity factorUseEntity = factorUseService.getByFactorId(id, tenantCode); + + // 因子热度+1 + plusHotScore(id); + + // 有消费记录直接返回 + if (Objects.nonNull(factorUseEntity)) { + return factor; + } + + // 未消费过, 添加消费记录 + CarbonPubMaterialFactorUseDTO dto = new CarbonPubMaterialFactorUseDTO(); + dto.setMaterialFactorId(id); + dto.setUseTime(System.currentTimeMillis()); + dto.setTenantCode(tenantCode); + factorUseService.saveDto(dto); + + // 积分-- + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + supplierService.minusPointOn(tenantCode, pointsRule.getFactorUsePoints()); + + logsService.save( + new CarbonPubPointsLogsEntity() + .setPoints(pointsRule.getFactorUsePoints()) + .setType(PointsConstant.FACTOR_USE) + .setTenantCode(tenantCode) + .setCreateTime(System.currentTimeMillis())); + + return factor; + } + + @Override + public PageData handlePage(Map params) { + PageData page = getPageData(params, CarbonPubMaterialFactorDTO.class); + + Long tenantCode = UserContext.getRealTenantCode(); + SysTenantDetailEntity tenant = sysTenantDetailService.getById(tenantCode); + if (!tenant.getName().contains(pubTenantTag)) { + // 非公共服务侧,不给看碳排因子 + page.getList().forEach(e -> e.setFactor(null)); + } + if (CarbonFactorConstant.CHANGE_SOURCE_NAME) { + page.getList().forEach(e -> e.setSourceName(CarbonFactorConstant.SOURCE_NAME)); + } + + return page; + } + + @Override + public Set targetFieldHistoryRecords(String field) { + List list = + list(QueryWrapper.create().select(distinct(new QueryColumn(field)))); + if(CollectionUtils.isEmpty(list)){ + return Collections.emptySet(); + } + Set fields = new HashSet<>(); + list.stream() + .filter(Objects::nonNull) + .forEach( + e -> { + switch (field) { + case "source_name" -> + fields.add( + CarbonFactorConstant.CHANGE_SOURCE_NAME + ? CarbonFactorConstant.SOURCE_NAME + : e.getSourceName()); + case "geo_full_cn" -> fields.add(e.getGeoFullCn()); + case "industry_cn" -> appendIndustry(e.getIndustryCn(), fields); + default -> + throw new IllegalStateException( + "UpSupport Field: " + field); + } + }); + return fields; + } + + /** 每次拿100条数据去翻译 */ + @Override + public void translate() { + // 查询出500条没有翻译过的数据 + List entities = + list( + QueryWrapper.create() + .isNull(CarbonPubMaterialFactorEntity::getGeoFullCn) + .or(CarbonPubMaterialFactorEntity::getProductNameCn) + .isNull() + .limit(500)); + + entities.forEach( + entity -> { + // 地理位置和产品名称分开查询,判断各自是否有中文名先 + try { + if (StringUtils.isBlank(entity.getGeoFullCn())) { + String result = executeTranslate(entity.getGeoFull()); + updateForGeo(entity.getGeoFull(), result); + } + if (StringUtils.isBlank(entity.getProductNameCn())) { + String result = executeTranslate(entity.getProductName()); + updateForProduct(entity.getProductName(), result); + } + } catch (Exception e) { + log.error("translate entity: {} error: ", entity.getId(), e); + } + }); + } + + private TranslateRequest initRequest(String query) { + return new TranslateRequest(query, "en", "zh"); + } + + private String executeTranslate(String content) { + TranslateRequest request = initRequest(content); + TranslateResponse response = + apiService.apiCall(request, HttpMethod.GET, TranslateResponse.class); + if (response.failed()) { + log.warn("translate error...for: {}; msg: {}", content, response.getError_msg()); + return null; + } + return response.getTrans_result().get(0).getDst(); + } + + private void updateForGeo(String geoFull, String geoFullCn) { + mapper.updateByQuery( + new CarbonPubMaterialFactorEntity().setGeoFullCn(geoFullCn), + QueryWrapper.create().eq(CarbonPubMaterialFactorEntity::getGeoFull, geoFull)); + } + + private void updateForProduct(String productName, String productNameCn) { + mapper.updateByQuery( + new CarbonPubMaterialFactorEntity().setProductNameCn(productNameCn), + QueryWrapper.create().eq(CarbonPubMaterialFactorEntity::getProductName, productName)); + } + + private static void appendIndustry(String industry, Set industries) { + if (StringUtils.isNotBlank(industry)) { + String[] industryArr = industry.split("[,,;;]"); + Arrays.stream(industryArr).map(String::trim).forEach(industries::add); + } + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorUseServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorUseServiceImpl.java new file mode 100644 index 0000000..10ee9b1 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubMaterialFactorUseServiceImpl.java @@ -0,0 +1,36 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.entity.CarbonPubMaterialFactorUseEntity; +import com.thing.carbon.pub.mapper.CarbonPubMaterialFactorUseMapper; +import com.thing.carbon.pub.service.CarbonPubMaterialFactorUseService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 原材料碳排因子库使用记录 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-17 + */ +@Service +public class CarbonPubMaterialFactorUseServiceImpl extends BaseServiceImpl implements CarbonPubMaterialFactorUseService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public CarbonPubMaterialFactorUseEntity getByFactorId(String materialFactorId, Long tenantCode) { + return mapper.selectOneByQuery( + QueryWrapper.create() + .eq(CarbonPubMaterialFactorUseEntity::getMaterialFactorId, materialFactorId) + .eq(CarbonPubMaterialFactorUseEntity::getTenantCode, tenantCode) + .limit(1)); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsLogsServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsLogsServiceImpl.java new file mode 100644 index 0000000..f4ccde9 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsLogsServiceImpl.java @@ -0,0 +1,31 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.mapper.CarbonPubPointsLogsMapper; +import com.thing.carbon.pub.service.CarbonPubPointsLogsService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.MapUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 积分增减记录 + * + * @author xc + * @since 3.0 2024-06-17 + */ +@Service +public class CarbonPubPointsLogsServiceImpl extends BaseServiceImpl implements CarbonPubPointsLogsService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + Long tenantCode = MapUtils.getLong(params,"tenantCode"); + wrapper.eq(CarbonPubPointsLogsEntity::getTenantCode, tenantCode); + return wrapper; + } + + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsRuleServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsRuleServiceImpl.java new file mode 100644 index 0000000..7afecac --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubPointsRuleServiceImpl.java @@ -0,0 +1,39 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsRuleEntity; +import com.thing.carbon.pub.mapper.CarbonPubPointsRuleMapper; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 碳足迹积分规则 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Service +public class CarbonPubPointsRuleServiceImpl extends BaseServiceImpl implements CarbonPubPointsRuleService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public CarbonPubPointsRuleDTO getFirst() { + List rules = mapper.selectAll(); + if (!rules.isEmpty()) { + return ConvertUtils.sourceToTarget(rules.get(0), CarbonPubPointsRuleDTO.class); + } + return null; + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionModelServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionModelServiceImpl.java new file mode 100644 index 0000000..bb45f2f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionModelServiceImpl.java @@ -0,0 +1,268 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import com.thing.carbon.pub.constant.PointsConstant; +import com.thing.carbon.pub.dto.CarbonPubPointsRuleDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionIdName; +import com.thing.carbon.pub.dto.CarbonPubProductionModelDTO; +import com.thing.carbon.pub.entity.CarbonPubPointsLogsEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.mapper.CarbonPubProductionModelMapper; +import com.thing.carbon.pub.service.CarbonPubPointsLogsService; +import com.thing.carbon.pub.service.CarbonPubPointsRuleService; +import com.thing.carbon.pub.service.CarbonPubProductionModelService; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.orm.utils.IdGenerator; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.service.SysTenantDetailService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.distinct; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionModelEntityTableDef.CARBON_PUB_PRODUCTION_MODEL_ENTITY; +import static com.thing.sys.tenant.entity.table.SysTenantDetailEntityTableDef.SYS_TENANT_DETAIL_ENTITY; + +/** + * 产品详情 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Service +@RequiredArgsConstructor +public class CarbonPubProductionModelServiceImpl extends BaseServiceImpl implements CarbonPubProductionModelService { + private final IotCarbonBomService bomService; + private final CarbonPubPointsRuleService ruleService; + private final CarbonPubPointsLogsService logsService; + private final CarbonPubSupplierService supplierService; + private final SysTenantDetailService tenantDetailService; + private final IotCarbonProcessStepsService processStepsService; + private final IotCarbonProductionVarietyService productionVarietyService; + + @Value("${carbon.pub.tenantTag}") + private String pubTenantTag; + + + @Override + public QueryWrapper getWrapper(Map params){ + String keywords = MapUtils.getString(params, "keywords"); + String industryCategory = MapUtils.getString(params, "industryCategory"); + String industryType = MapUtils.getString(params, "industryType"); + Integer showInCommunity = MapUtils.getInteger(params, "showInCommunity"); + Long tenantCode = MapUtils.getLong(params, "tenantCode"); + return QueryWrapper.create() + .like(CarbonPubProductionModelEntity::getProductName, keywords, StringUtils::isNotBlank) + .eq(CarbonPubProductionModelEntity::getIndustryCategory, industryCategory, StringUtils::isNotBlank) + .eq(CarbonPubProductionModelEntity::getIndustryType, industryType, StringUtils::isNotBlank) + .eq(CarbonPubProductionModelEntity::getShowInCommunity, showInCommunity, Objects::nonNull) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode, Objects::nonNull); + } + + @Override + public List getIndustryTypes() { + QueryWrapper queryWrapper = + QueryWrapper.create() + .select(distinct(CARBON_PUB_PRODUCTION_MODEL_ENTITY.INDUSTRY_TYPE)) + .from(CARBON_PUB_PRODUCTION_MODEL_ENTITY).isNotNull("industry_type"); + List list = list(queryWrapper); + return list.stream() + .map(CarbonPubProductionModelEntity::getIndustryType) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public List getIndustryCategories(Map params) { + String industryType = MapUtils.getString(params, "industryType"); + List list = + mapper.selectListByQuery( + QueryWrapper.create() + .select(distinct(CARBON_PUB_PRODUCTION_MODEL_ENTITY.INDUSTRY_CATEGORY)) + .from(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .eq( + CarbonPubProductionModelEntity::getIndustryType, + industryType, + StringUtils::isNotBlank).isNotNull("industry_category")); + return list.stream() + .map(CarbonPubProductionModelEntity::getIndustryCategory) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + @Override + public PageData getSimplePageData(Map params) { + Page page = getPage(params, CarbonPubProductionModelDTO.class); + QueryWrapper queryWrapper = + getWrapper(params) + .select( + CARBON_PUB_PRODUCTION_MODEL_ENTITY.ID, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_MODEL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.URL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.FUNCTION_UNIT, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_JSON, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PROCESS_JSON, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.INDUSTRY_TYPE, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.INDUSTRY_CATEGORY, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_TYPE, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.STEPS_URL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.STEPS_JSON, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.DOWNLOAD_COUNT, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.CREATE_DATE, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.UPDATE_DATE) + .from(CARBON_PUB_PRODUCTION_MODEL_ENTITY); + Page paginate = + mapper.paginateAs(page, queryWrapper, CarbonPubProductionModelDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public CarbonPubProductionModelDTO handleDetail(CarbonPubProductionModelEntity entity, Long tenantCode) { + // 如果查看的是其他企业的模型,那么产品所属的那家企业积分++ + if (!Objects.equals(entity.getTenantCode(), tenantCode)) { + CarbonPubPointsRuleDTO pointsRule = ruleService.getFirst(); + supplierService.plusPointOn(entity.getTenantCode(), pointsRule.getModelClickPoints()); + + // 积分记录 + logsService.save( + new CarbonPubPointsLogsEntity() + .setPoints(pointsRule.getModelClickPoints()) + .setType(PointsConstant.MODEL_CLICK) + .setTenantCode(tenantCode) + .setCreateTime(System.currentTimeMillis())); + } + return ConvertUtils.sourceToTarget(entity, CarbonPubProductionModelDTO.class); + } + + /** + * 将现有的模型社区中的某个产品模型copy一份到我的模型里 + */ + @Override + public void download(Long id) { + CarbonPubProductionModelEntity entity = getById(id); + Long tenantCode = UserContext.getTenantCode(); + if (Objects.equals(tenantCode, entity.getTenantCode())) { + throw new SysException("您已拥有该模型,无需下载"); + } + syncModel(entity, tenantCode); + } + + /** + * 从自己的模型中copy一份副本 + */ + @Override + public void copy(Long id) { + Long tenantCode = UserContext.getTenantCode(); + CarbonPubProductionModelEntity entity = getById(id); + // 校验产品模型是否是当前企业的 + if (!Objects.equals(entity.getTenantCode(), tenantCode)) { + throw new SysException("只允许复制自己的模型"); + } + syncModel(entity, tenantCode); + } + + @Override + public CarbonPubProductionModelEntity getByProductId(Long productId, Long tenantCode) { + return mapper.selectOneByQuery( + QueryWrapper.create() + .eq(CarbonPubProductionModelEntity::getProductId, productId) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode) + .limit(1)); + } + + @Override + public List listSimpleInfoByProductIds(Set productIds, Long tenantCode) { + if(CollectionUtils.isEmpty(productIds)){ + return Collections.emptyList(); + } + QueryWrapper wrapper = + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_MODEL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.FUNCTION_UNIT) + .from(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .in(CarbonPubProductionModelEntity::getProductId, productIds) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode); + return mapper.selectListByQuery(wrapper); + } + + @Override + public CarbonPubProductionModelEntity createByProductJson(String productJson, Long tenantCode) { + CarbonPubProductionModelEntity entity = + new CarbonPubProductionModelEntity() + .setProductId(IdGenerator.nextId()) + // todo 从json中取出行业和产品分类 + .setShowInCommunity(0) + .setProductJson(productJson); + + entity.setTenantCode(tenantCode); + entity.setCompanyId(tenantCode); + entity.setDeptId(tenantCode); + + save(entity); + return entity; + } + + @Override + public List getSimpleList(Long tenantCode) { + SysTenantDetailEntity tenantEntity = tenantDetailService.getById(tenantCode); + if (tenantEntity.getName().contains(pubTenantTag)) { + tenantCode = null; + } + QueryWrapper wrapper = + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID.as(CarbonPubProductionIdName::getProductId), + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME.as(CarbonPubProductionIdName::getProductName), + CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE, + SYS_TENANT_DETAIL_ENTITY.NAME.as(CarbonPubProductionIdName::getCompanyName)) + .from(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .innerJoin(SYS_TENANT_DETAIL_ENTITY) + .on(SYS_TENANT_DETAIL_ENTITY.ID.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE)) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode, Objects::nonNull) + .orderBy(CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE.asc(), + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME.asc()); + return mapper.selectListByQueryAs(wrapper, CarbonPubProductionIdName.class); + } + + /** + * 同步到相关的业务处理表:产品、原料、工艺过程 + * + * @param entity 模型实体 + * @param tenantCode 租户编码 + */ + public void syncModel(CarbonPubProductionModelEntity entity, Long tenantCode) { + Long pid = productionVarietyService.saveWithJson(entity.getProductJson(), tenantCode); + bomService.saveOrUpdateWithJson(entity.getMaterialJson(),pid); + processStepsService.saveOrUpdateWithJson(entity.getProcessJson(),pid); + } + + @Override + public void plusDownloadCount(Long id) { + UpdateChain.of(CarbonPubProductionModelEntity.class) + .setRaw(CarbonPubProductionModelEntity::getDownloadCount, "download_count + 1") + .where(CarbonPubProductionModelEntity::getId) + .eq(id) + .update(); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportConfigServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportConfigServiceImpl.java new file mode 100644 index 0000000..c0cd88a --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportConfigServiceImpl.java @@ -0,0 +1,65 @@ +package com.thing.carbon.pub.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.carbon.pub.mapper.CarbonPubProductionReportConfigMapper; +import com.thing.carbon.pub.service.CarbonPubProductionReportConfigService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Objects; + +/** + * 碳足迹报告配置 + * + * @author sys 2337720667@qq.com + * @since 5.1 2024-05-27 + */ +@Service +public class CarbonPubProductionReportConfigServiceImpl extends BaseServiceImpl implements CarbonPubProductionReportConfigService { + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public CarbonPubProductionReportConfigDTO getByProductIdAndTenantCode( + Long productId, Long tenantCode) { + return mapper.selectOneByQueryAs( + QueryWrapper.create() + .eq(CarbonPubProductionReportConfigEntity::getProductId, productId) + .eq(CarbonPubProductionReportConfigEntity::getTenantCode, tenantCode) + .limit(1), + CarbonPubProductionReportConfigDTO.class); + } + + @Override + public void handleSave(CarbonPubProductionReportConfigDTO dto) { + // 检验是否存在 + CarbonPubProductionReportConfigEntity entity = + getOne( + QueryWrapper.create() + .eq( + CarbonPubProductionReportConfigEntity::getProductId, + dto.getProductId()) + .eq( + CarbonPubProductionReportConfigEntity::getTenantCode, + dto.getTenantCode()) + .eq( + CarbonPubProductionReportConfigEntity::getBoundaryType, + dto.getBoundaryType()) + .limit(1)); + if (Objects.nonNull(entity)) { + // 存在则更新 + dto.setId(entity.getId()); + updateDto(dto); + } else { + // 不存在则新增 + saveDto(dto); + } + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportServiceImpl.java new file mode 100644 index 0000000..f2e7d8b --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionReportServiceImpl.java @@ -0,0 +1,191 @@ +package com.thing.carbon.pub.service.impl; + +import static com.thing.carbon.pub.entity.table.CarbonPubProductionModelEntityTableDef.CARBON_PUB_PRODUCTION_MODEL_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionReportConfigEntityTableDef.CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionReportEntityTableDef.CARBON_PUB_PRODUCTION_REPORT_ENTITY; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionReportDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import com.thing.carbon.pub.mapper.CarbonPubProductionReportMapper; +import com.thing.carbon.pub.service.CarbonPubProductionReportConfigService; +import com.thing.carbon.pub.service.CarbonPubProductionReportService; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.*; + +/** + * 产品碳足迹报告 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Service +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +public class CarbonPubProductionReportServiceImpl extends BaseServiceImpl implements CarbonPubProductionReportService { + private final CarbonPubProductionResultService resultService; + private final CarbonPubProductionReportConfigService reportConfigService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper queryWrapper = new QueryWrapper(); + + Long id = MapUtils.getLong(params, "id"); + String productIdStr = MapUtils.getString(params, "productIds"); + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + Long tenantCode = MapUtils.getLong(params, "tenantCode"); + + List productIds = + StringUtils.isBlank(productIdStr) + ? null + : Arrays.stream(productIdStr.split(",")) + .map(String::trim) + .map(Long::valueOf) + .toList(); + Long startTs = DateTimeUtils.convertTimeToLong(start); + Long endTs = DateTimeUtils.convertTimeToLong(end); + + queryWrapper + .select( + CARBON_PUB_PRODUCTION_REPORT_ENTITY.ALL_COLUMNS, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_NAME, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_MODEL, + CARBON_PUB_PRODUCTION_MODEL_ENTITY.FUNCTION_UNIT, + CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY.SHOW_RESULT) + .from(CARBON_PUB_PRODUCTION_REPORT_ENTITY) + .innerJoin(CARBON_PUB_PRODUCTION_MODEL_ENTITY) + .on(CARBON_PUB_PRODUCTION_REPORT_ENTITY.PRODUCT_ID.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.PRODUCT_ID) + .and(CARBON_PUB_PRODUCTION_REPORT_ENTITY.TENANT_CODE.eq(CARBON_PUB_PRODUCTION_MODEL_ENTITY.TENANT_CODE))) + .leftJoin(CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY) + .on(CARBON_PUB_PRODUCTION_REPORT_ENTITY.CONFIG_ID.eq(CARBON_PUB_PRODUCTION_REPORT_CONFIG_ENTITY.ID)) + .eq(CarbonPubProductionReportEntity::getId, id, Objects::nonNull) + .in(CarbonPubProductionReportEntity::getProductId, productIds, CollectionUtils.isNotEmpty(productIds)) + .ge(CarbonPubProductionReportEntity::getCreateDate, startTs, Objects::nonNull) + .le(CarbonPubProductionReportEntity::getCreateDate, endTs, Objects::nonNull) + .eq(CarbonPubProductionReportEntity::getTenantCode, tenantCode, Objects::nonNull); + + return queryWrapper; + } + + @Override + public CarbonPubProductionReportDTO generateReport(CarbonPubProductionReportConfigDTO config) { + Long tenantCode = Optional.ofNullable(config.getTenantCode()).orElse(UserContext.getTenantCode()); + Pair timePair = getBoundary(config); + return resultService.generateReport( + config.getProductId(), + config.getShowResult(), + config.getBoundaryType(), + new Date(timePair.getLeft()), + new Date(timePair.getRight()), + tenantCode); + } + + @Override + public List getAllModels(Long tenantCode) { + return mapper.getAllModels(tenantCode); + } + + public Pair getBoundary(CarbonPubProductionReportConfigDTO config) { + Integer boundaryType = config.getBoundaryType(); + return switch (boundaryType) { + case 1 -> Pair.of(DateTimeUtils.lastMonthStartTs(), DateTimeUtils.lastMonthEndTs()); + case 2 -> Pair.of(DateTimeUtils.yearStartTs(), DateTimeUtils.yearEndTs()); + case 3 -> Pair.of(config.getBoundaryStart(), config.getBoundaryEnd()); + default -> throw new IllegalArgumentException("碳足迹配置错误:boundaryType=" + boundaryType); + }; + } + + @Override + public void handleSave(CarbonPubProductionReportDTO dto) { + // 检测是否已存在相同配置的报告,存在则更新 + CarbonPubProductionReportEntity latestOne = + getOne( + QueryWrapper.create() + .eq(CarbonPubProductionReportEntity::getBoundary, dto.getBoundary()) + .eq(CarbonPubProductionReportEntity::getBoundaryType, dto.getBoundaryType()) + .eq(CarbonPubProductionReportEntity::getBoundaryStart, dto.getBoundaryStart()) + .eq(CarbonPubProductionReportEntity::getBoundaryEnd, dto.getBoundaryEnd()) + .orderBy(CarbonPubProductionReportEntity::getCreateDate) + .desc() + .limit(1)); + if(Objects.nonNull(latestOne)){ + latestOne.setTotalCarbon(dto.getTotalCarbon()); + latestOne.setStagePercentJson(dto.getStagePercentJson()); + updateById(latestOne); + return; + } + + String reportCode = generateReportCode(dto.getConfigId(), dto.getBoundaryStart(), dto.getBoundaryEnd()); + dto.setReportCode(reportCode); + saveDto(dto); + } + + @Override + public void handleUpdate(CarbonPubProductionReportDTO dto) { + // 修改报告本身的展示结果 + updateDto(dto); + + // 如果该报告是由配置定时生成的(configId非空),则更新对应的配置 + if (Objects.nonNull(dto.getConfigId())) { + CarbonPubProductionReportConfigEntity config = new CarbonPubProductionReportConfigEntity(); + config.setId(dto.getConfigId()); + config.setShowResult(dto.getShowResult()); + reportConfigService.updateById(config); + } + } + + public String generateReportCode(Long configId, Date boundaryStart, Date boundaryEnd) { + String reportCodePrefix = "LRD-ECO-%s%s"; + int reportCodeNumLength = 3; + CarbonPubProductionReportEntity latestOne = + getOne( + QueryWrapper.create() + .eq(CarbonPubProductionReportEntity::getConfigId, configId, Objects::nonNull) + .eq(CarbonPubProductionReportEntity::getBoundaryStart, boundaryStart, Objects::nonNull) + .eq(CarbonPubProductionReportEntity::getBoundaryEnd, boundaryEnd, Objects::nonNull) + .orderBy(CarbonPubProductionReportEntity::getCreateDate) + .desc() + .limit(1)); + LocalDateTime now = LocalDateTime.now(); + String timeStr = + now.getYear() + + addZeroPrefix(now.getMonthValue(), 2) + + addZeroPrefix(now.getDayOfMonth(), 2); + if(Objects.isNull(latestOne)){ + return String.format(reportCodePrefix, timeStr, addZeroPrefix(1, reportCodeNumLength)); + } else { + Integer codeNum = getReportSuffix(latestOne.getReportCode()) + 1; + String numSuffix = addZeroPrefix(codeNum, reportCodeNumLength); + return String.format(reportCodePrefix, timeStr, numSuffix); + } + } + + private Integer getReportSuffix(String reportCode) { + if (StringUtils.isBlank(reportCode)) { + return 0; + } + String suffix = reportCode.substring(16); + return Integer.parseInt(suffix); + } + + private String addZeroPrefix(Integer num, int length){ + return StringUtils.leftPad(num.toString(), length, "0"); + } +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionResultServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionResultServiceImpl.java new file mode 100644 index 0000000..28e4cc6 --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubProductionResultServiceImpl.java @@ -0,0 +1,675 @@ +package com.thing.carbon.pub.service.impl; + +import static com.mybatisflex.core.query.QueryMethods.*; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionResultEntityTableDef.CARBON_PUB_PRODUCTION_RESULT_ENTITY; +import static com.thing.common.core.enumeration.CarbonLifecycleEnum.*; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.constant.ReportSourceTypeConstant; +import com.thing.carbon.pub.dto.*; +import com.thing.carbon.pub.entity.CarbonPubProductionModelEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.mapper.CarbonPubProductionResultMapper; +import com.thing.carbon.pub.service.CarbonPubProductionModelService; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.pub.dto.ProductJsonBean; +import com.thing.carbontrack.report.dto.IotCarbonResultReport; +import com.thing.common.core.enumeration.CarbonLifecycleEnum; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 生产记录核算结果 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Service +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +public class CarbonPubProductionResultServiceImpl extends BaseServiceImpl implements CarbonPubProductionResultService { + private final CarbonPubProductionModelService modelService; + private final CarbonPubSupplierService supplierService; + + /** 碳足迹核算类型与生命周期对应关系 */ + private static final Map> CARBON_TYPE_GROUP = new HashMap<>(); + + static { + CARBON_TYPE_GROUP.put(MATERIAL_PURCHASE_TRANSPORT, Set.of("1")); + CARBON_TYPE_GROUP.put(PRODUCT_MANUFACTURE, Set.of("2", "3")); + CARBON_TYPE_GROUP.put(PRODUCT_TRANSPORT, Set.of("4")); + CARBON_TYPE_GROUP.put(PRODUCT_USAGE, Set.of("5")); + CARBON_TYPE_GROUP.put(DISPOSE, Set.of("6")); + } + + @Override + @SneakyThrows + public QueryWrapper getWrapper(Map params) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String finishStartStr = MapUtils.getString(params, "finishStart"); + String finishEndStr = MapUtils.getString(params, "finishEnd"); + Date finishStart = StringUtils.isNotBlank(finishStartStr) ? sdf.parse(finishStartStr) : null; + Date finishEnd = StringUtils.isNotBlank(finishEndStr) ? sdf.parse(finishEndStr) : null; + + String boundary = MapUtils.getString(params, "boundary"); + String resultType = MapUtils.getString(params, "resultType"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(CarbonPubProductionResultEntity::getBoundary, boundary, StringUtils::isNotBlank) + .eq(CarbonPubProductionResultEntity::getResultType, resultType, StringUtils::isNotBlank) + .between( + CarbonPubProductionResultEntity::getFinishTime, + finishStart, + finishEnd, + Objects.nonNull(finishStart) && Objects.nonNull(finishEnd)); + return wrapper; + } + + @Override + public PageData handlePage(Map params) { + Long tenantCode = UserContext.getTenantCode(); + PageData pageData = getAggPage(params); + List list = pageData.getList(); + fillProductInfo(list, tenantCode); + return pageData; + } + + @Override + public CarbonPubProductionReportDTO generateReport(Long productId, Integer showResult, Integer boundaryType, Date start, Date end, Long tenantCode) { + CarbonPubProductionReportDTO res = new CarbonPubProductionReportDTO(); + + List productionResultList = + aggProductResult( + productId, + null, + List.of(tenantCode), + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE); + if (CollectionUtils.isEmpty(productionResultList)) { + return res; + } + CarbonPubProductionResultDTO productResult = productionResultList.get(0); + String sourceType = + Objects.isNull(productResult.getSourceType()) + ? null + : Objects.equals(productResult.getSourceType(), "1") + ? ReportSourceTypeConstant.COLLECT + : ReportSourceTypeConstant.FILL_IN; + + List aggCarbons = convert2AggCarbon(productionResultList); + Map carbonLifeCycleMap = AggCarbon.agg(aggCarbons).get(AggCarbon.AVG); + + IotCarbonResultReport resultReport = IotCarbonResultReport.convert(carbonLifeCycleMap); + BigDecimal totalCarbon = carbonLifeCycleMap.get(AggCarbon.TOTAL_KEY); + carbonLifeCycleMap.remove(AggCarbon.TOTAL_KEY); + + // 碳足迹各阶段占比 + JSONObject carbonStageValue = new JSONObject(); + if (totalCarbon.compareTo(BigDecimal.ZERO) > 0) { + carbonStageValue.put(MATERIAL_PURCHASE_TRANSPORT.getDesc(), resultReport.calcMptPercent()); + carbonStageValue.put(PRODUCT_MANUFACTURE.getDesc(), resultReport.calcPmPercent()); + carbonStageValue.put(PRODUCT_TRANSPORT.getDesc(), resultReport.calcPtPercent()); + carbonStageValue.put(PRODUCT_USAGE.getDesc(), resultReport.calcPuPercent()); + carbonStageValue.put(DISPOSE.getDesc(), resultReport.calcDisposePercent()); + } + + // 查询产品基本信息 + CarbonPubProductionModelEntity model = modelService.getByProductId(productId, tenantCode); + CarbonPubSupplierEntity supplier = supplierService.getByCode(tenantCode); + + // 报告开始时间为当前日期,有效期默认到1年后的昨天 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime validEndTime = now.plusYears(1).minusDays(1); + + // 组装最终结果 + res.setProductId(productId); + res.setProductName(model.getProductName()); + res.setProductModel(model.getProductModel()); + res.setFunctionUnit(model.getFunctionUnit()); + res.setBoundary(productResult.getBoundary()); + res.setBoundaryType(boundaryType); + res.setBoundaryStart(start); + res.setBoundaryEnd(end); + res.setTotalCarbon(totalCarbon); + res.setStagePercentJson(carbonStageValue.toJSONString()); + res.setCreateDate(DateTimeUtils.parse(now)); + res.setValidEndTime(DateTimeUtils.parse(validEndTime)); + res.setShowResult(showResult); + res.setCompanyName(supplier.getName()); + res.setSourceType(sourceType); + res.setTenantCode(tenantCode); + return res; + } + + @Override + @SneakyThrows + public List getSimpleAggResult(List productIds, String start, String end, String boundary, String resultType, List tenantCodeList) { + List res = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + aggProductResult( + productIds, + StringUtils.isBlank(start) ? null : sdf.parse(start), + StringUtils.isBlank(end) ? null : sdf.parse(end), + boundary, + resultType, + tenantCodeList, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.RESULT_TYPE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE) + .stream() + .collect(Collectors.groupingBy(e -> e.getProductId() + "_" + e.getPrCode())) + .forEach((uniqueKey, list) -> res.add(new CarbonPubLibRecordSimpleAgg(list))); + res.sort( + Comparator.comparing(CarbonPubLibRecordSimpleAgg::getProductName) + .thenComparing(CarbonPubLibRecordSimpleAgg::getPrCode) + .thenComparing(CarbonPubLibRecordSimpleAgg::getFinishTime)); + return res; + } + + @Override + public CarbonPubLibRecordBaseInfo getDetailOfBaseInfo(Long productId, String prCode, Long tenantCode) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + CarbonPubLibRecordBaseInfo res = new CarbonPubLibRecordBaseInfo(); + + List productionResultList = + aggProductResult( + productId, + prCode, + List.of(tenantCode), + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE); + if (CollectionUtils.isEmpty(productionResultList)) { + return res; + } + // 产品基本信息 + CarbonPubProductionResultDTO first = productionResultList.get(0); + res.setProductName(first.getProductName()); + res.setProductCode(first.getProductCode()); + res.setPrCode(prCode); + + List aggCarbons = convert2AggCarbon(productionResultList); + Map carbonLifeCycleMap = AggCarbon.agg(aggCarbons).get(AggCarbon.MIN); + res.setCarbonLifeCycleMap(carbonLifeCycleMap); + + // 生产聚合信息 + for (CarbonPubProductionResultDTO productionResult : productionResultList) { + if (Objects.isNull(res.getFinishTime()) + && Objects.nonNull(productionResult.getFinishTime())) { + res.setFinishTime(sdf.format(productionResult.getFinishTime())); + } + res.setNum(productionResult.getFinalNum()); + res.setPrDur(res.getPrDur() + productionResult.getPrDur()); + } + BigDecimal totalCarbon = carbonLifeCycleMap.get(AggCarbon.TOTAL_KEY); + res.setCarbonTotal(totalCarbon); + + return res; + } + + private List convert2AggCarbon(List list) { + return list.stream() + .map( + dto -> + new AggCarbon( + dto.getCarbonType(), + dto.getPrCode(), + dto.getUsageAvg(), + dto.getCarbonAvg(), + dto.getFqCarbonAvg())) + .collect(Collectors.toList()); + } + + @Override + public CarbonPubLibRecordBaseInfoOnYear getDetailOfBaseInfoOnYear(Long productId, Long tenantCode) { + CarbonPubProductionModelEntity production = + modelService.getOne( + QueryWrapper.create() + .eq(CarbonPubProductionModelEntity::getProductId, productId) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode) + .limit(1)); + ProductJsonBean productJsonBean = JSONObject.parseObject(production.getProductJson(), ProductJsonBean.class); + CarbonPubLibRecordBaseInfoOnYear res = CarbonPubLibRecordBaseInfoOnYear.init(productJsonBean); + Date start = new Date(DateTimeUtils.yearStartTs()); + Date end = new Date(DateTimeUtils.yearEndTs()); + + List aggCarbons = aggCarbon(productId, start, end, tenantCode); + Map> aggMap = AggCarbon.agg(aggCarbons); + + res.setCarbonAvgMap(aggMap.get(AggCarbon.AVG)); + res.setCarbonMaxMap(aggMap.get(AggCarbon.MAX)); + res.setCarbonMinMap(aggMap.get(AggCarbon.MIN)); + + return res; + } + + @Override + public List getDetailOfBaseInfoOnYear(List productIds, Long tenantCode) { + List productions = + modelService.list( + QueryWrapper.create() + .in(CarbonPubProductionModelEntity::getProductId, productIds) + .eq(CarbonPubProductionModelEntity::getTenantCode, tenantCode)); + List res = + productions.stream() + .map( + p -> + CarbonPubLibRecordBaseInfoOnYear.init( + JSONObject.parseObject(p.getProductJson(), ProductJsonBean.class), + p.getProductId())) + .toList(); + + Date start = new Date(DateTimeUtils.yearStartTs()); + Date end = new Date(DateTimeUtils.yearEndTs()); + + List aggCarbons = aggCarbon(productIds, start, end, tenantCode); + Map> aggCarbonMap = + aggCarbons.stream().collect(Collectors.groupingBy(AggCarbon::getProductId)); + + res.forEach( + item -> { + List carbons = aggCarbonMap.get(item.getProductId()); + Map> aggMap = AggCarbon.agg(carbons); + item.setCarbonAvgMap(aggMap.get(AggCarbon.AVG)); + }); + + return res; + } + + @Override + public CarbonPubLibRecordMPT getDetailOfMPT(Long productId, String prCode, String start, String end, Long tenantCode) { + CarbonPubLibRecordMPT res = new CarbonPubLibRecordMPT(); + List list = + listByCarbonLifecycle(MATERIAL_PURCHASE_TRANSPORT, productId, prCode, start, end, tenantCode); + if (CollectionUtils.isEmpty(list)) { + return res; + } + + List lotCarbonList = list.stream().map(CarbonPubLibRecordR::new).toList(); + CarbonPubLibRecordR recordR = + lotCarbonList.stream() + .reduce(CarbonPubLibRecordR::add) + .orElse(new CarbonPubLibRecordR()) + .refreshCarbonAvg() + .refreshUsageAvg() + .clearDetail(); + + List detailList = new ArrayList<>(); + for (CarbonPubProductionResultEntity item : list) { + String detailJson = item.getJson(); + if(StringUtils.isBlank(detailJson)){ + continue; + } + List details = JSONArray.parseArray(detailJson, MptDetail.class); + details.forEach(e -> e.setNum(Long.valueOf(item.getFinalNum()))); + detailList.addAll(details); + } + + List detailResults = + detailList.stream() + .collect( + Collectors.groupingBy( + MptDetail::getMaterialCode, + Collectors.collectingAndThen( + Collectors.toList(), + details -> + details.stream() + .reduce(MptDetail::add) + .orElse(new MptDetail())))) + .values() + .stream() + .toList(); + + res.setBaseInfoList(List.of(recordR)); + res.setDetails(detailResults); + return res; + } + + @Override + public CarbonPubLibRecordPM getDetailOfPM(Long productId, String prCode, String start, String end, Long tenantCode) { + CarbonPubLibRecordPM res = new CarbonPubLibRecordPM(); + List list = + listByCarbonLifecycle(PRODUCT_MANUFACTURE, productId, prCode, start, end, tenantCode); + if (list.isEmpty()) { + return res; + } + + List records = + list.stream() + .map(CarbonPubLibRecordR::new) + .collect( + Collectors.groupingBy( + e -> e.getCarbonType() + "_" + e.getEvId(), + Collectors.collectingAndThen( + Collectors.toList(), + l -> + l.stream() + .reduce(CarbonPubLibRecordR::add) + .orElse(new CarbonPubLibRecordR()) + .refreshCarbonAvg() + .refreshUsageAvg()))) + .values() + .stream() + .toList(); + res.setBaseInfoList(records); + + // 按碳排类型分组 + Map> carbonTypeDetailMap = + records.stream() + .collect( + Collectors.groupingBy( + CarbonPubLibRecordR::getCarbonType, + Collectors.mapping( + CarbonPubLibRecordR::getDetails, Collectors.toList()))); + + // 生产消耗与排放 + List pmJsonArrayList = carbonTypeDetailMap.get("2"); + List pmProcessDetailResult = new ArrayList<>(); + List pmProcessDetails = + pmJsonArrayList.stream() + .map(arr -> arr.toJavaList(PmProcessDetail.class)) + .flatMap(Collection::stream) + .sorted(Comparator.comparing(PmProcessDetail::getOrderNum)) + .toList(); + pmProcessDetails.stream() + .collect( + Collectors.groupingBy( + e -> + e.getProcessName() + + "_" + + e.getProcessCode() + + "_" + + e.getEvId())) + .forEach( + (k, v) -> { + Optional opt = v.stream().reduce(PmProcessDetail::add); + opt.ifPresent(pmProcessDetailResult::add); + }); + res.setProductDetails(pmProcessDetailResult); + + // 公摊消耗与排放 + List shareJsonArrayList = carbonTypeDetailMap.get("3"); + List pIndirectDetailResult = new ArrayList<>(); + List pIndirectDetails = + shareJsonArrayList.stream() + .map(arr -> arr.toJavaList(PIndirectDetail.class)) + .flatMap(Collection::stream) + .sorted(Comparator.comparing(PIndirectDetail::getOrderNum)) + .toList(); + pIndirectDetails.stream() + .collect( + Collectors.groupingBy( + e -> + e.getProcessName() + + "_" + + e.getProcessCode() + + "_" + + e.getIeName() + + "_" + + e.getEvId())) + .forEach( + (k, v) -> { + Optional opt = v.stream().reduce(PIndirectDetail::add); + opt.ifPresent(pIndirectDetailResult::add); + }); + res.setShareDetails(pIndirectDetailResult); + + return res; + } + + @Override + public CarbonPubLibRecordPT getDetailOfPT(Long productId, String prCode, String start, String end, Long tenantCode) { + CarbonPubLibRecordPT res = new CarbonPubLibRecordPT(); + List records = getRecords(PRODUCT_TRANSPORT, productId, prCode, start, end, tenantCode); + res.setBaseInfoList(records); + return res; + } + + @Override + public CarbonPubLibRecordPU getDetailOfPU(Long productId, String prCode, String start, String end, Long tenantCode) { + CarbonPubLibRecordPU res = new CarbonPubLibRecordPU(); + List records = getRecords(PRODUCT_USAGE, productId, prCode, start, end, tenantCode); + res.setBaseInfoList(records); + return res; + } + + @Override + public CarbonPubLibRecordDispose getDetailOfDispose(Long productId, String prCode, String start, String end, Long tenantCode) { + CarbonPubLibRecordDispose res = new CarbonPubLibRecordDispose(); + List records = getRecords(DISPOSE, productId, prCode, start, end, tenantCode); + res.setBaseInfoList(records); + return res; + } + + private List getRecords( + CarbonLifecycleEnum carbonLifecycle, Long productId, String prCode, String start, String end, Long tenantCode) { + List list = + listByCarbonLifecycle(carbonLifecycle, productId, prCode, start, end, tenantCode); + if (list.isEmpty()) { + return new ArrayList<>(); + } + CarbonPubLibRecordR recordR = + list.stream() + .map(CarbonPubLibRecordR::new) + .reduce(CarbonPubLibRecordR::add) + .orElse(new CarbonPubLibRecordR()) + .refreshAvg(); + return List.of(recordR); + } + + @SneakyThrows + private List listByCarbonLifecycle( + CarbonLifecycleEnum carbonLifecycle, Long productId, String prCode, String start, String end, Long tenantCode) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + QueryWrapper wrapper = QueryWrapper.create() + .in(CarbonPubProductionResultEntity::getCarbonType, CARBON_TYPE_GROUP.get(carbonLifecycle)) + .eq(CarbonPubProductionResultEntity::getProductId, productId) + .eq(CarbonPubProductionResultEntity::getPrCode, prCode) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode); + if(Objects.nonNull(start) && Objects.nonNull(end)){ + Date startDate = sdf.parse(start); + Date endDate = sdf.parse(end); + wrapper.and(CARBON_PUB_PRODUCTION_RESULT_ENTITY.START_TIME.between(startDate, endDate) + .or(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINISH_TIME.between(startDate, endDate))); + } + return list(wrapper); + } + + private List aggCarbon(Long productId, Date start, Date end, Long tenantCode) { + return mapper.selectListByQueryAs( + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE, + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.USAGE_AVG).as(AggCarbon::getUsageAvg), + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_AVG).as(AggCarbon::getCarbonAvg), + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FQ_CARBON_AVG).as(AggCarbon::getFCarbonAvg)) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .eq(CarbonPubProductionResultEntity::getProductId, productId) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode) + .between( + CarbonPubProductionResultEntity::getFinishTime, + start, + end, + Objects.nonNull(start) && Objects.nonNull(end)) + .groupBy( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE), + AggCarbon.class); + } + + private List aggCarbon(List productIds, Date start, Date end, Long tenantCode) { + return mapper.selectListByQueryAs( + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE, + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.USAGE_AVG).as(AggCarbon::getUsageAvg), + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_AVG).as(AggCarbon::getCarbonAvg), + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FQ_CARBON_AVG).as(AggCarbon::getFCarbonAvg)) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .in(CarbonPubProductionResultEntity::getProductId, productIds) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode) + .between( + CarbonPubProductionResultEntity::getFinishTime, + start, + end, + Objects.nonNull(start) && Objects.nonNull(end)) + .groupBy( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE), + AggCarbon.class); + } + + private PageData getAggPage(Map params) { + Page page = getPage(params, CarbonPubProductionResultDTO.class); + QueryWrapper queryWrapper = getWrapper(params) + .select( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE, + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINISH_TIME).as(CarbonPubProductionResultDTO::getFinishTime), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.BOUNDARY).as(CarbonPubProductionResultDTO::getBoundary), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.RESULT_TYPE).as(CarbonPubProductionResultDTO::getResultType), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.NUM).as(CarbonPubProductionResultDTO::getNum), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINAL_NUM).as(CarbonPubProductionResultDTO::getFinalNum), + sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON).as(CarbonPubProductionResultDTO::getCarbon)) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .groupBy( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.TENANT_CODE); + Page paginate = + mapper.paginateAs(page, queryWrapper, CarbonPubProductionResultDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + private List aggProductResult(List productIds, Date start, Date end, String boundary, String resultType, List tenantCodes, QueryColumn... groupColumns) { + return aggProductResult(productIds, null, start, end, boundary, resultType, tenantCodes, groupColumns); + } + + private List aggProductResult(Long productId, String prCode, List tenantCodes, QueryColumn... groupColumns) { + return aggProductResult(List.of(productId), prCode, null, null, null, null, tenantCodes, groupColumns); + } + + private List aggProductResult( + List productIds, + String prCode, + Date start, + Date end, + String boundary, + String resultType, + Collection tenantCodes, + QueryColumn... groupColumns) { + // 通用查询列 + Set defaultSelectColumns = new HashSet<>(); + defaultSelectColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID); + defaultSelectColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_CODE); + defaultSelectColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_NAME).as(CarbonPubProductionResultDTO::getProductName)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_MODEL).as(CarbonPubProductionResultDTO::getProductModel)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FUNCTION_UNIT).as(CarbonPubProductionResultDTO::getFunctionUnit)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.BOUNDARY).as(CarbonPubProductionResultDTO::getBoundary)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINISH_TIME).as(CarbonPubProductionResultDTO::getFinishTime)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.NUM).as(CarbonPubProductionResultDTO::getNum)); + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINAL_NUM).as(CarbonPubProductionResultDTO::getFinalNum)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.USAGE).as(CarbonPubProductionResultDTO::getUsage)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON).as(CarbonPubProductionResultDTO::getCarbon)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.CARBON_AVG).as(CarbonPubProductionResultDTO::getCarbonAvg)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.USAGE_AVG).as(CarbonPubProductionResultDTO::getUsageAvg)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FQ_CARBON_AVG).as(CarbonPubProductionResultDTO::getFqCarbonAvg)); + defaultSelectColumns.add(sum(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_DUR).as(CarbonPubProductionResultDTO::getPrDur)); + if (productIds.size() == 1) { + defaultSelectColumns.add(min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.SOURCE_TYPE).as(CarbonPubProductionResultDTO::getSourceType)); + } + + // 通用分组列 + Set defaultGroupColumns = new HashSet<>(); + defaultGroupColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID); + defaultGroupColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_CODE); + defaultGroupColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE); + defaultGroupColumns.add(CARBON_PUB_PRODUCTION_RESULT_ENTITY.TENANT_CODE); + + // 新增查询列和分组列 + Set defaultSelectColNames = defaultSelectColumns.stream().map(QueryColumn::getName).collect(Collectors.toSet()); + Set defaultGroupColNames = defaultGroupColumns.stream().map(QueryColumn::getName).collect(Collectors.toSet()); + for (QueryColumn groupColumn : groupColumns) { + String newGroupColName = groupColumn.getName(); + if (!defaultSelectColNames.contains(newGroupColName)) { + defaultSelectColumns.add(groupColumn); + } + if (!defaultGroupColNames.contains(newGroupColName)) { + defaultGroupColumns.add(groupColumn); + } + } + // 将全量分组列转为数组,别介意入参长度为0,实际会创建合理长度的数组 + QueryColumn[] actualGroupColumns = defaultGroupColumns.toArray(new QueryColumn[0]); + + QueryWrapper queryWrapper = + QueryWrapper.create() + .select(defaultSelectColumns) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .in(CarbonPubProductionResultEntity::getProductId, productIds, CollectionUtils.isNotEmpty(productIds)) + .eq(CarbonPubProductionResultEntity::getPrCode, prCode, StringUtils::isNotBlank) + .in(CarbonPubProductionResultEntity::getTenantCode, tenantCodes) + .eq(CarbonPubProductionResultEntity::getBoundary, boundary, StringUtils::isNotBlank) + .eq(CarbonPubProductionResultEntity::getResultType, resultType, StringUtils::isNotBlank) + .between( + CarbonPubProductionResultEntity::getFinishTime, + start, + end, + Objects.nonNull(start) && Objects.nonNull(end)) + .groupBy(actualGroupColumns); + return mapper.selectListByQueryAs(queryWrapper, CarbonPubProductionResultDTO.class); + } + + private void fillProductInfo(List list, Long tenantCode) { + if (CollectionUtils.isEmpty(list)) { + return; + } + Set productIds = + list.stream() + .map(CarbonPubProductionResultDTO::getProductId) + .collect(Collectors.toSet()); + List models = + modelService.listSimpleInfoByProductIds(productIds, tenantCode); + if (CollectionUtils.isEmpty(models)) { + return; + } + Map modelMap = + models.stream() + .collect( + Collectors.toMap( + CarbonPubProductionModelEntity::getProductId, + Function.identity(), + (v1, v2) -> v1)); + list.forEach( + item -> { + Long productId = item.getProductId(); + CarbonPubProductionModelEntity model = modelMap.get(productId); + item.setProductName(model.getProductName()); + item.setProductModel(model.getProductModel()); + item.setFunctionUnit(model.getFunctionUnit()); + }); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubSupplierServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubSupplierServiceImpl.java new file mode 100644 index 0000000..ca247ca --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/CarbonPubSupplierServiceImpl.java @@ -0,0 +1,258 @@ +package com.thing.carbon.pub.service.impl; + +import static com.mybatisflex.core.query.QueryMethods.*; +import static com.thing.carbon.pub.entity.table.CarbonPubProductionResultEntityTableDef.CARBON_PUB_PRODUCTION_RESULT_ENTITY; +import static com.thing.carbon.pub.entity.table.CarbonPubSupplierEntityTableDef.CARBON_PUB_SUPPLIER_ENTITY; +import static com.thing.carbontrack.production.entity.table.IotCarbonProductionVarietyEntityTableDef.IOT_CARBON_PRODUCTION_VARIETY_ENTITY; +import static com.thing.sys.biz.entity.table.SysRegionEntityTableDef.SYS_REGION_ENTITY; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryMethods; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import com.thing.carbon.pub.dto.CarbonPubLibRecordBaseInfoOnYear; +import com.thing.carbon.pub.dto.CarbonPubSupplierDTO; +import com.thing.carbon.pub.dto.CarbonPubSupplierDetail; +import com.thing.carbon.pub.dto.CarbonPubSupplierProduct; +import com.thing.carbon.pub.entity.CarbonPubProductionResultEntity; +import com.thing.carbon.pub.entity.CarbonPubSupplierEntity; +import com.thing.carbon.pub.mapper.CarbonPubSupplierMapper; +import com.thing.carbon.pub.service.CarbonPubProductionResultService; +import com.thing.carbon.pub.service.CarbonPubSupplierService; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.productionResult.dto.AggCarbon; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysRegionDTO; + +import jakarta.annotation.Resource; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 产品碳足迹供应商 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-15 + */ +@Service +public class CarbonPubSupplierServiceImpl extends BaseServiceImpl implements CarbonPubSupplierService { + @Resource @Lazy + private CarbonPubProductionResultService resultService; + + @Override + public QueryWrapper getWrapper(Map params) { + String productType = MapUtils.getString(params, "productType"); + String name = MapUtils.getString(params, "name"); + Long regionId = MapUtils.getLong(params, "regionId"); + return new QueryWrapper() + .like( + CarbonPubSupplierEntity::getProductType, + productType, + Objects.nonNull(productType)) + .like(CarbonPubSupplierEntity::getName, name, Objects.nonNull(name)) + .eq(CarbonPubSupplierEntity::getRegionId, regionId, Objects.nonNull(regionId)); + } + + @Override + public void plusPointOn(Long code, Integer points) { + UpdateChain.of(CarbonPubSupplierEntity.class) + .setRaw(CarbonPubSupplierEntity::getPoints, "points + " + Math.abs(points)) + .where(CarbonPubSupplierEntity::getCode) + .eq(code) + .update(); + } + + @Override + public void minusPointOn(Long code, Integer points) { + UpdateChain.of(CarbonPubSupplierEntity.class) + .setRaw(CarbonPubSupplierEntity::getPoints, "points - " + Math.abs(points)) + .where(CarbonPubSupplierEntity::getCode) + .eq(code) + .update(); + } + + @Override + public CarbonPubSupplierEntity getByAccessToken(String accessToken) { + return mapper.selectOneByQuery( + QueryWrapper.create() + .eq(CarbonPubSupplierEntity::getAccessToken, accessToken) + .limit(1)); + } + + @Override + public List getRegionList() { + QueryWrapper wrapper = + QueryWrapper.create() + .select(distinct(SYS_REGION_ENTITY.ID, SYS_REGION_ENTITY.NAME)) + .from(SYS_REGION_ENTITY) + .innerJoin(CARBON_PUB_SUPPLIER_ENTITY) + .on(CARBON_PUB_SUPPLIER_ENTITY.REGION_ID.eq(SYS_REGION_ENTITY.ID)); + return listAs(wrapper, SysRegionDTO.class); + } + + @Override + public Set getProductTypes(Long tenantCode) { + QueryWrapper wrapper = + QueryWrapper.create() + .select(distinct(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.INDUSTRY_SUB)) + .from(IOT_CARBON_PRODUCTION_VARIETY_ENTITY) + .eq(IotCarbonProductionVarietyEntity::getTenantCode, tenantCode, Objects::nonNull); + List list = + listAs(wrapper, IotCarbonProductionVarietyEntity.class); + List productTypes = + list.stream() + .filter(Objects::nonNull) + .map(IotCarbonProductionVarietyEntity::getIndustrySub) + .toList(); + Set types = new HashSet<>(); + for (String productType : productTypes) { + if (StringUtils.isNotBlank(productType)) { + types.addAll(Arrays.asList(productType.split(","))); + } + } + return types; + } + + @Override + public PageData handlePage(Map params) { + Integer page = Optional.ofNullable(MapUtils.getInteger(params, Constant.PAGE)).orElse(1); + Integer limit = Optional.ofNullable(MapUtils.getInteger(params, Constant.LIMIT)).orElse(10); + params.put("limit", limit); + params.put("offset", (page - 1) * limit); + long count = mapper.countBy(params); + if (count <= 0) { + return new PageData<>(Collections.emptyList(), 0); + } + List list = mapper.selectBy(params); + return new PageData<>(list, count); + } + + @Override + @SuppressWarnings("Duplicates") + public CarbonPubSupplierDetail handleDetailSummary(Map params) { + Long id = MapUtils.getLong(params, "id"); + CarbonPubSupplierEntity supplier = getById(id); + + // 要查询的年份,默认当年 + Integer year = MapUtils.getInteger(params, "year"); + LocalDateTime today = LocalDateTime.now(); + LocalDateTime targetYear = Objects.isNull(year) ? today : today.withYear(year); + Date start = DateTimeUtils.yearStartDate(targetYear); + Date end = DateTimeUtils.yearEndDate(targetYear); + + CarbonPubSupplierDetail res = new CarbonPubSupplierDetail(); + Map recordCountMap = countRecords( start, end, supplier.getCode()); + Long recordsCount = recordCountMap.values().stream().reduce(Long::sum).orElse(0L); + res.setCompanyName(supplier.getName()); + res.setTenantCode(supplier.getCode()); + res.setProductTypes(getProductTypes(supplier.getCode())); + res.setAvgCarbon(avgCarbonOfAllProduct(supplier.getCode(), start, end)); + res.setRecordsCount(recordsCount); + + return res; + } + + @Override + @SuppressWarnings("Duplicates") + public PageData handleDetailProduct(Map params) { + Long id = MapUtils.getLong(params, "id"); + CarbonPubSupplierEntity supplier = getById(id); + + // 要查询的年份,默认当年 + Integer year = MapUtils.getInteger(params, "year"); + LocalDateTime today = LocalDateTime.now(); + LocalDateTime targetYear = Objects.isNull(year) ? today : today.withYear(year); + Date start = DateTimeUtils.yearStartDate(targetYear); + Date end = DateTimeUtils.yearEndDate(targetYear); + + // 统计数量 + Map recordCountMap = countRecords( start, end, supplier.getCode()); + + // 统计碳足迹值 + PageData productInfoPageData = aggCarbonPage(getPage(params, CarbonPubSupplierProduct.class), start, end, supplier.getCode()); + List productInfoList = productInfoPageData.getList(); + List productIds = productInfoList.stream().map(CarbonPubSupplierProduct::getProductId).toList(); + List details = resultService.getDetailOfBaseInfoOnYear(productIds, supplier.getCode()); + Map productAvgCarbonMap = + details.stream() + .collect( + Collectors.toMap( + CarbonPubLibRecordBaseInfoOnYear::getProductId, + e -> e.getCarbonAvgMap().get(AggCarbon.TOTAL_KEY))); + + productInfoList.forEach(p -> { + p.setRecordsCount(recordCountMap.get(p.getProductId())); + p.setAvgCarbon(productAvgCarbonMap.get(p.getProductId())); + }); + return productInfoPageData; + } + + @Override + public CarbonPubSupplierEntity getByCode(Long tenantCode) { + return getOne( + QueryWrapper.create().eq(CarbonPubSupplierEntity::getCode, tenantCode).limit(1)); + } + + private BigDecimal avgCarbonOfAllProduct(Long tenantCode, Date start, Date end) { + BigDecimal carbonAvg = mapper.avgCarbonOfAllProduct(tenantCode, start, end); + return Optional.ofNullable(carbonAvg) + .orElse(BigDecimal.ZERO) + .setScale(2, RoundingMode.HALF_UP); + } + + private Map countRecords(Date start, Date end, Long tenantCode) { + QueryWrapper queryWrapper = + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID.as(CarbonPubSupplierProduct::getProductId), + QueryMethods.count( + distinct(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE)) + .as(CarbonPubSupplierProduct::getRecordsCount)) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .ge(CarbonPubProductionResultEntity::getFinishTime, start) + .le(CarbonPubProductionResultEntity::getFinishTime, end) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode) + .groupBy( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID, + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PR_CODE); + List list = listAs(queryWrapper, CarbonPubSupplierProduct.class); + return list.stream() + .collect( + Collectors.groupingBy( + CarbonPubSupplierProduct::getProductId, + Collectors.reducing( + 0L, CarbonPubSupplierProduct::getRecordsCount, Long::sum))); + } + + private PageData aggCarbonPage(Page page, Date start, Date end, Long tenantCode) { + QueryWrapper queryWrapper = + QueryWrapper.create() + .select( + CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_ID.as(CarbonPubSupplierProduct::getProductId), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_NAME).as(CarbonPubSupplierProduct::getProductName), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.PRODUCT_MODEL).as(CarbonPubSupplierProduct::getProductModel), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FUNCTION_UNIT).as(CarbonPubSupplierProduct::getFunctionUnit), + min(CARBON_PUB_PRODUCTION_RESULT_ENTITY.BOUNDARY).as(CarbonPubSupplierProduct::getBoundary)) + .from(CARBON_PUB_PRODUCTION_RESULT_ENTITY) + .ge(CarbonPubProductionResultEntity::getFinishTime, start) + .le(CarbonPubProductionResultEntity::getFinishTime, end) + .eq(CarbonPubProductionResultEntity::getTenantCode, tenantCode) + .groupBy(CarbonPubProductionResultEntity::getProductId) + .orderBy(max(CARBON_PUB_PRODUCTION_RESULT_ENTITY.FINISH_TIME).desc()); + Page productInfoPage = + mapper.paginateAs(page, queryWrapper, CarbonPubSupplierProduct.class); + return new PageData<>(productInfoPage.getRecords(), productInfoPage.getTotalRow()); + } +} diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/IotCarbonEnterpriseAuthServiceImpl.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/IotCarbonEnterpriseAuthServiceImpl.java new file mode 100644 index 0000000..91cd47f --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/service/impl/IotCarbonEnterpriseAuthServiceImpl.java @@ -0,0 +1,103 @@ +package com.thing.carbon.pub.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.IotCarbonEnterpriseAuthDTO; +import com.thing.carbon.pub.entity.IotCarbonEnterpriseAuthEntity; +import com.thing.carbon.pub.mapper.IotCarbonEnterpriseAuthMapper; +import com.thing.carbon.pub.service.IotCarbonEnterpriseAuthService; +import com.thing.common.core.enumeration.TenantSaveType; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import com.thing.sys.tenant.dto.TenantDetailForm; +import com.thing.sys.tenant.service.SysTenantDetailService; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 企业注册 + * + * @author xc + * @since 3.0 2024-07-09 + */ +@Service +public class IotCarbonEnterpriseAuthServiceImpl extends BaseServiceImpl implements IotCarbonEnterpriseAuthService { + + @Autowired + private SysTenantDetailService sysTenantDetailService; + @Autowired + private SysRoleMenuService sysRoleMenuService; + + Long roleId = 168522254848626688L; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String tenantName = MapUtils.getString(params,"tenantName"); + String status = MapUtils.getString(params,"status"); + wrapper.eq("status",status, ObjectUtils.isNotEmpty(status)); + wrapper.like("tenant_name",tenantName, ObjectUtils.isNotEmpty(tenantName)); + return wrapper; + } + + + @Override + public void doSaveTenant(IotCarbonEnterpriseAuthDTO dto) { + TenantDetailForm detailForm = new TenantDetailForm(); + SysTenantDetailDTO tenantDetail = new SysTenantDetailDTO(); + tenantDetail.setName(dto.getTenantName()); + tenantDetail.setCorporationCode(dto.getCorporationCode()); + tenantDetail.setIndustryCategory(dto.getIndustryCategory()); + tenantDetail.setIndustryType(dto.getIndustryType()); + tenantDetail.setRegionCode(dto.getRegionCode()); + tenantDetail.setAddress(dto.getAddress()); + tenantDetail.setLngLat(dto.getLngLat()); + tenantDetail.setEmail(dto.getEmail()); + tenantDetail.setContact(dto.getContact()); + tenantDetail.setContactNumber(dto.getContactNumber()); + tenantDetail.setTenantType(0); + detailForm.setTenantDetail(tenantDetail); + TenantDetailForm.TenantUser tenantUser = new TenantDetailForm.TenantUser(); + tenantUser.setUsername(dto.getUsername()); + tenantUser.setPassword(dto.getPassword()); + tenantUser.setEmail(dto.getEmail()); + tenantUser.setPhone(dto.getContactNumber()); + tenantUser.setConfirmPassword(dto.getPassword()); + detailForm.setTenantUser(tenantUser); + List menuIdList = sysRoleMenuService.getMenuIdList(roleId); + TenantDetailForm.TenantRole role = new TenantDetailForm.TenantRole(); + role.setDefaultRole(false); + role.setMenuIdList(menuIdList); + detailForm.setTenantRole(role); + sysTenantDetailService.saveDetail(detailForm, + CollectionUtil.newArrayList(TenantSaveType.base, TenantSaveType.user, TenantSaveType.menu)); + this.updateDto(dto); + } + + @Override + public void sendMsg(IotCarbonEnterpriseAuthDTO dto) { + + + this.updateDto(dto); + } + + @Override + public Result saveIotCarbonEnterpriseAuthDTO(IotCarbonEnterpriseAuthDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("corporation_code",dto.getCorporationCode()); + wrapper.ne("status","3"); + if(this.mapper.selectCountByQuery(wrapper)>0){ + return new Result<>().error("企业已审核或审核中!"); + } + this.saveDto(dto); + return new Result<>().ok("申请成功"); + } + +} \ No newline at end of file diff --git a/modules/carbon-public/src/main/java/com/thing/carbon/pub/task/CarbonReportGenerateTask.java b/modules/carbon-public/src/main/java/com/thing/carbon/pub/task/CarbonReportGenerateTask.java new file mode 100644 index 0000000..1d745cc --- /dev/null +++ b/modules/carbon-public/src/main/java/com/thing/carbon/pub/task/CarbonReportGenerateTask.java @@ -0,0 +1,134 @@ +package com.thing.carbon.pub.task; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.pub.dto.CarbonPubProductionReportConfigDTO; +import com.thing.carbon.pub.dto.CarbonPubProductionReportDTO; +import com.thing.carbon.pub.entity.CarbonPubProductionReportConfigEntity; +import com.thing.carbon.pub.entity.CarbonPubProductionReportEntity; +import com.thing.carbon.pub.service.CarbonPubProductionReportConfigService; +import com.thing.carbon.pub.service.CarbonPubProductionReportService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.quartz.timetask.task.ITask; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Component; + +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.thing.common.core.utils.DateTimeUtils.DATE_PATTERN; + +/** + * @author siyang + * @date 2024/6/13 16:13 + * @description 碳足迹报表生成任务 + */ +@Slf4j +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +@Component("CarbonReportGenerateTask") +public class CarbonReportGenerateTask implements ITask { + + private final CarbonPubProductionReportService reportService; + private final CarbonPubProductionReportConfigService reportConfigService; + + @Override + public void run(String params) { + List configEntities = + reportConfigService.list( + QueryWrapper.create() + .isNotNull(CarbonPubProductionReportConfigEntity::getBoundaryStart) + .isNotNull(CarbonPubProductionReportConfigEntity::getBoundaryEnd)); + if (CollectionUtils.isEmpty(configEntities)) { + return; + } + + // 查询出这些配置对应的报告,并按配置id分组 + Map> reportGroup = + getReportsByConfigs(configEntities); + + List configs = + ConvertUtils.sourceToTarget( + configEntities, CarbonPubProductionReportConfigDTO.class); + + List saveList = new ArrayList<>(); + List updateList = new ArrayList<>(); + configs.forEach( + config -> { + // 生成新的报告 + CarbonPubProductionReportDTO dto = reportService.generateReport(config); + CarbonPubProductionReportEntity entity = + ConvertUtils.sourceToTarget(dto, CarbonPubProductionReportEntity.class); + entity.setConfigId(config.getId()) + .setTenantCode(config.getTenantCode()) + .setDeptId(config.getTenantCode()) + .setCompanyId(config.getTenantCode()); + + // 从以往报告中找出需要更新的一条,若存在则更新,否则新增 + List existReports = + reportGroup.get(config.getId()); + CarbonPubProductionReportEntity toUpdateReport = + Objects.isNull(existReports) + ? null + : existReports.stream() + .filter(e -> needUpdate(config, e)) + .findFirst() + .orElse(null); + + if (Objects.nonNull(toUpdateReport)) { + toUpdateReport + .setTotalCarbon( + entity.getTotalCarbon().setScale(4, RoundingMode.HALF_UP)) + .setStagePercentJson(entity.getStagePercentJson()); + updateList.add(toUpdateReport); + } else { + String reportCode = + reportService.generateReportCode(config.getId(), null, null); + entity.setReportCode(reportCode); + saveList.add(entity); + } + }); + if (CollectionUtils.isNotEmpty(saveList)) { + reportService.saveBatch(saveList); + } + if (CollectionUtils.isNotEmpty(updateList)) { + reportService.updateBatch(updateList); + } + } + + private Map> getReportsByConfigs( + List configs) { + List configIds = + configs.stream().map(CarbonPubProductionReportConfigEntity::getId).toList(); + List reportEntities = + reportService.list( + QueryWrapper.create() + .in(CarbonPubProductionReportEntity::getConfigId, configIds)); + return reportEntities.stream() + .collect(Collectors.groupingBy(CarbonPubProductionReportEntity::getConfigId)); + } + + private Boolean needUpdate( + CarbonPubProductionReportConfigDTO config, CarbonPubProductionReportEntity report) { + Pair boundaryTimePair = reportService.getBoundary(config); + boolean sameType = Objects.equals(report.getBoundaryType(), config.getBoundaryType()); + boolean sameStart = + Objects.equals( + DateTimeUtils.timestamp2Str( + report.getBoundaryStart().getTime(), DATE_PATTERN), + DateTimeUtils.timestamp2Str(boundaryTimePair.getLeft(), DATE_PATTERN)); + boolean sameEnd = + Objects.equals( + DateTimeUtils.timestamp2Str( + report.getBoundaryEnd().getTime(), DATE_PATTERN), + DateTimeUtils.timestamp2Str(boundaryTimePair.getRight(), DATE_PATTERN)); + return sameType && sameStart && sameEnd; + } +} diff --git a/modules/carbon-public/src/main/resources/mapper/CarbonPubProductionReportMapper.xml b/modules/carbon-public/src/main/resources/mapper/CarbonPubProductionReportMapper.xml new file mode 100644 index 0000000..3ed8ebb --- /dev/null +++ b/modules/carbon-public/src/main/resources/mapper/CarbonPubProductionReportMapper.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/modules/carbon-public/src/main/resources/mapper/CarbonPubSupplierMapper.xml b/modules/carbon-public/src/main/resources/mapper/CarbonPubSupplierMapper.xml new file mode 100644 index 0000000..452f014 --- /dev/null +++ b/modules/carbon-public/src/main/resources/mapper/CarbonPubSupplierMapper.xml @@ -0,0 +1,94 @@ + + + + + + + + + and cps.name like concat('%', #{params.name}, '%') + + + and cps.region_id = #{params.regionId}::bigint + + + and temp.product_type like concat('%', #{params.productType}, '%') + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/pom.xml b/modules/carbon-track/pom.xml new file mode 100644 index 0000000..5a53e4c --- /dev/null +++ b/modules/carbon-track/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + + com.thing + modules + 5.1 + + + com.thing.modules + carbon-track + jar + ThingBI Server Modules carbon-track + + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-aop + + + com.thing.common + cache + + + com.thing.common + orm + + + com.thing.modules + thing + + + com.thing.modules + report-analysis + + + com.thing.modules + quartz + + + diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/dto/IotCarbonIndirectEnergyDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/dto/IotCarbonIndirectEnergyDTO.java new file mode 100644 index 0000000..dffcb22 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/dto/IotCarbonIndirectEnergyDTO.java @@ -0,0 +1,52 @@ +package com.thing.carbontrack.Indirect.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 间接用能配置记录 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "间接用能配置记录") +public class IotCarbonIndirectEnergyDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "间接用能过程编号") + private String ieCode; + @Schema(description = "间接用能过程名称") + private String ieName; + @Schema(description = "间接用能能源类型id") + private Long evId; + @Schema(description = "间接用能能源类型编码") + private String evCode; + @Schema(description = "间接用能能源名称") + private String evName; + @Schema(description = "间接用能设备编码/物实体编码") + private String thingCode; + @Schema(description = "使用比率") + private BigDecimal ratio; + @Schema(description = "用量") + private BigDecimal dosage; + @Schema(description = "工序数量") + private Integer stepsNum; + @Schema(description = "提交数据组id") + private Long groupId; + @Schema(description = "计算状态") + private Long prStatus; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "工单类型,批次录入,时段录入") + private String prType; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/entity/IotCarbonIndirectEnergyEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/entity/IotCarbonIndirectEnergyEntity.java new file mode 100644 index 0000000..3c1da11 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/entity/IotCarbonIndirectEnergyEntity.java @@ -0,0 +1,84 @@ +package com.thing.carbontrack.Indirect.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 间接用能配置记录 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_indirect_energy") +public class IotCarbonIndirectEnergyEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + /** + * 生产记录编码 + */ + private String prCode; + /** + * 间接用能过程编号 + */ + private String ieCode; + /** + * 间接用能过程名称 + */ + private String ieName; + /** + * 间接用能能源类型id + */ + private Long evId; + /** + * 间接用能设备编码/物实体编码 + */ + private String thingCode; + /** + * 使用比率 + */ + private BigDecimal ratio; + /** + * 用量 + */ + private BigDecimal dosage; + + /** + * 工序数量 + */ + private Integer stepsNum; + /** + *提交数据组id + */ + private Long groupId; + /** + *计算状态 + */ + private Long prStatus; + /** + * 产品id + */ + private Long m_id; + + /** + * 工单类型,批次录入,时段录入 + */ + private String prType; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/mapper/IotCarbonIndirectEnergyMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/mapper/IotCarbonIndirectEnergyMapper.java new file mode 100644 index 0000000..f82f011 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/mapper/IotCarbonIndirectEnergyMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.Indirect.mapper; + +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 间接用能配置记录 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonIndirectEnergyMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/IotCarbonIndirectEnergyService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/IotCarbonIndirectEnergyService.java new file mode 100644 index 0000000..756e2e4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/IotCarbonIndirectEnergyService.java @@ -0,0 +1,22 @@ +package com.thing.carbontrack.Indirect.service; + +import com.thing.carbontrack.Indirect.dto.IotCarbonIndirectEnergyDTO; +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; + +/** + * 间接用能配置记录 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonIndirectEnergyService extends IBaseService { + + List getListByPrCodeAndGroupId(String prCode,Long groupId); + + List getListByStatus(String status); + + void saveDtoOrUpdateDtoInfo(IotCarbonIndirectEnergyDTO temp); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/impl/IotCarbonIndirectEnergyServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/impl/IotCarbonIndirectEnergyServiceImpl.java new file mode 100644 index 0000000..210b742 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/Indirect/service/impl/IotCarbonIndirectEnergyServiceImpl.java @@ -0,0 +1,76 @@ +package com.thing.carbontrack.Indirect.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.Indirect.dto.IotCarbonIndirectEnergyDTO; +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.carbontrack.Indirect.mapper.IotCarbonIndirectEnergyMapper; +import com.thing.carbontrack.Indirect.service.IotCarbonIndirectEnergyService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 间接用能配置记录 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonIndirectEnergyServiceImpl extends BaseServiceImpl implements IotCarbonIndirectEnergyService { + + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public List getListByPrCodeAndGroupId(String prCode, Long groupId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonIndirectEnergyEntity::getPrCode,prCode); + wrapper.eq(IotCarbonIndirectEnergyEntity::getGroupId,groupId); + List resultDto = this.listAs(wrapper,IotCarbonIndirectEnergyDTO.class); + resultDto.forEach(temp->{ + CarbonEnergyVarietyEntity varietyEntity = carbonEnergyVarietyService.getById(temp.getEvId()); + if(ObjectUtils.isNotEmpty(varietyEntity)){ + temp.setEvCode(varietyEntity.getCode()); + temp.setEvName(varietyEntity.getName()); + } + }); + return resultDto; + } + + @Override + public List getListByStatus(String status) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonIndirectEnergyEntity::getPrStatus,Long.valueOf(status)); + return this.list(wrapper); + } + + @Override + public void saveDtoOrUpdateDtoInfo(IotCarbonIndirectEnergyDTO temp) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",temp.getM_id()); + wrapper.eq("ie_code",temp.getIeCode()); + wrapper.eq("ie_name",temp.getIeName()); + wrapper.eq("ev_id",temp.getEvId()); + wrapper.eq("pr_code",temp.getPrCode()); + IotCarbonIndirectEnergyEntity energyEntity = this.getOne(wrapper); + if(ObjectUtils.isNotEmpty(energyEntity)){ + temp.setId(energyEntity.getId()); + this.updateDto(temp); + }else { + this.saveDto(temp); + } + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/controller/IotCarbonBomController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/controller/IotCarbonBomController.java new file mode 100644 index 0000000..1d9e6f3 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/controller/IotCarbonBomController.java @@ -0,0 +1,172 @@ +package com.thing.carbontrack.bom.controller; + +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.bom.dto.IotCarbonBomDTO; +import com.thing.carbontrack.bom.dto.IotCarbonBomExcel; +import com.thing.carbontrack.bom.dto.IotCarbonBomExcelImport; +import com.thing.carbontrack.bom.dto.IotCarbonBomImport; +import com.thing.carbontrack.bom.entity.IotCarbonBomEntity; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.pub.service.MaterialFactorService; +import com.thing.carbontrack.share.dto.IotCarbonShareDTO; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 产品bom信息 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/Bom/iotcarbonbom") +@Tag(name="AAA产品bom信息") +@RequiredArgsConstructor +public class IotCarbonBomController { + + private final IotCarbonBomService iotCarbonBomService; + + private final MaterialFactorService materialFactorService; + + private final IotCarbonShareService carbonShareService; + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "mname", description = "原料名称"), + @Parameter(name = "supplierName", description = "供应商名称"), + @Parameter(name = "productId", description = "产品id") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonBomService.getPageIotCarbonBomDTO(params, IotCarbonBomDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonBomDTO data = iotCarbonBomService.getByIdAs(id, IotCarbonBomDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonBomImport dto){ + //效验数据 + iotCarbonBomService.saveIotCarbonBomDTO(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonBomImport dto){ + //效验数据 + dto.getBomExcels().forEach(temp->{ + IotCarbonBomEntity entity = ConvertUtils.sourceToTarget(temp,IotCarbonBomEntity.class); + if(ObjectUtils.isEmpty(entity.getReference())){ + entity.setReference("公共服务碳足迹库"); + } + iotCarbonBomService.initTransportUse(entity); + iotCarbonBomService.updateById(entity); + }); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonBomService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("import") + @Operation(summary="导入") + public Result importExcel(@ModelAttribute IotCarbonBomExcelImport bomImport){ + iotCarbonBomService.importExcel(bomImport); + return new Result<>(); + } + + @PostMapping("exportTemplate") + @Operation(summary="导出模板") + public void export( HttpServletResponse response){ + ExcelUtils.exportExcel(Lists.newArrayList(), "产品BOM信息", "产品BOM信息", IotCarbonBomExcel.class, "产品BOM信息模板.xls", response); + } + + @PostMapping("exportInfoById") + @Operation(summary="导出Bom详情") + public void exportInfo(@RequestParam Long productId, HttpServletResponse response){ + List excelList = iotCarbonBomService.queryListByMid(productId); + ExcelUtils.exportExcel(excelList, "产品BOM信息", "产品BOM信息", IotCarbonBomExcel.class, "产品BOM信息.xls", response); + } + + @GetMapping("fetchMaterialFactorPage") + @Operation(summary="根据原料名称,分页查询原料因子库列表数据") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productName", description = "产品名称,支持中英文模糊搜索") + }) + public String fetchMaterialFactorPage(@RequestParam Map params){ + String token = getAccessToken(); + if(ObjectUtils.isEmpty(token)){ + return "token异常,请联系管理员处理!"; + } + return materialFactorService.fetchMaterialFactorPage(params,token); + } + + @GetMapping("fetchMaterialFactorDetail") + @Operation(summary="根据原料id,获取原料碳排放因子值") + @Parameters({ + @Parameter(name = "id", description = "碳排因子库,原料id") + }) + public String fetchMaterialFactorDetail(@RequestParam Map params){ + String token = getAccessToken(); + String id = MapUtils.getString(params,"id"); + if(ObjectUtils.isEmpty(token)){ + return "token异常,请联系管理员处理!"; + } + return materialFactorService.fetchMaterialFactorDetail(id,token); + } + + + private String getAccessToken(){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", UserContext.getTenantCode()); + IotCarbonShareDTO dto = carbonShareService.getOneAs(wrapper, IotCarbonShareDTO.class); + if(ObjectUtils.isNotEmpty(dto)){ + return dto.getToken(); + } + return null; + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/BomCarbonReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/BomCarbonReq.java new file mode 100644 index 0000000..9595038 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/BomCarbonReq.java @@ -0,0 +1,15 @@ +package com.thing.carbontrack.bom.dto; + + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class BomCarbonReq { + + private BigDecimal transportUse; + + private BigDecimal muFinalUse; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomDTO.java new file mode 100644 index 0000000..17ae649 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomDTO.java @@ -0,0 +1,65 @@ +package com.thing.carbontrack.bom.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 产品bom信息 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "产品bom信息") +public class IotCarbonBomDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "最终产品id") + private Long productId; + @Schema(description = "父物料id") + private Long pid; + @Schema(description = "原料编码") + private String mcode; + @Schema(description = "原料名称") + private String mname; + @Schema(description = "原料类别") + private String m_type; + @Schema(description = "原料单位") + private String unit; + @Schema(description = "原料重量") + private BigDecimal weight; + @Schema(description = "重量重量计量单位") + private String w_unit; + @Schema(description = "原料用量") + private BigDecimal dosage; + @Schema(description = "供应商编码") + private String supplierCode; + @Schema(description = "供应商名称") + private String supplierName; + @Schema(description = "供应商地址") + private String supplierAddress; + @Schema(description = "排放因子库id") + private Long crId; + @Schema(description = "排放因子库能源品种名称/运输方式名称") + private String crName; + @Schema(description = "运输距离") + private BigDecimal transportKm; + @Schema(description = "物料运输碳排:原料获取碳排(kg)=物料重量*物料用量*物料排放因子") + private BigDecimal transportUse; + @Schema(description = "物料使用碳排:原料运输碳排(kg)=物料重量*物料用量*运输距离*运输k排放因子") + private BigDecimal muFinalUse; + @Schema(description = "物料单耗,物料因子值") + private BigDecimal muUse; + @Schema(description = "参考标准") + private String reference; + @Schema(description = "备注补充说明") + private String remake; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcel.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcel.java new file mode 100644 index 0000000..4d4a195 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcel.java @@ -0,0 +1,68 @@ +package com.thing.carbontrack.bom.dto; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import cn.afterturn.easypoi.excel.annotation.ExcelIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class IotCarbonBomExcel { + + @ExcelIgnore + private Long id; + @ExcelIgnore + private Long pid; + @ExcelIgnore + private Long productId; + @Schema(description = "原料编码") + @Excel(name = "原料编码", width = 25) + private String mcode; + @Schema(description = "原料名称") + @Excel(name = "原料名称", width = 25) + private String mname; + @Schema(description = "原料类别") + @Excel(name = "原料类别", width = 25) + private String m_type; + @Schema(description = "原料单位") + @Excel(name = "原料单位", width = 25) + private String unit; + @Schema(description = "原料重量") + @Excel(name = "原料重量", width = 25) + private BigDecimal weight; + @Schema(description = "重量计量单位") + @Excel(name = "重量计量单位", width = 25) + private String w_unit; + @Schema(description = "原料用量") + @Excel(name = "原料用量", width = 25) + private BigDecimal dosage; + @Schema(description = "供应商编码") + @Excel(name = "供应商编码", width = 25) + private String supplierCode; + @Schema(description = "过程能源类别编码") + @Excel(name = "过程能源类别编码", width = 25) + private String crCode; + @ExcelIgnore + @Excel(name = "过程能源碳排因子库id", width = 25) + private Long crId; + @Schema(description = "运输距离") + @Excel(name = "运输距离", width = 25) + private BigDecimal transportKm; + @Schema(description = "供应商名称") + @Excel(name = "供应商名称", width = 25) + private String supplierName; + @Schema(description = "供应商地址") + @Excel(name = "供应商地址", width = 25) + private String supplierAddress; + @ExcelIgnore + @Schema(description = "原料单位因子值") + private BigDecimal muUse; + @ExcelIgnore + @Schema(description = "参考标准") + private String reference; + @ExcelIgnore + @Schema(description = "参考说明") + private String remake; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcelImport.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcelImport.java new file mode 100644 index 0000000..6c7c5f2 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomExcelImport.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.bom.dto; + +import lombok.Data; +import org.springframework.web.multipart.MultipartFile; + +@Data +public class IotCarbonBomExcelImport { + + private Long productId; + + private Long pid; + + private MultipartFile file; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomImport.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomImport.java new file mode 100644 index 0000000..cb11ecf --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/dto/IotCarbonBomImport.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.bom.dto; + +import lombok.Data; + +import java.util.List; + +@Data +public class IotCarbonBomImport { + + private Long productId; + + private Long pid; + + private List bomExcels; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/entity/IotCarbonBomEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/entity/IotCarbonBomEntity.java new file mode 100644 index 0000000..2f44932 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/entity/IotCarbonBomEntity.java @@ -0,0 +1,104 @@ +package com.thing.carbontrack.bom.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 产品bom信息 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_bom") +public class IotCarbonBomEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + /** + * 最终产品id + */ + private Long productId; + /** + * 父物料id + */ + private Long pid; + /** + * 原料编码 + */ + private String mcode; + /** + * 原料名称 + */ + private String mname; + /** + * 原料单位 + */ + private String unit; + /** + * 原料重量 + */ + private BigDecimal weight; + /** + * 重量重量计量单位 + */ + private String w_unit; + /** + * 原料用量 + */ + private BigDecimal dosage; + /** + * 供应商编码 + */ + private String supplierCode; + /** + * 供应商名称 + */ + private String supplierName; + /** + * 供应商地址 + */ + private String supplierAddress; + /** + * 排放因子库id 运输方式id + */ + private Long crId; + /** + * 运输距离 + */ + private BigDecimal transportKm; + /** + * 物料单耗 + */ + private BigDecimal muUse; + /** + * 参考标准 + */ + private String reference; + /** + * 备注补充说明 + */ + private String remake; + //物料类型 + private String m_type; + //运输单耗 + private BigDecimal transportUse; + //物料单耗 + private BigDecimal muFinalUse; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/mapper/IotCarbonBomMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/mapper/IotCarbonBomMapper.java new file mode 100644 index 0000000..a79eef7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/mapper/IotCarbonBomMapper.java @@ -0,0 +1,23 @@ +package com.thing.carbontrack.bom.mapper; + +import com.thing.carbontrack.bom.dto.BomCarbonReq; +import com.thing.carbontrack.bom.entity.IotCarbonBomEntity; +import com.thing.carbontrack.productionResult.dto.MptDetail; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 产品bom信息 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonBomMapper extends PowerBaseMapper { + + BomCarbonReq queryBomCarbonByMid(Long productId); + + List listByProductId(Long productId); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/IotCarbonBomService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/IotCarbonBomService.java new file mode 100644 index 0000000..20a038c --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/IotCarbonBomService.java @@ -0,0 +1,45 @@ +package com.thing.carbontrack.bom.service; + +import com.thing.carbontrack.bom.dto.*; +import com.thing.carbontrack.bom.entity.IotCarbonBomEntity; +import com.thing.carbontrack.productionResult.dto.MptDetail; +import com.thing.carbontrack.pub.dto.MaterialJsonBean; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * 产品bom信息 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonBomService extends IBaseService { + + void saveIotCarbonBomDTO(IotCarbonBomImport dto); + + void importExcel(IotCarbonBomExcelImport bomImport); + + List queryListByMid(Long productId); + + PageData getPageIotCarbonBomDTO(Map params, Class iotCarbonBomDTOClass); + + void initTransportUse(IotCarbonBomEntity entity); + IotCarbonBomEntity queryOneBuyParam(Long productId,Long pid,String mCode); + BomCarbonReq queryBomCarbonByMid(Long productId); + + List listByProductId(Long mId); + + List queryMaterialJsonBeanListByMid(Long mId); + + void saveWithJson(String productJson,Long pId); + + void saveOrUpdateWithJson(String productJson, Long productId); + + void deleteByMid(Long mId); + + BigDecimal getKmBySupplierAddress(String supplierAddress); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/impl/IotCarbonBomServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/impl/IotCarbonBomServiceImpl.java new file mode 100644 index 0000000..e0607e1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/bom/service/impl/IotCarbonBomServiceImpl.java @@ -0,0 +1,320 @@ +package com.thing.carbontrack.bom.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.bom.dto.*; +import com.thing.carbontrack.bom.entity.IotCarbonBomEntity; +import com.thing.carbontrack.bom.mapper.IotCarbonBomMapper; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.dto.IotCarbonProductionVarietyDTO; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.productionResult.dto.MptDetail; +import com.thing.carbontrack.pub.dto.MaterialJsonBean; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 产品bom信息 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonBomServiceImpl extends BaseServiceImpl implements IotCarbonBomService { + + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + @Autowired + private IotCarbonRatioService carbonRatioService; + @Autowired + private IotCarbonProductionVarietyService productionVarietyService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String mname = MapUtils.getString(params,"mname"); + String supplierName = MapUtils.getString(params,"supplierName"); + Long productId = MapUtils.getLong(params,"productId"); + wrapper.eq("product_id",productId, ObjectUtils.isNotEmpty(productId)); + wrapper.like("mname",mname, StringUtils.isNotEmpty(mname)); + wrapper.like("supplier_name",supplierName, StringUtils.isNotEmpty(supplierName)); + return wrapper; + } + + + @Override + public void saveIotCarbonBomDTO(IotCarbonBomImport dto) { + dto.getBomExcels().forEach(temp->{ + IotCarbonBomEntity entity = ConvertUtils.sourceToTarget(temp,IotCarbonBomEntity.class); + entity.setProductId(dto.getProductId()); + entity.setPid(dto.getPid()); + IotCarbonBomEntity tempEntity = this.queryOneBuyParam(dto.getProductId(),dto.getPid(),temp.getMcode()); + if(ObjectUtils.isNotEmpty(tempEntity)){ + entity.setId(tempEntity.getId()); + //计算运输单耗 + initTransportUse(entity); + this.updateById(entity); + }else { + //半成品的id + IotCarbonProductionVarietyDTO productionVarietyDTO = productionVarietyService.queryByCode(temp.getMcode()); + if(ObjectUtils.isNotEmpty(productionVarietyDTO)){ + entity.setId(productionVarietyDTO.getId()); + } + IotCarbonBomEntity bomEntity = this.queryOneBuyParam(null,null,temp.getMcode()); + //物料编码已存在,碳足因子同步过来,若导入时没有供应商信息,原数据中存在,也同步过来 + if(ObjectUtils.isNotEmpty(bomEntity)){ + entity.setMuUse(bomEntity.getMuUse()); + if(ObjectUtils.isEmpty(entity.getSupplierCode())){ + //导入或新增时,未配置供应商信息 + if(ObjectUtils.isNotEmpty(bomEntity.getSupplierCode())){ + entity.setSupplierCode(bomEntity.getSupplierCode()); + entity.setSupplierName(bomEntity.getSupplierName()); + entity.setSupplierAddress(bomEntity.getSupplierAddress()); + entity.setCrId(bomEntity.getCrId()); + entity.setTransportKm(entity.getTransportKm()); + } + } + }else { + //导入时配置了供应商信息 配置的能源标准编码 获取最新得 + if(ObjectUtils.isEmpty(temp.getCrId())){ + if(ObjectUtils.isNotEmpty(temp.getCrCode())){ + Long evId = this.evIdByCode(temp.getCrCode()); + IotCarbonRatioEntity ratioEntity = carbonRatioService.queryOnByEvIdAndType(evId,"1"); + entity.setCrId(ratioEntity.getId()); + } + } + } + initTransportUse(entity); + this.save(entity); + } + }); + } + + + @Override + public void importExcel(IotCarbonBomExcelImport bomImport) { + IotCarbonBomImport carbonBomImport = new IotCarbonBomImport(); + carbonBomImport.setPid(bomImport.getPid()); + carbonBomImport.setProductId(bomImport.getProductId()); + List sheetData = ExcelUtils.importExcel(bomImport.getFile(), 1, 1, IotCarbonBomExcel.class, 0); + carbonBomImport.setBomExcels(sheetData); + this.saveIotCarbonBomDTO(carbonBomImport); + } + + @Override + public List queryListByMid(Long productId) { + List entityList = this.listByMap(Map.of("product_id",productId)); + List excelList = new ArrayList<>(); + + entityList.forEach(temp->{ + IotCarbonBomExcel excel = ConvertUtils.sourceToTarget(temp,IotCarbonBomExcel.class); + try { + IotCarbonRatioEntity ratioEntity = carbonRatioService.getById(temp.getCrId()); + CarbonEnergyVarietyEntity energyVarietyEntity = carbonEnergyVarietyService.getById(ratioEntity.getEvId()); + excel.setCrCode(energyVarietyEntity.getCode()); + } catch (Exception e) { + excel.setCrCode("暂无"); + } + excelList.add(excel); + }); + return excelList; + } + + @Override + public PageData getPageIotCarbonBomDTO(Map params, Class iotCarbonBomDTOClass) { + PageData pageData = this.getPageData(params,iotCarbonBomDTOClass); + pageData.getList().forEach(temp->{ + try { + IotCarbonRatioEntity ratioEntity = carbonRatioService.getById(temp.getCrId()); + CarbonEnergyVarietyEntity energyVarietyEntity = carbonEnergyVarietyService.getById(ratioEntity.getEvId()); + temp.setCrName(energyVarietyEntity.getName()); + } catch (Exception e) { + temp.setCrName("未知"); + } + }); + return pageData; + } + + + /** + * 根据能源品种编码 获取能源品种id + * @param evCode + * @return + */ + private Long evIdByCode(String evCode){ + CarbonEnergyVarietyEntity entity = carbonEnergyVarietyService.selectByCode(evCode); + return entity.getId(); + } + + + /** + * 根据入参 获取Bom节点树信息 + * @param productId + * @param pid + * @param mCode + * @return + @*/ + @Override + public IotCarbonBomEntity queryOneBuyParam(Long productId,Long pid,String mCode){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonBomEntity::getProductId,productId, ObjectUtils.isNotEmpty(productId)); + wrapper.eq(IotCarbonBomEntity::getPid,pid, ObjectUtils.isNotEmpty(pid)); + wrapper.eq(IotCarbonBomEntity::getMcode,mCode, ObjectUtils.isNotEmpty(mCode)); + return this.mapper.selectOneByQuery(wrapper); + } + + + /** + * 初始化计算运输单耗 + * @param entity + */ + + @Override + public void initTransportUse(IotCarbonBomEntity entity) { + if(ObjectUtils.isNotEmpty(entity.getSupplierCode())||ObjectUtils.isNotEmpty(entity.getSupplierName())){ + try { + IotCarbonRatioEntity ratioEntity = carbonRatioService.getById(entity.getCrId()); + BigDecimal transportUse = entity.getWeight().multiply(entity.getDosage()) + .multiply(entity.getTransportKm()) + .multiply(ratioEntity.getRatio()) + .divide(new BigDecimal("1000"),4,RoundingMode.UP); + entity.setTransportUse(transportUse); + } catch (Exception e) { + entity.setTransportUse(new BigDecimal(0)); + } + } + + if(ObjectUtils.isNotEmpty(entity.getMuUse())){ + try { + BigDecimal muFinalUse = entity.getWeight().multiply(entity.getDosage()) + .multiply(entity.getMuUse()) + .setScale(4, RoundingMode.UP); + + entity.setMuFinalUse(muFinalUse); + } catch (Exception e) { + entity.setMuFinalUse(new BigDecimal(0)); + } + } + } + + @Override + public BomCarbonReq queryBomCarbonByMid(Long productId) { + return this.mapper.queryBomCarbonByMid(productId); + } + + @Override + public List listByProductId(Long mId) { + return this.mapper.listByProductId(mId); + } + + @Override + public List queryMaterialJsonBeanListByMid(Long mId) { + List entityList = this.listByMap(Map.of("product_id",mId)); + return ConvertUtils.sourceToTarget(entityList,MaterialJsonBean.class); + } + + @Override + public void saveWithJson(String productJson,Long pId) { + List materialJsonBeans = JSONArray.parseArray(productJson, MaterialJsonBean.class); + List entities = ConvertUtils.sourceToTarget(materialJsonBeans, IotCarbonBomEntity.class); + entities.forEach(temp->{ + temp.setId(null); + temp.setProductId(pId); + temp.setPid(pId); + }); + saveBatch(entities); + } + + @Override + @SuppressWarnings("Duplicates") + public void saveOrUpdateWithJson(String productJson, Long productId) { + // 查询出当前产品现有的原料信息, 与新数据对比,进行增删改 + List existList = list(QueryWrapper.create().eq(IotCarbonBomEntity::getProductId, productId)); + List materialJsonBeans = JSONArray.parseArray(productJson, MaterialJsonBean.class); + List newList = ConvertUtils.sourceToTarget(materialJsonBeans, IotCarbonBomEntity.class); + + Map> existUniqueMap = + existList.stream() + .collect(Collectors.groupingBy(e -> e.getMcode() + "_" + e.getMname())); + + Map> newUniqueMap = + newList.stream() + .collect(Collectors.groupingBy(e -> e.getMcode() + "_" + e.getMname())); + + List saveList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + for (IotCarbonBomEntity newBom : newList) { + String uniqueKey = newBom.getMcode() + "_" + newBom.getMname(); + if (existUniqueMap.containsKey(uniqueKey)) { + IotCarbonBomEntity existBom = existUniqueMap.get(uniqueKey).get(0); + newBom.setId(existBom.getId()); + newBom.setProductId(existBom.getProductId()); + newBom.setPid(existBom.getPid()); + updateList.add(newBom); + } else { + newBom.setId(null); + newBom.setProductId(productId); + newBom.setPid(productId); + saveList.add(newBom); + } + } + + Set deleteIds = + existList.stream() + .filter(e -> !newUniqueMap.containsKey(e.getMcode() + "_" + e.getMname())) + .map(IotCarbonBomEntity::getId) + .collect(Collectors.toSet()); + + if (CollectionUtils.isNotEmpty(deleteIds)) { + removeByIds(deleteIds); + } + if (CollectionUtils.isNotEmpty(saveList)) { + saveBatch(saveList); + } + if (CollectionUtils.isNotEmpty(updateList)) { + updateBatch(updateList); + } + } + + @Override + public void deleteByMid(Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonBomEntity::getProductId,mId); + this.remove(wrapper); + } + + @Override + public BigDecimal getKmBySupplierAddress(String supplierAddress) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonBomEntity::getSupplierAddress,supplierAddress); + wrapper.limit(1); + IotCarbonBomEntity entity = this.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + return entity.getTransportKm(); + } + return null; + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/controller/IotCarbonCertificateController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/controller/IotCarbonCertificateController.java new file mode 100644 index 0000000..3fe8b76 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/controller/IotCarbonCertificateController.java @@ -0,0 +1,126 @@ +package com.thing.carbontrack.certification.controller; + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.certification.service.IotCarbonCertificateService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@RestController +@RequestMapping("v2/carbonCertificate/iotcarboncertificate") +@Tag(name = "碳足迹证书-企业侧") +@RequiredArgsConstructor +public class IotCarbonCertificateController { + + private final IotCarbonCertificateService carbonCertificateService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode", description = "租户编码"), + @Parameter(name = "productName", description = "产品名称"), + @Parameter(name = "certificationOrg", description = "认证机构"), + @Parameter(name = "shared", description = "是否已共享"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = carbonCertificateService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode", description = "租户编码"), + @Parameter(name = "productName", description = "产品名称"), + @Parameter(name = "certificationOrg", description = "认证机构"), + @Parameter(name = "shared", description = "是否已共享"), + }) + public Result> list(@Parameter(hidden = true) @RequestParam Map params) { + List page = carbonCertificateService.handleList(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + IotCarbonCertificateDTO data = carbonCertificateService.handleDetail(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonCertificateDTO dto) { + ValidatorUtils.validateEntity(dto, AddGroup.class); + carbonCertificateService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonCertificateDTO dto) { + // 效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonCertificateService.updateById(dto.toEntity()); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + // 效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonCertificateService.batchDelete(ids); + return new Result<>(); + } + + @DeleteMapping("delete/doc") + @Operation(summary = "删除证书文件") + @LogOperation("删除证书文件") + public Result deleteDoc(@RequestBody Long[] ids) { + // 效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + carbonCertificateService.deleteDoc(List.of(ids)); + return new Result<>(); + } + + @PostMapping("share") + @Operation(summary = "共享") + @LogOperation("共享") + public Result share(@RequestBody Long[] ids) { + if (ids.length == 0) { + return new Result<>(); + } + carbonCertificateService.share(List.of(ids)); + return new Result<>(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/CarbonStageValue.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/CarbonStageValue.java new file mode 100644 index 0000000..a22528f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/CarbonStageValue.java @@ -0,0 +1,21 @@ +package com.thing.carbontrack.certification.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024/6/12 10:57 + * @description 碳足迹阶段数值 + */ +@Data +@Schema(description = "碳足迹阶段数值") +public class CarbonStageValue { + @Schema(description = "阶段名称") + private String name; + + @Schema(description = "阶段碳足迹占比") + private BigDecimal value; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/IotCarbonCertificateDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/IotCarbonCertificateDTO.java new file mode 100644 index 0000000..bf06818 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/IotCarbonCertificateDTO.java @@ -0,0 +1,96 @@ +package com.thing.carbontrack.certification.dto; + +import com.alibaba.fastjson.JSONObject; +import com.thing.carbontrack.certification.entity.IotCarbonCertificateEntity; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.apache.commons.collections4.CollectionUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Data +@Schema(description = "碳足迹证书") +public class IotCarbonCertificateDTO implements Serializable { + @Serial private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + + @Schema(description = "企业名称") + private String companyName; + + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "数据时间边界开始") + private Long boundaryStart; + + @Schema(description = "数据时间边界截止") + private Long boundaryEnd; + + @Schema(description = "认证机构") + private String certificationOrg; + + @Schema(description = "核查标准") + private String verificationStd; + + @Schema(description = "产品碳足迹值,单位kce") + private BigDecimal carbonVal; + + @Schema(description = "生命周期各阶段占比json") + private List stagePercentList; + + @Schema(description = "证书url") + private String certificateUrl; + + @Schema(description = "发证日期") + private Long certificateStart; + + @Schema(description = "证书到期时间") + private Long certificateEnd; + + @Schema(description = "是否已共享:0-未共享,1-已共享") + private Integer shared; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "简要产品信息") + @NotNull(message = "产品信息不能为空", groups = {AddGroup.class, UpdateGroup.class}) + private SimpleProductInfo productInfo; + + public IotCarbonCertificateEntity toEntity() { + IotCarbonCertificateEntity entity = + ConvertUtils.sourceToTarget(this, IotCarbonCertificateEntity.class); + if (CollectionUtils.isNotEmpty(stagePercentList)) { + entity.setStagePercentJson(JSONObject.toJSONString(stagePercentList)); + } + return entity; + } + + public static IotCarbonCertificateDTO fromEntity(IotCarbonCertificateEntity entity) { + IotCarbonCertificateDTO dto = + ConvertUtils.sourceToTarget(entity, IotCarbonCertificateDTO.class); + if (entity.getStagePercentJson() != null) { + dto.setStagePercentList( + JSONObject.parseArray(entity.getStagePercentJson(), CarbonStageValue.class)); + } + return dto; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/SimpleProductInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/SimpleProductInfo.java new file mode 100644 index 0000000..9f6afdb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/dto/SimpleProductInfo.java @@ -0,0 +1,99 @@ +package com.thing.carbontrack.certification.dto; + +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024/6/12 13:34 + * @description 产品简要信息 + */ +@Data +@Schema(description = "简要产品信息") +public class SimpleProductInfo { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品编码") + private String productCode; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "行业类型(一级行业)") + private String industryType; + + @Schema(description = "产品分类(二级行业)") + private String productCategory; + + @Schema(description = "产品类型: 1-成品 2-半成品") + private String productType; + + @Schema(description = "功能单位计量数") + private String functionNum; + + @Schema(description = "功能单位") + private String functionUnit; + + @Schema(description = "产品尺寸") + private String productSize; + + @Schema(description = "产品尺寸单位") + private String productSizeUnit; + + @Schema(description = "产品重量") + private BigDecimal productWeight; + + @Schema(description = "产品重量单位") + private String productWeightUnit; + + @Schema(description = "系统边界: 1-摇篮到大门, 2-摇篮到坟墓") + private String boundary; + + @Schema(description = "产品图片") + private String url; + + public IotCarbonProductionVarietyEntity toProductEntity(Long tenantCode){ + IotCarbonProductionVarietyEntity product = new IotCarbonProductionVarietyEntity(); + product.setName(productName); + product.setCode(productCode); + product.setModel(productModel); + product.setIndustry(industryType); + product.setIndustrySub(productCategory); + product.setType(productType); + product.setFsu(functionNum); + product.setFUnit(functionUnit); + product.setPSize(productSize); + product.setPSizeUnit(productSizeUnit); + product.setPWeight(productWeight); + product.setWUnit(productWeightUnit); + product.setBoundary(boundary); + product.setUrl(url); + product.setTenantCode(tenantCode); + product.setDeptId(tenantCode); + product.setCompanyId(tenantCode); + return product; + } + + public static SimpleProductInfo fromProductEntity(IotCarbonProductionVarietyEntity entity){ + SimpleProductInfo res = new SimpleProductInfo(); + res.setProductName(entity.getName()); + res.setProductCode(entity.getCode()); + res.setProductModel(entity.getModel()); + res.setIndustryType(entity.getIndustry()); + res.setProductCategory(entity.getIndustrySub()); + res.setProductType(entity.getType()); + res.setFunctionNum(entity.getFsu()); + res.setFunctionUnit(entity.getFUnit()); + res.setProductSize(entity.getPSize()); + res.setProductSizeUnit(entity.getPSizeUnit()); + res.setProductWeight(entity.getPWeight()); + res.setProductWeightUnit(entity.getWUnit()); + res.setBoundary(entity.getBoundary()); + res.setUrl(entity.getUrl()); + return res; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/entity/IotCarbonCertificateEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/entity/IotCarbonCertificateEntity.java new file mode 100644 index 0000000..726d251 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/entity/IotCarbonCertificateEntity.java @@ -0,0 +1,75 @@ +package com.thing.carbontrack.certification.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_certificate") +public class IotCarbonCertificateEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品id + */ + private Long productId; + /** + * 产品名称 + */ + private String productName; + /** + * 数据时间边界开始 + */ + private Long boundaryStart; + /** + * 数据时间边界截止 + */ + private Long boundaryEnd; + /** + * 认证机构 + */ + private String certificationOrg; + /** + * 核查标准 + */ + private String verificationStd; + /** + * 产品碳足迹值,单位kce + */ + private BigDecimal carbonVal; + /** + * 生命周期各阶段占比json + */ + private String stagePercentJson; + /** + * 证书url + */ + private String certificateUrl; + /** + * 发证日期 + */ + private Long certificateStart; + /** + * 证书到期时间 + */ + private Long certificateEnd; + /** + * 是否已共享:0-未共享,1-已共享 + */ + private Integer shared; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/mapper/IotCarbonCertificateMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/mapper/IotCarbonCertificateMapper.java new file mode 100644 index 0000000..04eea17 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/mapper/IotCarbonCertificateMapper.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.certification.mapper; + +import com.thing.carbontrack.certification.entity.IotCarbonCertificateEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Mapper +public interface IotCarbonCertificateMapper extends PowerBaseMapper {} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/IotCarbonCertificateService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/IotCarbonCertificateService.java new file mode 100644 index 0000000..abcae5a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/IotCarbonCertificateService.java @@ -0,0 +1,33 @@ +package com.thing.carbontrack.certification.service; + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.certification.entity.IotCarbonCertificateEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +public interface IotCarbonCertificateService extends IBaseService { + + + PageData handlePage(Map params); + + List handleList(Map params); + + IotCarbonCertificateDTO handleDetail(Long id); + + void handleSave(IotCarbonCertificateDTO dto); + + IotCarbonCertificateDTO getByProductId(Long productId); + + void share(List ids); + + void deleteDoc(List ids); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/impl/IotCarbonCertificateServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/impl/IotCarbonCertificateServiceImpl.java new file mode 100644 index 0000000..8e17d74 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/certification/service/impl/IotCarbonCertificateServiceImpl.java @@ -0,0 +1,234 @@ +package com.thing.carbontrack.certification.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateChain; +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.certification.dto.SimpleProductInfo; +import com.thing.carbontrack.certification.entity.IotCarbonCertificateEntity; +import com.thing.carbontrack.certification.mapper.IotCarbonCertificateMapper; +import com.thing.carbontrack.certification.service.IotCarbonCertificateService; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.pub.service.CertificateSyncService; +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.service.SysTenantDetailService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.thing.carbontrack.production.entity.table.IotCarbonProductionVarietyEntityTableDef.IOT_CARBON_PRODUCTION_VARIETY_ENTITY; + +/** + * 碳足迹证书 + * + * @author sys dev@lrd.com + * @since 5.1 2024-05-23 + */ +@Service +@RequiredArgsConstructor +public class IotCarbonCertificateServiceImpl extends BaseServiceImpl implements IotCarbonCertificateService { + + private final IotCarbonProductionVarietyService productionVarietyService; + private final SysTenantDetailService tenantDetailService; + private final CertificateSyncService certificateSyncService; + private final IotCarbonShareService shareService; + + @Override + public QueryWrapper getWrapper(Map params) { + String companyName = MapUtils.getString(params, "companyName"); + List tenantCodes = + tenantDetailService + .list( + QueryWrapper.create() + .like( + SysTenantDetailEntity::getName, + companyName, + StringUtils::isNotBlank)) + .stream() + .map(SysTenantDetailEntity::getId) + .toList(); + + String productName = MapUtils.getString(params, "productName"); + String certificationOrg = MapUtils.getString(params, "certificationOrg"); + Integer shared = MapUtils.getInteger(params, "shared"); + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper + .like( + IotCarbonCertificateEntity::getProductName, + productName, + StringUtils::isNotBlank) + .like( + IotCarbonCertificateEntity::getCertificationOrg, + certificationOrg, + StringUtils::isNotBlank) + .in( + IotCarbonCertificateEntity::getTenantCode, + tenantCodes, + CollectionUtils.isNotEmpty(tenantCodes)) + .eq( + IotCarbonCertificateEntity::getTenantCode, + -1, + Objects.nonNull(companyName) && CollectionUtils.isEmpty(tenantCodes)) + .eq(IotCarbonCertificateEntity::getShared, shared, Objects::nonNull); + return queryWrapper; + } + + @Override + public PageData handlePage(Map params) { + PageData pageData = getPageData(params); + List list = pageData.getList(); + List dtoList = list.stream().map(IotCarbonCertificateDTO::fromEntity).toList(); + fillData(dtoList); + return new PageData<>(dtoList, pageData.getTotal()); + } + + @Override + public List handleList(Map params) { + List list = list(getWrapper(params)); + List dtoList = list.stream().map(IotCarbonCertificateDTO::fromEntity).toList(); + fillData(dtoList); + return dtoList; + } + + @Override + public IotCarbonCertificateDTO handleDetail(Long id) { + IotCarbonCertificateEntity entity = getById(id); + IotCarbonCertificateDTO dto = IotCarbonCertificateDTO.fromEntity(entity); + fillData(List.of(dto)); + return dto; + } + + @Override + public void handleSave(IotCarbonCertificateDTO dto) { + SimpleProductInfo productInfo = dto.getProductInfo(); + IotCarbonProductionVarietyEntity productEntity = productInfo.toProductEntity(dto.getTenantCode()); + IotCarbonProductionVarietyEntity existProduct = productionVarietyService.getOne( + QueryWrapper.create() + .eq(IotCarbonProductionVarietyEntity::getTenantCode, dto.getTenantCode()) + .eq(IotCarbonProductionVarietyEntity::getName, productEntity.getName()) + .eq(IotCarbonProductionVarietyEntity::getCode, productEntity.getCode()) + .eq(IotCarbonProductionVarietyEntity::getIndustry, productEntity.getIndustry()) + .eq(IotCarbonProductionVarietyEntity::getIndustrySub, productEntity.getIndustrySub()) + .eq(IotCarbonProductionVarietyEntity::getType, productEntity.getType()) + .eq(IotCarbonProductionVarietyEntity::getBoundary, productEntity.getBoundary()) + .and(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.F_UNIT.eq(productEntity.getFUnit())) + .and(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.P_WEIGHT.eq(productEntity.getPWeight())) + .and(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.P_SIZE.eq(productEntity.getPSize())) + .limit(1) + ); + if (Objects.isNull(existProduct)) { + productionVarietyService.save(productEntity); + } else { + productEntity = existProduct; + } + + IotCarbonCertificateEntity entity = dto.toEntity(); + entity.setShared(0); + entity.setProductId(productEntity.getId()); + entity.setProductName(productEntity.getName()); + save(entity); + } + + @Override + public IotCarbonCertificateDTO getByProductId(Long productId) { + return getOneAs( + QueryWrapper.create() + .eq(IotCarbonCertificateEntity::getProductId, productId) + .orderBy(IotCarbonCertificateEntity::getCreateDate) + .desc() + .limit(1), + IotCarbonCertificateDTO.class); + } + + @Override + public void share(List ids) { + List dtoList = + getMapper().selectListByIds(ids).stream() + .map(IotCarbonCertificateDTO::fromEntity) + .collect(Collectors.toList()); + dtoList.stream() + .collect(Collectors.groupingBy(IotCarbonCertificateDTO::getTenantCode)) + .forEach((tenantCode, list) -> fillData(list)); + + IotCarbonShareEntity shareInfo = + shareService.getOne( + QueryWrapper.create() + .eq( + IotCarbonShareEntity::getTenantCode, + UserContext.getRealTenantCode()) + .limit(1)); + dtoList.forEach( + dto -> { + dto.setShared(1); + certificateSyncService.sync(dto, shareInfo.getToken(), shareInfo.getUrl()); + }); + + List entities = ConvertUtils.sourceToTarget(dtoList, IotCarbonCertificateEntity.class); + updateBatch(entities); + } + + @Override + public void deleteDoc(List ids) { + UpdateChain.of(IotCarbonCertificateEntity.class) + .set(IotCarbonCertificateEntity::getCertificateUrl, null) + .where(IotCarbonCertificateEntity::getId) + .in(ids) + .update(); + } + + private void fillData(List dtoList) { + // 查询产品信息,并按id分组 + Set productIds = + dtoList.stream() + .map(IotCarbonCertificateDTO::getProductId) + .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(productIds)) { + return; + } + List productEntities = + productionVarietyService.listByIds(productIds); + Map productMap = + productEntities.stream() + .collect( + Collectors.toMap( + IotCarbonProductionVarietyEntity::getId, + Function.identity())); + + Set tenantCodes = + dtoList.stream() + .map(IotCarbonCertificateDTO::getTenantCode) + .collect(Collectors.toSet()); + Map tenantMap = + tenantDetailService.getMapper().selectListByIds(tenantCodes).stream() + .collect( + Collectors.toMap( + SysTenantDetailEntity::getId, + SysTenantDetailEntity::getName)); + + // 填充产品信息 + dtoList.forEach( + dto -> { + IotCarbonProductionVarietyEntity entity = productMap.get(dto.getProductId()); + if (entity != null) { + SimpleProductInfo info = SimpleProductInfo.fromProductEntity(entity); + dto.setProductInfo(info); + dto.setCompanyName(tenantMap.get(dto.getTenantCode())); + } + }); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/controller/ReconnectController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/controller/ReconnectController.java new file mode 100644 index 0000000..5b08b9b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/controller/ReconnectController.java @@ -0,0 +1,37 @@ +package com.thing.carbontrack.ck.datacleaning.controller; + + +import com.thing.carbontrack.ck.datacleaning.service.OrderService; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +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; + +@RestController +@RequestMapping("v2/ckOrder") +@Tag(name="常开") +@RequiredArgsConstructor +public class ReconnectController { + + + private final OrderService orderService; + + + + @GetMapping("reconnect") + @Operation(summary="重新接入数据,传入年月 [yyyyMM] 格式") + public Result reconnect(@RequestParam String month){ + //稳定后改成异步执行 + orderService.initProductInfo(month); + return new Result().ok("重新接入成功!"); + } + + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderBomDto.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderBomDto.java new file mode 100644 index 0000000..24e810f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderBomDto.java @@ -0,0 +1,45 @@ +package com.thing.carbontrack.ck.datacleaning.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CkOrderBomDto { + + @Schema(description = "最终产品id") + private Long productId; + @Schema(description = "父物料id") + private Long pid; + @Schema(description = "原料编码") + private String mcode; + @Schema(description = "原料名称") + private String mname; + @Schema(description = "原料类别") + private String mType; + @Schema(description = "原料单位") + private String unit; + @Schema(description = "原料重量") + private BigDecimal weight; + @Schema(description = "重量重量计量单位") + private String wUnit; + @Schema(description = "原料用量") + private BigDecimal dosage; + @Schema(description = "供应商编码") + private String supplierCode; + @Schema(description = "供应商名称") + private String supplierName; + @Schema(description = "供应商地址") + private String supplierAddress; + @Schema(description = "排放因子库id") + private Long crId; + @Schema(description = "排放因子库能源品种名称/运输方式名称") + private String crName; + @Schema(description = "运输距离") + private BigDecimal transportKm; + @Schema(description = "物料运输碳排:原料获取碳排(kg)=物料重量*物料用量*物料排放因子") + private BigDecimal transportUse; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderDto.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderDto.java new file mode 100644 index 0000000..883379d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/dto/CkOrderDto.java @@ -0,0 +1,27 @@ +package com.thing.carbontrack.ck.datacleaning.dto; + +import lombok.Data; + +import java.math.BigDecimal; + + +@Data +public class CkOrderDto { + + private Long mid; + + private String orderNo; + + private String productInfo; + + private BigDecimal hours; + + private BigDecimal qty; + + private String mCode; + + private String mName; + + private String customerName; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/DiscardJsonInit.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/DiscardJsonInit.java new file mode 100644 index 0000000..6f6fd09 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/DiscardJsonInit.java @@ -0,0 +1,27 @@ +package com.thing.carbontrack.ck.datacleaning.jsonInit; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +public class DiscardJsonInit { + + @Value("classpath:discard.json") + private Resource resource; + + @Getter + private String discardJson; + + @PostConstruct + public void init() throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(resource.getURI())); + this.discardJson = new String(bytes); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/ProductionJsonInit.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/ProductionJsonInit.java new file mode 100644 index 0000000..72b22a1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/ProductionJsonInit.java @@ -0,0 +1,30 @@ +package com.thing.carbontrack.ck.datacleaning.jsonInit; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +public class ProductionJsonInit { + + + @Value("classpath:production.json") + private Resource resource; + + @Getter + private String productionJson; + + @PostConstruct + public void init() throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(resource.getURI())); + this.productionJson = new String(bytes); + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/SetpsJsonInit.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/SetpsJsonInit.java new file mode 100644 index 0000000..d1616e9 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/SetpsJsonInit.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.ck.datacleaning.jsonInit; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + + +@Service +public class SetpsJsonInit { + + @Value("classpath:setps.json") + private Resource resource; + + @Getter + private String setpsJson; + + @PostConstruct + public void init() throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(resource.getURI())); + this.setpsJson = new String(bytes); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/TransportJsonInit.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/TransportJsonInit.java new file mode 100644 index 0000000..969d4a7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/TransportJsonInit.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.ck.datacleaning.jsonInit; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +public class TransportJsonInit { + + + @Value("classpath:transport.json") + private Resource resource; + + @Getter + private String transportJson; + + @PostConstruct + public void init() throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(resource.getURI())); + this.transportJson = new String(bytes); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/UseJsonInit.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/UseJsonInit.java new file mode 100644 index 0000000..37a0c63 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/jsonInit/UseJsonInit.java @@ -0,0 +1,27 @@ +package com.thing.carbontrack.ck.datacleaning.jsonInit; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.Resource; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +public class UseJsonInit { + + @Value("classpath:use.json") + private Resource resource; + + @Getter + private String useJson; + + @PostConstruct + public void init() throws IOException { + byte[] bytes = Files.readAllBytes(Paths.get(resource.getURI())); + this.useJson = new String(bytes); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderBomMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderBomMapper.java new file mode 100644 index 0000000..9508b71 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderBomMapper.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.ck.datacleaning.mapper; + + +import com.mybatisflex.annotation.UseDataSource; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderBomDto; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +@UseDataSource("sqlServer") +public interface CkOrderBomMapper { + + List copperBarInfo(String orderNo); + + + List bomInfo(String orderNo); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderMapper.java new file mode 100644 index 0000000..459adb2 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/mapper/CkOrderMapper.java @@ -0,0 +1,15 @@ +package com.thing.carbontrack.ck.datacleaning.mapper; + +import com.mybatisflex.annotation.UseDataSource; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderDto; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@UseDataSource("sqlServer") +@Mapper +public interface CkOrderMapper { + + List getOrderList(String month); + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/OrderService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/OrderService.java new file mode 100644 index 0000000..dbb63e8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/OrderService.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.ck.datacleaning.service; + + + + +public interface OrderService { + + + void initProductInfo(String month); + + + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/impl/OrderServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/impl/OrderServiceImpl.java new file mode 100644 index 0000000..7ba676a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/service/impl/OrderServiceImpl.java @@ -0,0 +1,429 @@ +package com.thing.carbontrack.ck.datacleaning.service.impl; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.Indirect.dto.IotCarbonIndirectEnergyDTO; +import com.thing.carbontrack.bom.entity.IotCarbonBomEntity; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderBomDto; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderDto; +import com.thing.carbontrack.ck.datacleaning.jsonInit.*; +import com.thing.carbontrack.ck.datacleaning.mapper.CkOrderBomMapper; +import com.thing.carbontrack.ck.datacleaning.mapper.CkOrderMapper; +import com.thing.carbontrack.ck.datacleaning.service.OrderService; +import com.thing.carbontrack.event.standardcal.ProductionProcessEvent; +import com.thing.carbontrack.outbound.entity.IotCarbonOutboundConfigEntity; +import com.thing.carbontrack.outbound.mapper.IotCarbonOutboundConfigMapper; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.productionRecord.dto.OrderParam; +import com.thing.carbontrack.productionRecord.dto.ProductionParam; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.routematrix.dto.PlaceParam; +import com.thing.carbontrack.routematrix.service.RoutematrixService; +import com.thing.carbontrack.steps.entity.IotCarbonProcessStepsEntity; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.carbontrack.useConfig.entity.IotCarbonUseConfigEntity; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.utils.IdGenerator; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.math.RoundingMode; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class OrderServiceImpl implements OrderService { + private static final Long tenantCode = 463020701385752576L; + + private static final String supplierName = "八车间成套"; + + private static final String destinations = "31.693836,120.719728"; + + //道路运输(货运)平均 + private static final Long crId = 166755918688608257L; + + private static final Long A29EVID= 159136210321707008L; + + @Autowired + private CkOrderMapper ckOrderMapper; + @Autowired + private CkOrderBomMapper ckOrderBomMapper; + @Autowired + private RoutematrixService routematrixService; + @Autowired + private IotCarbonProductionVarietyService productionVarietyService; + @Autowired + private IotCarbonBomService carbonBomService; + @Autowired + private IotCarbonProcessStepsService carbonProcessStepsService; + @Autowired + private IotCarbonProductionRecordService iotCarbonProductionRecordService; + + @Autowired + private IotCarbonOutboundConfigMapper iotCarbonOutboundConfigMapper; + + @Autowired + private ProductionJsonInit productionJsonInit; + @Autowired + private SetpsJsonInit setpsJsonInit; + + @Autowired + private DiscardJsonInit discardJsonInit; + @Autowired + private TransportJsonInit transportJsonInit; + @Autowired + private UseJsonInit useJsonInit; + @Autowired + private IotCarbonUseConfigService carbonUseConfigService; + + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + @Override + public void initProductInfo(String month) { + List orderDtos = ckOrderMapper.getOrderList(month); + + List reslutDtos = new ArrayList<>(); + //总产量 + BigDecimal totalNum = BigDecimal.ZERO; + //总工时 + BigDecimal totalHour = BigDecimal.ZERO; + //总重量 + BigDecimal totalWeight = BigDecimal.ZERO; + Map copperBarMap = new HashMap<>(); + + for (CkOrderDto temp : orderDtos) { + totalNum = totalNum.add(temp.getQty()); + totalHour = totalHour.add(temp.getHours()); + //铜排的用量以及供应商信息 + List copperBarInfos = ckOrderBomMapper.copperBarInfo(temp.getOrderNo()); + //其他bom的用量以及供应商信息 + List bomInfos = ckOrderBomMapper.bomInfo(temp.getOrderNo()); + if(ObjectUtils.isNotEmpty(copperBarInfos)||ObjectUtils.isNotEmpty(bomInfos)){ + //temp 是产品, copperBarInfos是 铜排, bomInfos是原料 + IotCarbonProductionVarietyEntity productionEntity = JSONObject.toJavaObject((JSON) JSONObject.parse(productionJsonInit.getProductionJson()), IotCarbonProductionVarietyEntity.class); + productionEntity.setCode(productionEntity.getCode()+temp.getOrderNo()); + productionEntity.setName(productionEntity.getName()+temp.getOrderNo()); + productionEntity.setModel(temp.getProductInfo()); + initProductionEntity(productionEntity); + //初始化产品信息 已存在产品,更新 + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("code",productionEntity.getCode()); + wrapper.eq("name",productionEntity.getName()); + IotCarbonProductionVarietyEntity entity = productionVarietyService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + productionEntity.setId(entity.getId()); + productionVarietyService.updateById(productionEntity); + }else { + productionVarietyService.save(productionEntity); + } + temp.setMid(productionEntity.getId()); + temp.setMName(productionEntity.getName()); + temp.setMCode(productionEntity.getCode()); + reslutDtos.add( ConvertUtils.sourceToTarget(temp,CkOrderDto.class)); + //初始化bom信息 + copperBarInfos.addAll(bomInfos); + for (CkOrderBomDto info : copperBarInfos) { + IotCarbonBomEntity bomEntity = ConvertUtils.sourceToTarget(info,IotCarbonBomEntity.class); + //原料是否已经存在,已经存在更新,不去动重量和碳排 + IotCarbonBomEntity fBomEntity = carbonBomService.queryOneBuyParam(productionEntity.getId(),productionEntity.getId(),bomEntity.getMcode()); + if(ObjectUtils.isNotEmpty(fBomEntity)){ + //之前有这个原料 + bomEntity= ConvertUtils.sourceToTarget(fBomEntity,IotCarbonBomEntity.class); + }else { + //测试使用,随机一个重量 + bomEntity.setWeight(BigDecimal.valueOf(Math.random()).setScale(4,RoundingMode.UP)); + //测试使用,随机插入一个物料碳排值 + bomEntity.setMuUse(BigDecimal.valueOf(Math.random() * 2).setScale(4,RoundingMode.UP)); + bomEntity.setProductId(productionEntity.getId()); + bomEntity.setPid(productionEntity.getId()); + bomEntity.setM_type("1"); + //默认道路运输(货运)平均 + bomEntity.setCrId(crId); + } + //之前无需修改得铜排,也需要参与铜排总重量得计算 + if(ObjectUtils.isEmpty(bomEntity.getMname())||bomEntity.getMname().contains("铜排")){ + bomEntity.setUnit("批"); + //新进来铜排,重量和用量翻转一下 + if(ObjectUtils.isEmpty(bomEntity.getMname())){ + bomEntity.setWeight(bomEntity.getDosage()); + bomEntity.setDosage(BigDecimal.valueOf(1)); + } + if(bomEntity.getSupplierName().equals(supplierName)){ + bomEntity.setM_type("2"); + bomEntity.setMname("厂内加工铜排"); + copperBarMap.put(temp.getOrderNo(),bomEntity.getWeight()); + totalWeight = totalWeight.add(bomEntity.getWeight()); + }else { + bomEntity.setMname("采购铜排"); + } + } + if(ObjectUtils.isEmpty(bomEntity.getUnit())){ + bomEntity.setUnit("kg"); + } + //bom中查找运输距离 + BigDecimal transportKm = carbonBomService.getKmBySupplierAddress(bomEntity.getSupplierAddress()); + if(transportKm==null|| transportKm.scale() == 0 && transportKm.unscaledValue().equals(BigInteger.ZERO)){ + PlaceParam param = new PlaceParam(); + param.setQuery(bomEntity.getSupplierAddress()); + param.setDestination(destinations); + String km = routematrixService.routematrixSearch(param).getKm(); + try { + bomEntity.setTransportKm(new BigDecimal(km)); + } catch (Exception e) { + bomEntity.setTransportKm(new BigDecimal("0")); + } + }else { + bomEntity.setTransportKm(transportKm); + } + carbonBomService.initTransportUse(bomEntity); + if(ObjectUtils.isNotEmpty(bomEntity.getId())){ + carbonBomService.updateById(bomEntity); + }else { + carbonBomService.save(bomEntity); + } + } + //初始化工序信息 + List stepsEntityList = JSON.parseArray(setpsJsonInit.getSetpsJson(), IotCarbonProcessStepsEntity.class); + stepsEntityList.forEach(step->{ + step.setMid(productionEntity.getId()); + }); + carbonProcessStepsService.saveOrUpdateInfo(stepsEntityList); + //配置记录得生成 1.运输配置生成 + IotCarbonUseConfigEntity tConfigEntity = JSONObject.toJavaObject((JSON) JSONObject.parse(transportJsonInit.getTransportJson()), IotCarbonUseConfigEntity.class); + IotCarbonUseConfigEntity.init(tConfigEntity,productionEntity,temp); + transportInit(tConfigEntity); + //出库记录的生成 + initOutboundConfig(tConfigEntity,temp); + //配置记录,产品废弃生成 + IotCarbonUseConfigEntity fConfigEntity = JSONObject.toJavaObject((JSON) JSONObject.parse(discardJsonInit.getDiscardJson()), IotCarbonUseConfigEntity.class); + IotCarbonUseConfigEntity.useInit(fConfigEntity,productionEntity); + useInit(fConfigEntity,"3"); + IotCarbonUseConfigEntity uConfigEntity = JSONObject.toJavaObject((JSON) JSONObject.parse(useJsonInit.getUseJson()), IotCarbonUseConfigEntity.class); + IotCarbonUseConfigEntity.useInit(uConfigEntity,productionEntity); + useInit(uConfigEntity,"2"); + + } + } + + //工单工序,生成产品工单,生成公摊记录 + for (CkOrderDto orderDto : reslutDtos) { + ProductionParam productionParam = iotCarbonProductionRecordService.listByPrCode(orderDto.getOrderNo(),orderDto.getMid()); + BigDecimal copperWeight = copperBarMap.get(orderDto.getOrderNo()); + if(copperWeight==null){ + copperWeight = BigDecimal.ZERO; + } + initProductionParam(productionParam,orderDto,month,totalNum,totalHour,totalWeight,copperWeight); + //常开的工单类型 + productionParam.setOrderType("1"); + iotCarbonProductionRecordService.saveProductionParam(productionParam); + } + applicationEventPublisher.publishEvent(new ProductionProcessEvent(this,"1")); + } + + + + + + /** + * 初始化出库记录 + * @param carbonUseConfigEntity + * @param ckOrderDto + */ + private void initOutboundConfig(IotCarbonUseConfigEntity carbonUseConfigEntity,CkOrderDto ckOrderDto){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",carbonUseConfigEntity.getM_id()); + wrapper.eq("pr_code",ckOrderDto.getOrderNo()); + wrapper.eq("customer_name",carbonUseConfigEntity.getCustomerName()); + wrapper.eq("customer_address",carbonUseConfigEntity.getCustomerAddress()); + IotCarbonOutboundConfigEntity entity = iotCarbonOutboundConfigMapper.selectOneByQuery(wrapper); + //不存在,new 一个 插入进去 + if(ObjectUtils.isEmpty(entity)){ + entity = IotCarbonOutboundConfigEntity.init(carbonUseConfigEntity,ckOrderDto); + iotCarbonOutboundConfigMapper.insert(entity); + } + } + + + /** + * 初始化运输配置 + * @param tConfigEntity + * @return + */ + private void transportInit(IotCarbonUseConfigEntity tConfigEntity ){ + //运输里程的生成 + PlaceParam param = new PlaceParam(); + param.setQuery(tConfigEntity.getCustomerAddress()); + param.setDestination(destinations); + String km = routematrixService.routematrixSearch(param).getKm(); + try { + tConfigEntity.setUse(new BigDecimal(km)); + } catch (Exception e) { + tConfigEntity.setUse(new BigDecimal("0")); + } + //这个产品运输配置是否存在,存在的话,就不操作了,不存在就新增 + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",tConfigEntity.getM_id()); + wrapper.eq("customer_name",tConfigEntity.getCustomerName()); + wrapper.eq("customer_address",tConfigEntity.getCustomerAddress()); + wrapper.eq("type","1"); + wrapper.limit(1); + IotCarbonUseConfigEntity entity = carbonUseConfigService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + tConfigEntity.setId(entity.getId()); + }else { + tConfigEntity.setId(IdGenerator.nextId()); + carbonUseConfigService.save(tConfigEntity); + } + } + + + + /** + * 初始化运输配置 + * @param tConfigEntity + * @return + */ + private void useInit(IotCarbonUseConfigEntity tConfigEntity,String type ){ + //这个产品运输配置是否存在,存在的话,就不操作了,不存在就新增 + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",tConfigEntity.getM_id()); + wrapper.eq("type",type); + wrapper.limit(1); + IotCarbonUseConfigEntity entity = carbonUseConfigService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + tConfigEntity.setId(entity.getId()); + }else { + tConfigEntity.setId(IdGenerator.nextId()); + carbonUseConfigService.save(tConfigEntity); + } + } + + + /** + * 初始化报工单 + * @param productionParam //总产量 + */ + private void initProductionParam(ProductionParam productionParam,CkOrderDto orderDto,String yearMonth, + BigDecimal totalNum, BigDecimal totalHour, BigDecimal totalWeight,BigDecimal copperWeight){ + try { + // 构建日期字符串并解析 + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMM"); + Date date = dateFormat.parse(yearMonth); + // 设置为月份的第一天 + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + calendar.set(Calendar.DAY_OF_MONTH, 1); // 设置为第一天 + // 获取月份开始时间 + Date monthStartDate = calendar.getTime(); + // 如果需要获取精确到时分秒的开始时间,可以设置时间为 00:00:00 + calendar.setTime(date); + calendar.add(Calendar.MONTH, 1); // 下个月的第一天 + calendar.set(Calendar.DAY_OF_MONTH, 1); // 设置为第一天 + // 回退一天,获取当前月份的最后一天 + calendar.add(Calendar.DAY_OF_MONTH, -1); + // 设置时间为最后一秒 + calendar.set(Calendar.HOUR_OF_DAY, 23); + calendar.set(Calendar.MINUTE, 59); + calendar.set(Calendar.SECOND, 59); + calendar.set(Calendar.MILLISECOND, 999); + Date lastSecondOfMonth = calendar.getTime(); + + productionParam.setM_id(orderDto.getMid()); + productionParam.setMName(orderDto.getMName()); + productionParam.setMCode(orderDto.getMCode()); + BigDecimal prDur = orderDto.getHours().divide(new BigDecimal(5),1,RoundingMode.UP); + productionParam.setPrDur(prDur); + productionParam.setFinalNum(orderDto.getQty().longValue()); + productionParam.setPNum(orderDto.getQty().longValue()); + productionParam.setWorkName("八车间"); + productionParam.setPrType("1"); + productionParam.setPrCode(orderDto.getOrderNo()); + productionParam.setStartTime(monthStartDate); + productionParam.setEndTime(lastSecondOfMonth); + //产量的占比 + BigDecimal yieldRatio = orderDto.getQty().divide(totalNum,4, RoundingMode.UP).multiply(new BigDecimal(100)); + //工时的占比 + BigDecimal hourRatio = orderDto.getHours().divide(totalHour,4, RoundingMode.UP).multiply(new BigDecimal(100)); + //铜排重量的占比 + BigDecimal weightRatio = copperWeight.divide(totalWeight,4, RoundingMode.UP).multiply(new BigDecimal(100)); + for (OrderParam orderParam : productionParam.getOrderParams()) { + //工序编码不同,入的比率不同 + if(orderParam.getPsCode().equals("A001")||orderParam.getPsCode().equals("A002")||orderParam.getPsCode().equals("A003")){ + //后续如果有其他能源品种,在里面追加判断即可 + if(orderParam.getPsCode().equals("A001")){ + orderParam.setThingCode("A_1"); + } + if(orderParam.getPsCode().equals("A002")){ + orderParam.setThingCode("A_2"); + } + if(orderParam.getPsCode().equals("A003")){ + orderParam.setThingCode("A_3"); + } + orderParam.setRatio(weightRatio.setScale(2,RoundingMode.UP)); + }else if(orderParam.getPsCode().equals("A004")){ + orderParam.setThingCode("A_4"); + orderParam.setRatio(hourRatio.setScale(2,RoundingMode.UP)); + }else if (orderParam.getPsCode().equals("A005")){ + orderParam.setThingCode("A_5"); + orderParam.setRatio(yieldRatio.setScale(2,RoundingMode.UP)); + } + } + //公摊数据初始化 + productionParam.setIndirectEnergyDTOS(this.initIndirectEnergyDTO(yieldRatio)); + } catch (ParseException e) { + e.printStackTrace(); + } + } + + private List initIndirectEnergyDTO(BigDecimal yieldRatio){ + List resultList = new ArrayList<>(); + + IotCarbonIndirectEnergyDTO ktdto = new IotCarbonIndirectEnergyDTO(); + ktdto.setEvId(A29EVID); + ktdto.setIeCode("A"); + ktdto.setIeName("空调"); + ktdto.setThingCode("A_6"); + ktdto.setRatio(yieldRatio); + resultList.add(ktdto); + + IotCarbonIndirectEnergyDTO zmdto = new IotCarbonIndirectEnergyDTO(); + zmdto.setEvId(A29EVID); + zmdto.setIeCode("B"); + zmdto.setIeName("照明"); + zmdto.setThingCode("A_7"); + zmdto.setRatio(yieldRatio); + resultList.add(zmdto); + return resultList; + } + + + + /** + * 初始化补全产品信息 + * @param productionVariety + */ + private void initProductionEntity(IotCarbonProductionVarietyEntity productionVariety){ + productionVariety.setCompanyId(tenantCode); + productionVariety.setTenantCode(tenantCode); + productionVariety.setDeptId(tenantCode); + productionVariety.setCreateDate(System.currentTimeMillis()); + } + + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/task/TestController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/task/TestController.java new file mode 100644 index 0000000..e1fb232 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ck/datacleaning/task/TestController.java @@ -0,0 +1,40 @@ +package com.thing.carbontrack.ck.datacleaning.task; + +import com.thing.carbontrack.ck.datacleaning.service.OrderService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@RestController +@RequestMapping("v2/orderTest") +@RequiredArgsConstructor +public class TestController { + + private final OrderService orderService; + + + @GetMapping("test") + public Object test(){ + String lastMonth = getLastMonth(); + orderService.initProductInfo(lastMonth); + return null; + } + + + + + + + private String getLastMonth(){ + // 获取当前日期的上一个月的年月 + LocalDate lastMonth = LocalDate.now().minusMonths(1); + // 格式化为yyyyMM + return lastMonth.format(DateTimeFormatter.ofPattern("yyyyMM")); + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/controller/IotCarbonProductionCustomController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/controller/IotCarbonProductionCustomController.java new file mode 100644 index 0000000..29a80f7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/controller/IotCarbonProductionCustomController.java @@ -0,0 +1,62 @@ +package com.thing.carbontrack.custom.controller; + +import com.thing.carbontrack.custom.dto.IotCarbonProductionCustomDTO; +import com.thing.carbontrack.custom.service.IotCarbonProductionCustomService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** +* 产品自定义信息 +* +* @author xc +* @since 3.0 2024-06-21 +*/ +@RestController +@RequestMapping("v2/custom/iotcarbonproductioncustom") +@Tag(name="产品自定义信息") +@RequiredArgsConstructor +public class IotCarbonProductionCustomController { + + private final IotCarbonProductionCustomService iotCarbonProductionCustomService; + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionCustomDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonProductionCustomService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionCustomDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProductionCustomService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionCustomService.batchDelete(ids); + return new Result<>(); + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/dto/IotCarbonProductionCustomDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/dto/IotCarbonProductionCustomDTO.java new file mode 100644 index 0000000..f8c4282 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/dto/IotCarbonProductionCustomDTO.java @@ -0,0 +1,30 @@ +package com.thing.carbontrack.custom.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 产品自定义信息 +* +* @author xc +* @since 3.0 2024-06-21 +*/ +@Data +@Schema(description = "产品自定义信息") +public class IotCarbonProductionCustomDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "产品id") + private Long productionId; + @Schema(description = "字段名称描述") + private String title; + @Schema(description = "字段信息详情") + private String info; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/entity/IotCarbonProductionCustomEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/entity/IotCarbonProductionCustomEntity.java new file mode 100644 index 0000000..834b3c1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/entity/IotCarbonProductionCustomEntity.java @@ -0,0 +1,43 @@ +package com.thing.carbontrack.custom.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 产品自定义信息 + * + * @author xc + * @since 3.0 2024-06-21 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_custom") +public class IotCarbonProductionCustomEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 产品id + */ + private Long productionId; + /** + * 字段名称描述 + */ + private String title; + /** + * 字段信息详情 + */ + private String info; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/mapper/IotCarbonProductionCustomMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/mapper/IotCarbonProductionCustomMapper.java new file mode 100644 index 0000000..c41670f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/mapper/IotCarbonProductionCustomMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.custom.mapper; + +import com.thing.carbontrack.custom.entity.IotCarbonProductionCustomEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 产品自定义信息 +* +* @author xc +* @since 3.0 2024-06-21 +*/ +@Mapper +public interface IotCarbonProductionCustomMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/IotCarbonProductionCustomService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/IotCarbonProductionCustomService.java new file mode 100644 index 0000000..42ea774 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/IotCarbonProductionCustomService.java @@ -0,0 +1,20 @@ +package com.thing.carbontrack.custom.service; + +import com.thing.carbontrack.custom.dto.IotCarbonProductionCustomDTO; +import com.thing.carbontrack.custom.entity.IotCarbonProductionCustomEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; + +/** + * 产品自定义信息 + * + * @author xc + * @since 3.0 2024-06-21 + */ +public interface IotCarbonProductionCustomService extends IBaseService { + + + List getInfoByProductionId(Long productionId); + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/impl/IotCarbonProductionCustomServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/impl/IotCarbonProductionCustomServiceImpl.java new file mode 100644 index 0000000..a1f7187 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/custom/service/impl/IotCarbonProductionCustomServiceImpl.java @@ -0,0 +1,36 @@ +package com.thing.carbontrack.custom.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.custom.dto.IotCarbonProductionCustomDTO; +import com.thing.carbontrack.custom.entity.IotCarbonProductionCustomEntity; +import com.thing.carbontrack.custom.mapper.IotCarbonProductionCustomMapper; +import com.thing.carbontrack.custom.service.IotCarbonProductionCustomService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 产品自定义信息 + * + * @author xc + * @since 3.0 2024-06-21 + */ +@Service +public class IotCarbonProductionCustomServiceImpl extends BaseServiceImpl implements IotCarbonProductionCustomService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public List getInfoByProductionId(Long productionId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("production_id",productionId); + return this.listAs(wrapper,IotCarbonProductionCustomDTO.class); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/IndirectEnergyEvent.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/IndirectEnergyEvent.java new file mode 100644 index 0000000..b61c6fb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/IndirectEnergyEvent.java @@ -0,0 +1,21 @@ +package com.thing.carbontrack.event.standardcal; + + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + + +@Getter +@Setter +/** + * 间接碳排计算 + */ +public class IndirectEnergyEvent extends ApplicationEvent{ + + private String orderType; + public IndirectEnergyEvent(Object source,String orderType) { + super(source); + this.orderType=orderType; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessEvent.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessEvent.java new file mode 100644 index 0000000..55239d5 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessEvent.java @@ -0,0 +1,25 @@ +package com.thing.carbontrack.event.standardcal; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; + +@Getter +@Setter +/** + * 生产碳排计算 + */ +public class ProductionProcessEvent extends ApplicationEvent { + + @Serial + private static final long serialVersionUID = -8330363330704274977L; + private String orderType; + + public ProductionProcessEvent(Object source,String orderType) { + super(source); + this.orderType=orderType; + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessStatusEvent.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessStatusEvent.java new file mode 100644 index 0000000..b6bd47a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionProcessStatusEvent.java @@ -0,0 +1,20 @@ +package com.thing.carbontrack.event.standardcal; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; +@Getter +@Setter +/** + * 工单状态翻转 + */ +public class ProductionProcessStatusEvent extends ApplicationEvent { + + + private String orderType; + + public ProductionProcessStatusEvent(Object source,String orderType) { + super(source); + this.orderType=orderType; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionResultEvent.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionResultEvent.java new file mode 100644 index 0000000..c783f08 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/ProductionResultEvent.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.event.standardcal; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + + +@Getter +@Setter +/** + * 工单全流程碳排计算 + */ +public class ProductionResultEvent extends ApplicationEvent { + + public ProductionResultEvent(Object source) { + super(source); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/IndirectEnergyListener.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/IndirectEnergyListener.java new file mode 100644 index 0000000..2833293 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/IndirectEnergyListener.java @@ -0,0 +1,188 @@ +package com.thing.carbontrack.event.standardcal.listener; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.carbontrack.Indirect.service.IotCarbonIndirectEnergyService; +import com.thing.carbontrack.event.standardcal.IndirectEnergyEvent; +import com.thing.carbontrack.event.standardcal.ProductionProcessStatusEvent; +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Slf4j +@Component +@RequiredArgsConstructor +/** + * 公摊计算 + */ +public class IndirectEnergyListener { + + + private final TsKvService tskvService; + + private final IotCarbonIndirectEnergyService iotCarbonIndirectEnergyService; + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonRatioService carbonRatioService; + + private final IotCarbonProductionProcessVarietyService processVarietyService; + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + private final ApplicationEventPublisher applicationEventPublisher; + + + @EventListener(IndirectEnergyEvent.class) + public void start(IndirectEnergyEvent event) throws InterruptedException { + //获取所有未计算得 间接用能记录 + List indirectEntitys = iotCarbonIndirectEnergyService.getListByStatus("1"); + indirectEntitys.forEach(temp->{ + //当前 公摊对应得工单记录列表 由产品id+工单编码+报工组id查询而来 + List recordEntities = iotCarbonProductionRecordService.getListByParam(temp.getM_id(),temp.getPrCode(),temp.getGroupId()); + //判断数据是设备接入,还是能源数据录入 + try { + if(temp.getPrType().equals("1")){ + recordEntities.forEach(recordEntity->{ + lotCalculation(temp,recordEntity); + }); + }else { + recordEntities.forEach(recordEntity->{ + timePeriodCalculation(temp,recordEntity); + }); + } + temp.setPrStatus(2L); + } catch (Exception e) { + e.printStackTrace(); + temp.setPrStatus(3L); + } + }); + iotCarbonIndirectEnergyService.updateBatch(indirectEntitys); + applicationEventPublisher.publishEvent(new ProductionProcessStatusEvent(this,event.getOrderType())); + } + + + /** + * 批次计算 + * @param temp + * @param recordEntity + */ + private void lotCalculation(IotCarbonIndirectEnergyEntity temp, IotCarbonProductionRecordEntity recordEntity) { + BigDecimal ratio = this.getRatioByEvId(temp.getEvId()); + //批次计算,先获取设备总能耗 + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(temp,recordEntity); + //获取对应能源品种得编码 + CarbonEnergyVarietyEntity energy = carbonEnergyVarietyService.getById(temp.getEvId()); + String attrCode = energy.getCode()+"hh"; + //查询tskv + TsKvDTO tsKvDTO = tskvService.findTsKvAggByCodeAndAttr(temp.getThingCode(),attrCode,recordEntity.getStartTime().getTime(),recordEntity.getEndTime().getTime(), AggType.SUM); + BigDecimal use = new BigDecimal(tsKvDTO.getVal()==null?"0":tsKvDTO.getVal()); + BigDecimal newRatio = temp.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(newRatio).divide(new BigDecimal(temp.getStepsNum()), RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + this.saveOrUpdate(varietyEntity); + } + + + + + + /** + * 时段计算 + * @param temp + * @param recordEntity + */ + private void timePeriodCalculation(IotCarbonIndirectEnergyEntity temp, IotCarbonProductionRecordEntity recordEntity) { + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(temp,recordEntity); + //折标媒系数 + BigDecimal ratio = this.getRatioByEvId(temp.getEvId()); + //计算总能耗 == 先计算 pNum 除以 finalNum,并设置保留小数位数为 4 + getTimePeriodResult(temp, ratio,varietyEntity,recordEntity); + //判断当前entity 是否存在,存在则更新,不存在则新增 拿prId 去结果中查询 + this.saveOrUpdate(varietyEntity); + + } + + private BigDecimal getRatioByEvId(Long evId){ + return carbonRatioService.queryOnByEvIdAndType(evId,"1").getRatio(); + } + + /** + * 保存,还是修改数据 + * @param varietyEntity + */ + private void saveOrUpdate(IotCarbonProductionProcessVarietyEntity varietyEntity){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("pr_id",varietyEntity.getPrId()); + wrapper.eq("ps_id",varietyEntity.getPsId()); + wrapper.eq("m_id",varietyEntity.getMId()); + wrapper.eq("ev_id",varietyEntity.getEvId()); + IotCarbonProductionProcessVarietyEntity entity = processVarietyService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + varietyEntity.setId(entity.getId()); + processVarietyService.updateById(varietyEntity); + }else { + processVarietyService.save(varietyEntity); + } + } + + /** + * 区间数据 最终碳排计算 + * @param entity + * @param ratio + * @return + */ + @NotNull + private static void getTimePeriodResult(IotCarbonIndirectEnergyEntity entity, BigDecimal ratio,IotCarbonProductionProcessVarietyEntity varietyEntity,IotCarbonProductionRecordEntity recordEntity) { + BigDecimal pNumBigDecimal = new BigDecimal(recordEntity.getPNum()); + BigDecimal finalNumBigDecimal = new BigDecimal(recordEntity.getFinalNum()); + BigDecimal oneRatio = finalNumBigDecimal.divide(pNumBigDecimal, 4, RoundingMode.HALF_UP); + //计算总能耗 == 先计算 能耗分摊比率 + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal use = entity.getDosage().multiply(newRatio).setScale(4, RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(oneRatio).divide(new BigDecimal(entity.getStepsNum()), RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + } + + /** + * 初始化计算结果对象 + * @return + */ + private IotCarbonProductionProcessVarietyEntity initVarietyEntity(IotCarbonIndirectEnergyEntity temp,IotCarbonProductionRecordEntity entity ){ + IotCarbonProductionProcessVarietyEntity varietyEntity = new IotCarbonProductionProcessVarietyEntity(); + varietyEntity.setPsId(entity.getPsId()); + varietyEntity.setPrId(temp.getId()); + varietyEntity.setPrCode(entity.getPrCode()); + varietyEntity.setEvId(temp.getEvId()); + varietyEntity.setFinal_num(entity.getFinalNum()); + varietyEntity.setMId(temp.getM_id()); + varietyEntity.setType("3"); + return varietyEntity; + } + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessListener.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessListener.java new file mode 100644 index 0000000..067b23d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessListener.java @@ -0,0 +1,167 @@ +package com.thing.carbontrack.event.standardcal.listener; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.event.standardcal.IndirectEnergyEvent; +import com.thing.carbontrack.event.standardcal.ProductionProcessEvent; +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + + +@Slf4j +@Component +@RequiredArgsConstructor +/** + * 生产计算 + */ +public class ProductionProcessListener { + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final TsKvService tskvService; + + private final IotCarbonRatioService carbonRatioService; + + private final IotCarbonProductionProcessVarietyService processVarietyService; + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + + private final ApplicationEventPublisher applicationEventPublisher; + + + @EventListener(ProductionProcessEvent.class) + public void start(ProductionProcessEvent event) throws InterruptedException { + //获取所有未计算得 报工记录 + List productionRecordList = iotCarbonProductionRecordService.getListByStatus("1"); + productionRecordList.forEach(temp->{ + try { + if(temp.getPrType().equals("1")){ + lotCalculation(temp); + }else { + timePeriodCalculation(temp); + } + temp.setPrStatus(2L); + } catch (Exception e) { + temp.setPrStatus(3L); + } + }); + iotCarbonProductionRecordService.updateBatch(productionRecordList); + applicationEventPublisher.publishEvent(new IndirectEnergyEvent(this,event.getOrderType())); + } + + /** + * 时段计算 + * @param entity + */ + private void timePeriodCalculation(IotCarbonProductionRecordEntity entity){ + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(entity); + //折标媒系数 + BigDecimal ratio = this.getRatioByEvId(entity.getEvId()); + //计算总能耗 == 先计算 pNum 除以 finalNum,并设置保留小数位数为 4 + getTimePeriodResult(entity, ratio,varietyEntity); + //判断当前entity 是否存在,存在则更新,不存在则新增 拿prId 去结果中查询 + this.saveOrUpdate(varietyEntity); + } + + /** + * 批次计算 + * @param entity + */ + private void lotCalculation(IotCarbonProductionRecordEntity entity){ + BigDecimal ratio = this.getRatioByEvId(entity.getEvId()); + //批次计算,先获取设备总能耗 + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(entity); + //获取对应能源品种得编码 + CarbonEnergyVarietyEntity energy = carbonEnergyVarietyService.getById(entity.getEvId()); + String attrCode = energy.getCode()+"hh"; + //查询tskv + TsKvDTO tsKvDTO = tskvService.findTsKvAggByCodeAndAttr(entity.getThingCode(),attrCode,entity.getStartTime().getTime(),entity.getEndTime().getTime(), AggType.SUM); + BigDecimal use = new BigDecimal(tsKvDTO.getVal()==null?"0":tsKvDTO.getVal()); + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"),4, RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(newRatio).setScale(8, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(8, RoundingMode.HALF_UP); + varietyEntity.setCarbon(carbon); + varietyEntity.setUsage(finalUse); + this.saveOrUpdate(varietyEntity); + } + + private BigDecimal getRatioByEvId(Long evId){ + return carbonRatioService.queryOnByEvIdAndType(evId,"1").getRatio(); + } + + /** + * 保存,还是修改数据 + * @param varietyEntity + */ + private void saveOrUpdate(IotCarbonProductionProcessVarietyEntity varietyEntity){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("pr_id",varietyEntity.getPrId()); + IotCarbonProductionProcessVarietyEntity entity = processVarietyService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + varietyEntity.setId(entity.getId()); + processVarietyService.updateById(varietyEntity); + }else { + processVarietyService.save(varietyEntity); + } + } + + /** + * 区间数据 最终碳排计算 + * @param entity + * @param ratio + * @return + */ + @NotNull + private static void getTimePeriodResult(IotCarbonProductionRecordEntity entity, BigDecimal ratio,IotCarbonProductionProcessVarietyEntity varietyEntity) { + BigDecimal pNumBigDecimal = new BigDecimal(entity.getPNum()); + BigDecimal finalNumBigDecimal = new BigDecimal(entity.getFinalNum()); + BigDecimal oneRatio = finalNumBigDecimal.divide(pNumBigDecimal, 4, RoundingMode.HALF_UP); + + //计算总能耗 == 先计算 能耗分摊比率 + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal use = entity.getDosage().multiply(newRatio).setScale(4, RoundingMode.HALF_UP); + + BigDecimal finalUse=use.multiply(oneRatio).setScale(4, RoundingMode.HALF_UP); + + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + } + + /** + * 初始化计算结果对象 + * @param entity + * @return + */ + private IotCarbonProductionProcessVarietyEntity initVarietyEntity(IotCarbonProductionRecordEntity entity ){ + IotCarbonProductionProcessVarietyEntity varietyEntity = new IotCarbonProductionProcessVarietyEntity(); + varietyEntity.setPsId(entity.getPsId()); + varietyEntity.setPrId(entity.getId()); + varietyEntity.setPrCode(entity.getPrCode()); + varietyEntity.setEvId(entity.getEvId()); + varietyEntity.setFinal_num(entity.getFinalNum()); + varietyEntity.setMId(entity.getM_id()); + varietyEntity.setType("2"); + return varietyEntity; + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessStatusListener.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessStatusListener.java new file mode 100644 index 0000000..8735e3e --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionProcessStatusListener.java @@ -0,0 +1,49 @@ +package com.thing.carbontrack.event.standardcal.listener; + +import com.thing.carbontrack.event.standardcal.ProductionProcessStatusEvent; +import com.thing.carbontrack.event.standardcal.ProductionResultEvent; +import com.thing.carbontrack.productionRecord.dto.RecordReq; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +@Slf4j +@Component +@RequiredArgsConstructor +/** + * 工单状态翻转 + */ +public class ProductionProcessStatusListener{ + + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonProcessStepsService iotCarbonProcessStepsService; + + private final ApplicationEventPublisher applicationEventPublisher; + + @EventListener(ProductionProcessStatusEvent.class) + public void start(ProductionProcessStatusEvent event){ + //查询已计算状态工单,工序数量,工序数量与当前bom数量一致,则翻转工单状态为 全部计算完成 + List recordReqList = iotCarbonProductionRecordService.getRecordReqList(); + recordReqList.forEach(temp->{ + Long num = iotCarbonProcessStepsService.evNumByMid(temp.getMId()); + if(Objects.equals(num, temp.getNum())){ + //已全量计算 翻转记录表中工单状态 + iotCarbonProductionRecordService.updateByMidAndPrCode(temp.getMId() ,temp.getPrCode()); + } + }); + //根据入参,判断是否走这个监听 + if(event.getOrderType().equals("2")){ + applicationEventPublisher.publishEvent(new ProductionResultEvent(this)); + } + + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionResultListener.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionResultListener.java new file mode 100644 index 0000000..4d2fbae --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/event/standardcal/listener/ProductionResultListener.java @@ -0,0 +1,339 @@ +package com.thing.carbontrack.event.standardcal.listener; + + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.bom.dto.BomCarbonReq; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.event.standardcal.ProductionResultEvent; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.dto.ProductionPage; +import com.thing.carbontrack.productionRecord.dto.QuantityDto; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +/** + * 全流程碳排计算 + */ +public class ProductionResultListener { + + + private final IotCarbonProductionResultService carbonProductionResultService; + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonBomService carbonBomService; + + private final IotCarbonProductionProcessVarietyService carbonProductionProcessVarietyService; + + private final IotCarbonUseConfigService carbonUseConfigService; + + private final IotCarbonRatioService carbonRatioService; + + private final String APPTYPE = "CK"; + + private final String CHANGK = "CK"; + + private final String BASE = "BASE"; + + @EventListener(ProductionResultEvent.class) + public void start(){ + //获取所有生产报工已经计算完毕的报工记录 + List reqs =iotCarbonProductionRecordService.getRecordReqListByStatus(); + reqs.forEach(this::calculate); + + } + + /** + * 计算产品批次碳足迹 + * @param production + */ + @Transactional + public synchronized void calculate(ProductionPage production){ + try { + QuantityDto quantityDto = iotCarbonProductionRecordService.getQuantityByMidAndPrCode(production.getMId(),production.getPrCode()); + production.setFinalNum(quantityDto.getFinalNum()); + production.setPNum(production.getPNum()); + //bom 原料计算 + calculateBom(production); + //产品生产计算 + calculateProduce(production,"2"); + //产品公摊计算 + calculateProduce(production,"3"); + + if(production.getBoundary().equals("2")){ + //产品运输计算 + calculateTransport(production); + //产品使用计算 + calculateUse(production); + //产品废弃处理计算 + calculateDispose(production); + } + //翻转为5 汇总成功 + iotCarbonProductionRecordService.updatePrStatus(5L,production.getMId(),production.getPrCode()); + } catch (Exception e) { + e.printStackTrace(); + //翻转为6 汇总异常 + iotCarbonProductionRecordService.updatePrStatus(6L,production.getMId(),production.getPrCode()); + } + + + } + + + /** + * 废弃处理,碳排计算 + * @param production + */ + private void calculateDispose(ProductionPage production) { + List disposeDetails = carbonUseConfigService.getDisposeDetailListByMIdAndPrCode(production.getMId(),null); + + disposeDetails.forEach(temp->{ + CarBonRatioResult result = carbonRatioService.getRatioByEvIdAndTime(temp.getEvId(),production.getEndTime()); + //先计算废弃物能源 + temp.setEvName(result.getName()); + Long salesNum = production.getFinalNum(); + temp.setSalesNum(salesNum); + //废弃能耗单耗 + BigDecimal evUsageAvg = temp.getEvUsage(); + //用量单耗 + BigDecimal evUsage = temp.getEvUsage().multiply(new BigDecimal(salesNum)); + //碳排总量 + BigDecimal evUsageCarbon = evUsage.multiply(result.getRatio()).setScale(4, RoundingMode.UP); + //用能总量 + BigDecimal evUsageCarbonAvg = evUsageAvg.multiply(result.getRatio()).setScale(4,RoundingMode.UP); + temp.setEvUsageAvg(evUsageAvg); + temp.setEvUsage(evUsage); + temp.setEvUsageCarbon(evUsageCarbon); + temp.setEvUsageCarbonAvg(evUsageCarbonAvg); + //初始化结果对象 + production.setEvId(temp.getEvId()); + production.setEvName(temp.getEvName()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"6", + evUsageCarbon,evUsage,evUsageAvg,evUsageCarbonAvg); + //计算废弃运输能源消耗情况 + //单件运输碳排 + BigDecimal transportCarBonAvg = temp.getTransportDistance().multiply(temp.getTransportRatio()).multiply(temp.getProductWight()) + .divide(new BigDecimal("1000"),4,RoundingMode.UP); + //总运输碳排 + BigDecimal transportCarBon = transportCarBonAvg.multiply(new BigDecimal(salesNum)).setScale(4,RoundingMode.UP); + temp.setTransportCarBon(transportCarBon); + temp.setTransportCarBonAvg(transportCarBonAvg); + entity.setF_usage(temp.getTransportDistance()); + entity.setF_usage_avg(temp.getTransportDistance()); + entity.setF_carbon(transportCarBon); + entity.setF_carbon_avg(transportCarBonAvg); + entity.setF_ev_id(temp.getTransportId()); + entity.setF_ev_name(temp.getTransportType()); + entity.setJson(JSONObject.toJSONString(List.of(temp))); + //do update 还是 insert 废弃处理来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"6", temp.getEvId(), entity.getGroup_id(),entity.getF_ev_id()); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + + }); + + + + + } + + + /** + * 产品使用,碳排计算 + * @param production + */ + private void calculateUse(ProductionPage production) { + List puDetails = carbonUseConfigService.getPuDetailListByMIdAndPrCode(production.getMId(),null); + + puDetails.forEach(temp->{ + //evId获取 能源碳排因子值 + CarBonRatioResult result = carbonRatioService.getRatioByEvIdAndTime(temp.getEvId(),production.getEndTime()); + temp.setEvName(result.getName()); + temp.setFinishTime(production.getEndTime()); + temp.setSalesNum(production.getFinalNum()); + //碳排单耗 + BigDecimal carbonAvg = result.getRatio().multiply(new BigDecimal(temp.getServiceLife())).multiply(temp.getConsumption()).setScale(4,RoundingMode.UP); + //用量单耗 + BigDecimal consumptionAvg = temp.getConsumption().multiply(new BigDecimal(temp.getServiceLife())).setScale(4,RoundingMode.UP); + //碳排总量 + BigDecimal usageCarbon = carbonAvg.multiply(new BigDecimal(temp.getSalesNum())).setScale(4,RoundingMode.UP); + //用能总量 + BigDecimal consumption = consumptionAvg.multiply(new BigDecimal(temp.getSalesNum())).setScale(4,RoundingMode.UP); + temp.setCarbonAvg(carbonAvg); + temp.setConsumptionAvg(consumptionAvg); + temp.setUsageCarbon(usageCarbon); + temp.setConsumption(consumption); + production.setEvId(temp.getEvId()); + production.setEvName(temp.getEvName()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"5", + usageCarbon,consumption,consumptionAvg,carbonAvg); + entity.setJson(JSONObject.toJSONString(List.of(temp))); + entity.setLife(temp.getServiceLife()); + //do update 还是 insert 产品使用来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"5", temp.getEvId(), null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + }); + } + + /** + * 产品运输碳排计算 + * @param production + */ + private void calculateTransport(ProductionPage production) { + List ptDetails = carbonUseConfigService.getListByMIdAndPrCode(production.getMId(),production.getPrCode()); + ptDetails.forEach(temp->{ + production.setEvId(temp.getTransportId()); + production.setEvName(temp.getTransportType()); + production.setGroupId(temp.getGroupId()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"4", + temp.getTransportCarBon(),temp.getTransportDistance(),temp.getTransportDistance(),temp.getTransportCarBonAvg()); + + entity.setJson(JSONObject.toJSONString(List.of(temp))); + + //do update 还是 insert 产品运输来说,根据产品id,工单编码,类型,能源品种id,groupId 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"4", temp.getTransportId(), temp.getGroupId(),null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + }); + } + + + /** + * 生产/碳排计算 + * @param production + */ + private void calculateProduce(ProductionPage production,String type) { + List pmProcessDetails = carbonProductionProcessVarietyService.getPIndirectDetailListByMidAndPrCodes(production.getMId(),List.of(production.getPrCode()),type); + //按能源品种进行分组 + Map> mapPm = pmProcessDetails.stream().collect(Collectors.groupingBy(PIndirectDetail::getEvId)); + for (Long evId : mapPm.keySet()) { + production.setEvId(evId); + List group = mapPm.get(evId); + production.setEvName(group.stream().findFirst().orElse(new PIndirectDetail()).getEvName()); + //分别计算每个能源品种的 总能耗,平均值 + BigDecimal totalEvUsage = group.stream() + .map(PIndirectDetail::getEvUsage) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalCarbon = group.stream() + .map(PIndirectDetail::getCarbon) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal avgEvUsage= totalEvUsage.divide(new BigDecimal(production.getFinalNum()),RoundingMode.UP).setScale(4,RoundingMode.UP); + BigDecimal avgCarbon= totalCarbon.divide(new BigDecimal(production.getFinalNum()),RoundingMode.UP).setScale(4,RoundingMode.UP); + //初始化当前能源品种的碳排结果 + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,type, + totalCarbon,totalEvUsage,avgEvUsage,avgCarbon); + entity.setJson(JSONObject.toJSONString(group)); + //do update 还是 insert 生产间接来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode(),type,evId,null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + } + } + + + /** + * bom 碳排计算 + * @param production + */ + private void calculateBom(ProductionPage production) { + //单件产品 运输,物料,碳排放值 + BomCarbonReq carbonReq = carbonBomService.queryBomCarbonByMid(production.getMId()); + IotCarbonProductionResultEntity entity = new IotCarbonProductionResultEntity(); + //获取 BOM详情json + List entityList = carbonBomService.listByProductId(production.getMId()); + if(APPTYPE.equals(CHANGK)){ + //常开来说,除以 数量,等于平均单件用量 + BigDecimal uintCarbon = carbonReq.getMuFinalUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP); + BigDecimal uintTransportTotalCarbon = carbonReq.getTransportUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP); + //初始化运输结果 + entity = IotCarbonProductionResultEntity.initDto(production,"1", + carbonReq.getMuFinalUse(),carbonReq.getTransportUse(),uintTransportTotalCarbon,uintCarbon); + entityList.forEach(temp->{ + temp.setAcquireCarbon(temp.getAcquireCarbon().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + temp.setDosage(temp.getDosage().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + temp.setTransportUse(temp.getTransportUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + }); + }else if (APPTYPE.equals(BASE)){ + BigDecimal totalCarbon = carbonReq.getMuFinalUse().multiply(new BigDecimal(production.getFinalNum())).setScale(4, RoundingMode.UP); + BigDecimal transportTotalCarbon = carbonReq.getTransportUse().multiply(new BigDecimal(production.getFinalNum())).setScale(4, RoundingMode.UP); + //初始化运输结果 + entity = IotCarbonProductionResultEntity.initDto(production,"1", + totalCarbon,transportTotalCarbon,carbonReq.getTransportUse(),carbonReq.getMuFinalUse()); + } + entity.setJson(JSONObject.toJSONString(entityList)); + //do update 还是 insert 产品bom来说,根据 产品id,工单编码,类型,来判断是否已存在 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode(),"1",null,null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + } + + + /** + * 根据入参,查询汇算结果 + * @param mId + * @param prCode + * @param type + * @param evId + * @return + */ + private IotCarbonProductionResultEntity getResultEntityByParam(Long mId,String prCode,String type,Long evId,Long groupId,Long fevid){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId,ObjectUtils.isNotEmpty(mId)); + wrapper.eq("pr_code",prCode,ObjectUtils.isNotEmpty(prCode)); + wrapper.eq("carbon_type",type,ObjectUtils.isNotEmpty(type)); + wrapper.eq("ev_id",evId,ObjectUtils.isNotEmpty(evId)); + wrapper.eq("ev_id",evId,ObjectUtils.isNotEmpty(evId)); + wrapper.eq("f_ev_id",fevid,ObjectUtils.isNotEmpty(fevid)); + return carbonProductionResultService.getOne(wrapper); + } + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/controller/IotCarbonOutboundConfigController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/controller/IotCarbonOutboundConfigController.java new file mode 100644 index 0000000..fe61127 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/controller/IotCarbonOutboundConfigController.java @@ -0,0 +1,160 @@ +package com.thing.carbontrack.outbound.controller; + +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.outbound.dto.IotCarbonOutboundConfigDTO; +import com.thing.carbontrack.outbound.service.IotCarbonOutboundConfigService; +import com.thing.carbontrack.productionRecord.dto.ProductionOverviewReq; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.carbontrack.useConfig.dto.IotCarbonUseConfigDTO; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** +* 出库记录配置 +* +* @author xc +* @since 3.0 2024-06-25 +*/ +@RestController +@RequestMapping("v2/outbound/iotcarbonoutboundconfig") +@Tag(name="出库记录配置") +@RequiredArgsConstructor +public class IotCarbonOutboundConfigController { + + private final IotCarbonOutboundConfigService iotCarbonOutboundConfigService; + + private final IotCarbonUseConfigService carbonUseConfigService; + + private final CarbonEnergyVarietyService energyVarietyService; + + private final IotCarbonRatioService carbonRatioService; + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "m_id", description = "产品id"), + @Parameter(name = "prCode", description = "工单编码"), + @Parameter(name = "customerName", description = "客户名称"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonOutboundConfigService.getPageData(params, IotCarbonOutboundConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonOutboundConfigDTO data = iotCarbonOutboundConfigService.getByIdAs(id, IotCarbonOutboundConfigDTO.class); + Map params = new HashMap<>(); + params.put("m_id",data.getmId()); + params.put("customerName",data.getCustomerName()); + params.put("customerAddress",data.getCustomerAddress()); + List configDTOList =carbonUseConfigService.customerInfoList(params); + configDTOList.forEach(temp->{ + try { + IotCarbonRatioEntity ratioDTO = carbonRatioService.getById(temp.getEvId()); + CarbonEnergyVarietyEntity varietyEntity = energyVarietyService.getById(ratioDTO.getEvId()); + temp.setTransportName(varietyEntity.getName()); + } catch (Exception e) { + temp.setTransportName("未知"); + } + }); + data.setConfigDTOList(configDTOList); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonOutboundConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonOutboundConfigService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonOutboundConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonOutboundConfigService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonOutboundConfigService.batchDelete(ids); + return new Result<>(); + } + + @GetMapping("customerNameList/{m_id}") + @Operation(summary="获取产品,运输客户名称,传入产品id") + public Result> customerNameList(@PathVariable("m_id") Long m_id){ + //效验数据 + Set stringList =carbonUseConfigService.customerNameList(m_id); + return new Result>().ok(stringList); + } + + @GetMapping("customerAddressList") + @Operation(summary="获取产品,相应的运输客户地址集合,传入产品id,以及客户名称") + @Parameters({ + @Parameter(name = "m_id", description = "产品id"), + @Parameter(name = "customerName", description = "上个接口返回的选中的客户名称") + }) + public Result> customerAddressList(@Parameter(hidden = true) @RequestParam Map params){ + //效验数据 + Set stringList =carbonUseConfigService.customerAddressList(params); + return new Result>().ok(stringList); + } + + @GetMapping("customerInfoList") + @Operation(summary="获取产品,相应的运输客户地址集合,传入产品id,以及客户名称") + @Parameters({ + @Parameter(name = "m_id", description = "产品id"), + @Parameter(name = "customerName", description = "上上个接口返回的选中的客户名称"), + @Parameter(name = "customerAddress", description = "上个接口返回的选中的客户地址"), + }) + public Result> customerInfoList(@Parameter(hidden = true) @RequestParam Map params){ + //效验数据 + List configDTOList =carbonUseConfigService.customerInfoList(params); + return new Result>().ok(configDTOList); + } + + + @GetMapping("productionListByMid/{m_id}") + @Operation(summary="获取工单列表信息,传入产品id") + public Result> productionListByMid(@PathVariable("m_id") Long m_id){ + //效验数据 + List dtoList =carbonUseConfigService.productionListByMid(m_id); + return new Result>().ok(dtoList); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/dto/IotCarbonOutboundConfigDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/dto/IotCarbonOutboundConfigDTO.java new file mode 100644 index 0000000..3065676 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/dto/IotCarbonOutboundConfigDTO.java @@ -0,0 +1,125 @@ +package com.thing.carbontrack.outbound.dto; + +import com.thing.carbontrack.useConfig.dto.IotCarbonUseConfigDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 出库记录配置 +* +* @author xc +* @since 3.0 2024-06-25 +*/ +@Data +@Schema(description = "出库记录配置") +public class IotCarbonOutboundConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "产品id") + private Long mId; + @Schema(description = "产品编码") + private String mCode; + @Schema(description = "产品名称") + private String mName; + @Schema(description = "客户名称") + private String customerName; + @Schema(description = "客户地址") + private String customerAddress; + @Schema(description = "工单编码") + private String prCode; + @Schema(description = "出库数量") + private Long num; + @Schema(description = "出库时间") + private Date outboundTime; + + private List configDTOList; + + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Long getmId() { + return mId; + } + + public void setmId(Long mId) { + this.mId = mId; + } + + public String getmCode() { + return mCode; + } + + public void setmCode(String mCode) { + this.mCode = mCode; + } + + public String getmName() { + return mName; + } + + public void setmName(String mName) { + this.mName = mName; + } + + public String getCustomerName() { + return customerName; + } + + public void setCustomerName(String customerName) { + this.customerName = customerName; + } + + public String getCustomerAddress() { + return customerAddress; + } + + public void setCustomerAddress(String customerAddress) { + this.customerAddress = customerAddress; + } + + public String getPrCode() { + return prCode; + } + + public void setPrCode(String prCode) { + this.prCode = prCode; + } + + public Long getNum() { + return num; + } + + public void setNum(Long num) { + this.num = num; + } + + public Date getOutboundTime() { + return outboundTime; + } + + public void setOutboundTime(Date outboundTime) { + this.outboundTime = outboundTime; + } + + public List getConfigDTOList() { + return configDTOList; + } + + public void setConfigDTOList(List configDTOList) { + this.configDTOList = configDTOList; + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/entity/IotCarbonOutboundConfigEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/entity/IotCarbonOutboundConfigEntity.java new file mode 100644 index 0000000..297ce63 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/entity/IotCarbonOutboundConfigEntity.java @@ -0,0 +1,79 @@ +package com.thing.carbontrack.outbound.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderDto; +import com.thing.carbontrack.useConfig.entity.IotCarbonUseConfigEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 出库记录配置 + * + * @author xc + * @since 3.0 2024-06-25 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_outbound_config") +public class IotCarbonOutboundConfigEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 产品id + */ + private Long mId; + /** + * 产品编码 + */ + private String mCode; + /** + * 产品名称 + */ + private String mName; + /** + * 客户名称 + */ + private String customerName; + /** + * 客户地址 + */ + private String customerAddress; + /** + * 工单编码 + */ + private String prCode; + /** + * 出库数量 + */ + private Long num; + /** + * 出库时间 + */ + private Date outboundTime; + + public static IotCarbonOutboundConfigEntity init(IotCarbonUseConfigEntity carbonUseConfigEntity, CkOrderDto ckOrderDto) { + IotCarbonOutboundConfigEntity entity = new IotCarbonOutboundConfigEntity(); + entity.setNum(ckOrderDto.getQty().longValue()); + entity.setCustomerName(carbonUseConfigEntity.getCustomerName()); + entity.setCustomerAddress(carbonUseConfigEntity.getCustomerAddress()); + entity.setMCode(carbonUseConfigEntity.getM_code()); + entity.setMName(carbonUseConfigEntity.getM_name()); + entity.setMId(carbonUseConfigEntity.getM_id()); + entity.setPrCode(ckOrderDto.getOrderNo()); + entity.setOutboundTime(new Date()); + return entity; + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/mapper/IotCarbonOutboundConfigMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/mapper/IotCarbonOutboundConfigMapper.java new file mode 100644 index 0000000..e05fd4c --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/mapper/IotCarbonOutboundConfigMapper.java @@ -0,0 +1,17 @@ +package com.thing.carbontrack.outbound.mapper; + +import com.thing.carbontrack.outbound.entity.IotCarbonOutboundConfigEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 出库记录配置 +* +* @author xc +* @since 3.0 2024-06-25 +*/ +@Mapper +public interface IotCarbonOutboundConfigMapper extends PowerBaseMapper { + + Long getOutboundNumByMidAndPrCode(Long mId, String prCode); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/IotCarbonOutboundConfigService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/IotCarbonOutboundConfigService.java new file mode 100644 index 0000000..fcb97df --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/IotCarbonOutboundConfigService.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.outbound.service; + +import com.thing.carbontrack.outbound.entity.IotCarbonOutboundConfigEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 出库记录配置 + * + * @author xc + * @since 3.0 2024-06-25 + */ +public interface IotCarbonOutboundConfigService extends IBaseService { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/impl/IotCarbonOutboundConfigServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/impl/IotCarbonOutboundConfigServiceImpl.java new file mode 100644 index 0000000..39e7742 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/outbound/service/impl/IotCarbonOutboundConfigServiceImpl.java @@ -0,0 +1,34 @@ +package com.thing.carbontrack.outbound.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.outbound.entity.IotCarbonOutboundConfigEntity; +import com.thing.carbontrack.outbound.mapper.IotCarbonOutboundConfigMapper; +import com.thing.carbontrack.outbound.service.IotCarbonOutboundConfigService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 出库记录配置 + * + * @author xc + * @since 3.0 2024-06-25 + */ +@Service +public class IotCarbonOutboundConfigServiceImpl extends BaseServiceImpl implements IotCarbonOutboundConfigService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + Long mId = MapUtils.getLong(params,"m_id"); + String prCode = MapUtils.getString(params,"prCode"); + String customerName = MapUtils.getString(params,"customerName"); + wrapper.eq("m_id",mId, ObjectUtils.isNotEmpty(mId)); + wrapper.eq("pr_code",prCode,ObjectUtils.isNotEmpty(prCode)); + wrapper.eq("customer_name",customerName,ObjectUtils.isNotEmpty(customerName)); + return wrapper; + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/controller/IotCarbonProductionProcessVarietyController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/controller/IotCarbonProductionProcessVarietyController.java new file mode 100644 index 0000000..87eb653 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/controller/IotCarbonProductionProcessVarietyController.java @@ -0,0 +1,97 @@ +package com.thing.carbontrack.processVariety.controller; + +import com.thing.carbontrack.processVariety.dto.IotCarbonProductionProcessVarietyDTO; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 工单工序用能结果 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/processVariety/iotcarbonproductionprocessvariety") +@Tag(name="AAA工单工序用能结果") +@RequiredArgsConstructor +public class IotCarbonProductionProcessVarietyController { + + private final IotCarbonProductionProcessVarietyService iotCarbonProductionProcessVarietyService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionProcessVarietyService.getPageData(params, IotCarbonProductionProcessVarietyDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonProductionProcessVarietyDTO data = iotCarbonProductionProcessVarietyService.getByIdAs(id, IotCarbonProductionProcessVarietyDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionProcessVarietyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonProductionProcessVarietyService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionProcessVarietyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProductionProcessVarietyService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionProcessVarietyService.batchDelete(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotCarbonProductionProcessVarietyService.listAs(params, IotCarbonProductionProcessVarietyDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "工单工序用能结果", list, IotCarbonProductionProcessVarietyExcel.class); + *} + */ + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/dto/IotCarbonProductionProcessVarietyDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/dto/IotCarbonProductionProcessVarietyDTO.java new file mode 100644 index 0000000..93b6037 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/dto/IotCarbonProductionProcessVarietyDTO.java @@ -0,0 +1,44 @@ +package com.thing.carbontrack.processVariety.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 工单工序用能结果 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "工单工序用能结果") +public class IotCarbonProductionProcessVarietyDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "生产记录id") + private Long prId; + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "产品id") + private Long mId; + @Schema(description = "工序id") + private Long psId; + @Schema(description = "能源介质id") + private Long evId; + @Schema(description = "总用量") + private BigDecimal usage; + @Schema(description = "总碳排") + private BigDecimal carbon; + @Schema(description = "总能耗") + private BigDecimal tce; + @Schema(description = "类型1 生产 2公摊/间接") + private String type; + @Schema(description = "产成品数量") + private String final_num; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/entity/IotCarbonProductionProcessVarietyEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/entity/IotCarbonProductionProcessVarietyEntity.java new file mode 100644 index 0000000..cec051a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/entity/IotCarbonProductionProcessVarietyEntity.java @@ -0,0 +1,74 @@ +package com.thing.carbontrack.processVariety.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 工单工序用能结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_process_variety") +public class IotCarbonProductionProcessVarietyEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + /** + * 生产记录id + */ + private Long prId; + /** + * 生产记录编码 + */ + private String prCode; + /** + * 产品id + */ + private Long mId; + /** + * 工序id + */ + private Long psId; + /** + * 能源介质id + */ + private Long evId; + /** + * 总用量 + */ + private BigDecimal usage; + /** + * 总碳排 + */ + private BigDecimal carbon; + /** + * 总能耗 + */ + private BigDecimal tce; + /** + * 类型1 生产 2公摊/间接 + */ + private String type; + /** + * 产成品数量 + */ + private Long final_num; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/mapper/IotCarbonProductionProcessVarietyMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/mapper/IotCarbonProductionProcessVarietyMapper.java new file mode 100644 index 0000000..f0e85d4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/mapper/IotCarbonProductionProcessVarietyMapper.java @@ -0,0 +1,32 @@ +package com.thing.carbontrack.processVariety.mapper; + +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.productionResult.dto.PIndirectDetail; +import com.thing.carbontrack.productionResult.dto.PmProcessDetail; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** +* 工单工序用能结果 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonProductionProcessVarietyMapper extends PowerBaseMapper { + + List getPmProcessDetailListByMidAndPrCodes( + @Param("mId") Long mId, + @Param("prCodes") Collection prCodes, + @Param("type") String type); + + List getPIndirectDetailListByMidAndPrCodes( + @Param("mId") Long mId, + @Param("prCodes") Collection prCodes, + @Param("type") String type); + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/IotCarbonProductionProcessVarietyService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/IotCarbonProductionProcessVarietyService.java new file mode 100644 index 0000000..78aaffd --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/IotCarbonProductionProcessVarietyService.java @@ -0,0 +1,22 @@ +package com.thing.carbontrack.processVariety.service; + +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.productionResult.dto.PIndirectDetail; +import com.thing.carbontrack.productionResult.dto.PmProcessDetail; +import com.thing.common.orm.service.IBaseService; + +import java.util.Collection; +import java.util.List; + +/** + * 工单工序用能结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonProductionProcessVarietyService extends IBaseService { + + List getPmProcessDetailListByMidAndPrCodes(Long mId, Collection prCodes, String type); + + List getPIndirectDetailListByMidAndPrCodes(Long mId,Collection prCodes, String type); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/impl/IotCarbonProductionProcessVarietyServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/impl/IotCarbonProductionProcessVarietyServiceImpl.java new file mode 100644 index 0000000..73eebd9 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/processVariety/service/impl/IotCarbonProductionProcessVarietyServiceImpl.java @@ -0,0 +1,41 @@ +package com.thing.carbontrack.processVariety.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.processVariety.mapper.IotCarbonProductionProcessVarietyMapper; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionResult.dto.PIndirectDetail; +import com.thing.carbontrack.productionResult.dto.PmProcessDetail; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 工单工序用能结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonProductionProcessVarietyServiceImpl extends BaseServiceImpl implements IotCarbonProductionProcessVarietyService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public List getPmProcessDetailListByMidAndPrCodes(Long mId, Collection prCodes, String type) { + return this.mapper.getPmProcessDetailListByMidAndPrCodes(mId,prCodes,type); + } + + @Override + public List getPIndirectDetailListByMidAndPrCodes(Long mId, Collection prCode, String type) { + return this.mapper.getPIndirectDetailListByMidAndPrCodes(mId,prCode,type); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/controller/UsagePlanController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/controller/UsagePlanController.java new file mode 100644 index 0000000..fd252d4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/controller/UsagePlanController.java @@ -0,0 +1,52 @@ +package com.thing.carbontrack.produce.controller; + +import com.thing.carbontrack.produce.dto.UsagePlanFormParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportReq; +import com.thing.carbontrack.produce.service.UsagePlanService; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 产品品种信息 + * + * @author xc + * @since 3.0 2024-03-13 + */ +@RestController +@RequestMapping("v2/usage/plan") +@Tag(name="AAA用能计划报表") +@RequiredArgsConstructor +public class UsagePlanController { + + private final UsagePlanService usagePlanService; + + @PostMapping("generate") + @Operation(summary="根据条件,生成计划数据") + public Result generateUsagePlan(@RequestBody UsagePlanFormParam param){ + String result = usagePlanService.generateUsagePlan(param); + return new Result().ok(result); + } + + + @PostMapping("usagePlanReportPage") + @Operation(summary="按条件查询计划数据") + public Result> usagePlanReportPage(@RequestBody UsagePlanReportParam param){ + //UsagePlanReportParam reportParam = UsagePlanReportParam.conver(param); + PageData result = usagePlanService.usagePlanReportPage(param); + return new Result>().ok(result); + } + + + + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanFormParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanFormParam.java new file mode 100644 index 0000000..dc2c487 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanFormParam.java @@ -0,0 +1,77 @@ +package com.thing.carbontrack.produce.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.YearMonth; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +@Data +@Schema(description = "用量标准生成表单入参") +public class UsagePlanFormParam { + + private static final String NORM = "_norm"; + + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "产品信息+暂定为产品名称_型号") + private String production; + @Schema(description = "能源品种编码") + private String energyCode; + @Schema(description = "时间类型,hh=天,dd=月,mm=年") + private String dateType; + @Schema(description = "第一个时间入参,天,年,或者月的集合,天YYYY-MM-dd 月YYYY-MM 年YYYY") + private List parentDateParam; + @Schema(description = "第二个时间入参,小时,天,月的集合,小时hh 天dd 月mm,只用传两位") + private List subsetDateParam; + @Schema(description = "用能标准数据") + private String norm; + + + /** + * 初始化日期列表集合 + * @return + */ + public static List generateTimeList(String dateType,List parentDate, List subsetDate){ + List timeList = new ArrayList<>(); + parentDate.forEach(t->{ + subsetDate.forEach(i->{ + timeList.add(generateLocalDateTime(dateType,t,i)); + }); + }); + return timeList; + } + + /** + * 根据时间类型,时间字符串,生成时间数据 + * @param dateType + * @param t + * @param i + * @return + */ + private static LocalDateTime generateLocalDateTime(String dateType, String t, String i) { + return switch (dateType) { + case "hh" -> LocalDateTime.parse(t + " " + i, DateTimeFormatter.ofPattern("yyyy-MM-dd HH")); + case "dd" -> LocalDate.parse(t + "-" + i, DateTimeFormatter.ofPattern("yyyy-MM-dd")).atStartOfDay(); + case "mm" -> YearMonth.parse(t + "-" + i, DateTimeFormatter.ofPattern("yyyy-MM")).atDay(1).atStartOfDay(); + default -> null; + }; + } + + /** + * 根据入参,生成属性编码 + * @param energyCode + * @param dateType + * @return + */ + public static String generateAttrCode(String energyCode,String dateType){ + return energyCode+NORM+dateType; + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportParam.java new file mode 100644 index 0000000..8dc1f04 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportParam.java @@ -0,0 +1,96 @@ +package com.thing.carbontrack.produce.dto; + + +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.apache.commons.collections4.MapUtils; + +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +@Data +@Schema(description = "用量标准报表,请求入参") +public class UsagePlanReportParam { + private static final String NORM = "_norm"; + + private static final String ATTR_TYPE = "1"; + + private static final String ATTR_NORE_TYPE = "2"; + + + @Schema(description = "物编码集合") + private List thingCodes; + + @Schema(description = "能源品种编码集合") + private List energyCodes; + + @Schema(description = "时间") + private String dateStr; + + @Schema(description = "时间类型 日 hh 月dd,年mm") + private String dateType; + @Schema(description = "页码") + private Integer page; + @Schema(description = "分页大小") + private Integer limit; + @Schema(description = "产品图片") + private String url; + + /** + * 根据类型生成查询参数 + * @param energyCode 能源属性编码 + * @param dateType 时间类型 + * @param type 类型 1基础属性 ATTR_TYPE,2标准属性 ATTR_NORE_TYPE + * @return + */ + public static String generateAttrCodeByType(String energyCode,String dateType,String type){ + return type.equals(ATTR_TYPE) ? energyCode + dateType : energyCode + NORM + dateType; + } + + public static Long getStartTime(String str,String type){ + LocalDateTime dateTime = getLocalDateTime(type,str); + return switch (type) { + case "hh" -> DateTimeUtils.getDayStart(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + case "dd" -> DateTimeUtils.getMonthStart(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + case "mm" -> DateTimeUtils.getYearStart(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + default -> null; + }; + } + + + public static Long getEndTime(String str,String type){ + LocalDateTime dateTime = getLocalDateTime(type,str); + return switch (type) { + case "hh" -> DateTimeUtils.getDayEnd(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + case "dd" -> DateTimeUtils.getMonthEnd(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + case "mm" -> DateTimeUtils.getYearEnd(dateTime).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + default -> null; + }; + } + + + private static LocalDateTime getLocalDateTime(String type,String str){ + return switch (type) { + case "hh" -> LocalDateTime.parse(str + "T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME); + case "dd" -> LocalDateTime.parse(str + "-01T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME); + case "mm" -> LocalDateTime.parse(str + "-01-01T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME); + default -> null; + }; + } + + public static UsagePlanReportParam conver(Map param) { + UsagePlanReportParam reportParam = new UsagePlanReportParam(); + reportParam.setThingCodes(Arrays.asList(MapUtils.getString(param,"thingCodes").split(","))); + reportParam.setEnergyCodes(Arrays.asList(MapUtils.getString(param,"energyCodes").split(","))); + reportParam.setDateStr((String) param.get("dateStr")); + reportParam.setDateType((String) param.get("dateType")); + reportParam.setPage(MapUtils.getInteger(param,"page")); + reportParam.setLimit(MapUtils.getInteger(param,"limit")); + return reportParam; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportReq.java new file mode 100644 index 0000000..c408341 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/dto/UsagePlanReportReq.java @@ -0,0 +1,32 @@ +package com.thing.carbontrack.produce.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "用量标准报表,响应结果") +public class UsagePlanReportReq { + @Schema(description = "物名称") + private String thingName; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "产品信息+暂定为产品名称_型号") + private String production; + @Schema(description = "能源品种编码") + private String energyCode; + @Schema(description = "能源品种名称") + private String energyName; + @Schema(description = "单位") + private String unit; + @Schema(description = "数据时间") + private Long ts; + @Schema(description = "标准值") + private BigDecimal norm = BigDecimal.ZERO; + @Schema(description = "实际值") + private BigDecimal val; + @Schema(description = "差值") + private BigDecimal difference; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/UsagePlanService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/UsagePlanService.java new file mode 100644 index 0000000..6e4f2f6 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/UsagePlanService.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.produce.service; + +import com.thing.carbontrack.produce.dto.UsagePlanFormParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportReq; +import com.thing.common.core.web.response.PageData; + +public interface UsagePlanService { + + + String generateUsagePlan(UsagePlanFormParam param); + + PageData usagePlanReportPage(UsagePlanReportParam param); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/impl/UsagePlanServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/impl/UsagePlanServiceImpl.java new file mode 100644 index 0000000..f395985 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/produce/service/impl/UsagePlanServiceImpl.java @@ -0,0 +1,150 @@ +package com.thing.carbontrack.produce.service.impl; + + +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.produce.dto.UsagePlanFormParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportParam; +import com.thing.carbontrack.produce.dto.UsagePlanReportReq; +import com.thing.carbontrack.produce.service.UsagePlanService; +import com.thing.common.core.utils.PageUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +public class UsagePlanServiceImpl implements UsagePlanService { + + private static final String PRODUCTION_NAME = "production"; + + private static final String TABLE_SUFFIX = "_external"; + + @Resource + private TsKvService tsKvService; + @Resource + private CarbonEnergyVarietyService carbonEnergyVarietyService; + @Resource + private ThingManageContextService thingManageContextService; + + + @Override + public String generateUsagePlan(UsagePlanFormParam param) { + List tsKvDTOS = new ArrayList<>(); + List localTimes = UsagePlanFormParam.generateTimeList(param.getDateType(),param.getParentDateParam(),param.getSubsetDateParam()); + localTimes.forEach(time->{ + long ts = time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + tsKvDTOS.add(new TsKvDTO(param.getThingCode(), UsagePlanFormParam.generateAttrCode(param.getEnergyCode(),param.getDateType()) + ,ts,param.getNorm())); + tsKvDTOS.add(new TsKvDTO(param.getThingCode(),PRODUCTION_NAME,ts,param.getProduction())); + }); + try { + tsKvService.saveDTOTsKvByTableSuffix(tsKvDTOS,TABLE_SUFFIX); + } catch (Exception e) { + return "数据插入异常"; + } + return "插入成功"; + } + + @Override + public PageData usagePlanReportPage(UsagePlanReportParam param) { + List usagePlanReportReqs = new ArrayList<>(); + Long startTime = UsagePlanReportParam.getStartTime(param.getDateStr(), param.getDateType()); + Long endTime = UsagePlanReportParam.getEndTime(param.getDateStr(), param.getDateType()); + // 查询所有能源品种的信息 + Map energyVarietyMap = param.getEnergyCodes().stream() + .map(temp -> carbonEnergyVarietyService.selectByCode(temp)) + .filter(Objects::nonNull) + .collect(Collectors.toMap(CarbonEnergyVarietyEntity::getCode, Function.identity())); + + param.getEnergyCodes().forEach(temp -> { + CarbonEnergyVarietyEntity entity = energyVarietyMap.get(temp); + String attrName = entity != null ? entity.getName() : ""; + // 查询能源品种的单位 + String unit; + Optional optional = thingManageContextService.findDictByCode(temp); + if (optional.isPresent()) { + IotThingDictDTO dict = optional.get(); + unit = dict.getUnit(); + if (StringUtils.isEmpty(attrName)) { + attrName = dict.getName(); + } + } else { + unit = ""; + } + // 查询原始数据、标准数据、产品信息数据 + List tskv = tsKvService.findTsKvByCodesAndAttrsWithTableSuffix(param.getThingCodes(), + List.of(UsagePlanReportParam.generateAttrCodeByType(temp, param.getDateType(), "1")), + startTime, endTime, Boolean.FALSE, ""); + List normTskv = tsKvService.findTsKvByCodesAndAttrsWithTableSuffix(param.getThingCodes(), + List.of(UsagePlanReportParam.generateAttrCodeByType(temp, param.getDateType(), "2")), + startTime, endTime, Boolean.FALSE, TABLE_SUFFIX); + List productionTskv = tsKvService.findTsKvByCodesAndAttrsWithTableSuffix(param.getThingCodes(), + List.of(PRODUCTION_NAME), startTime, endTime, Boolean.FALSE, TABLE_SUFFIX); + + // 按物分组 + Map> tskvMap = tskv.stream().collect(Collectors.groupingBy(TsKvDTO::getThingCode)); + Map> normTskvMap = normTskv.stream().collect(Collectors.groupingBy(TsKvDTO::getThingCode)); + Map> productionTskvMap = productionTskv.stream().collect(Collectors.groupingBy(TsKvDTO::getThingCode)); + + String finalAttrName = attrName; + tskvMap.forEach((key, value) -> { + //物名称 + String thingName = null; + try { + Optional optionalIotThingViewDTO = thingManageContextService.findEntityByCode(key, UserContext.getRealTenantCode(), true); + IotThingEntityDTO dto = optionalIotThingViewDTO.orElse(new IotThingEntityDTO()); + thingName = dto.getName(); + } catch (Exception e) { + thingName= key; + } + List normList = normTskvMap.getOrDefault(key, Collections.emptyList()); + List productionList = productionTskvMap.getOrDefault(key, Collections.emptyList()); + + Map normListMap = normList.stream().collect(Collectors.toMap(TsKvDTO::getTs, Function.identity())); + Map productionTsMap = productionList.stream().collect(Collectors.toMap(TsKvDTO::getTs, Function.identity())); + + String finalThingName = thingName; + value.forEach(dto -> { + Long ts = dto.getTs(); + UsagePlanReportReq req = new UsagePlanReportReq(); + req.setThingCode(key); + req.setThingName(finalThingName); + req.setVal(StringUtils.isEmpty(dto.getVal()) ? BigDecimal.ZERO : new BigDecimal(dto.getVal())); + req.setTs(ts); + req.setUnit(unit); + req.setEnergyCode(temp); + req.setEnergyName(finalAttrName); + + TsKvDTO norm = normListMap.getOrDefault(ts, new TsKvDTO()); + req.setNorm(StringUtils.isEmpty(norm.getVal()) ? BigDecimal.ZERO : new BigDecimal(norm.getVal())); + + TsKvDTO production = productionTsMap.getOrDefault(ts, new TsKvDTO()); + req.setProduction(production.getVal()); + + req.setDifference(req.getVal().subtract(req.getNorm())); + usagePlanReportReqs.add(req); + }); + }); + }); + usagePlanReportReqs.sort( + Comparator.comparing(UsagePlanReportReq::getThingCode) + .thenComparing(UsagePlanReportReq::getTs, Comparator.reverseOrder()) + ); + List pageList = PageUtils.startPage(usagePlanReportReqs, param.getPage(), param.getLimit()); + return new PageData<>(pageList, usagePlanReportReqs.size()); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/controller/IotCarbonProductionVarietyController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/controller/IotCarbonProductionVarietyController.java new file mode 100644 index 0000000..601e05f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/controller/IotCarbonProductionVarietyController.java @@ -0,0 +1,158 @@ +package com.thing.carbontrack.production.controller; + +import com.thing.carbontrack.custom.service.IotCarbonProductionCustomService; +import com.thing.carbontrack.production.dto.IotCarbonProductionVarietyDTO; +import com.thing.carbontrack.production.dto.NormPageParam; +import com.thing.carbontrack.production.dto.ProductionNormInfo; +import com.thing.carbontrack.production.dto.UsageNormParam; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.utils.IdGenerator; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 产品信息表 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/ProductionVariety/iotcarbonproductionvariety") +@Tag(name="AAA产品信息表") +@RequiredArgsConstructor +public class IotCarbonProductionVarietyController { + + private final IotCarbonProductionVarietyService iotCarbonProductionVarietyService; + + private final IotCarbonProductionCustomService carbonProductionCustomService; + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "key", description = "产品名称or编码,模糊查询参数") , + @Parameter(name = "industry", description = "产品所属行业"), + @Parameter(name = "industry_sub", description = "产品所属行业子类型"), + @Parameter(name = "type", description = "产品种类 1成品 2半成品 "), + @Parameter(name = "isViewStep", description = "是否查看工序,1查看,2不查看") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionVarietyService.getPageDataIotCarbonProductionVarietyDTO(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "key", description = "产品名称or编码,模糊查询参数") , + @Parameter(name = "industry", description = "产品所属行业"), + @Parameter(name = "industry_sub", description = "产品所属行业子类型"), + @Parameter(name = "type", description = "产品种类 1成品 2半成品 ") + }) + public Result> list(@RequestParam Map params){ + List result = iotCarbonProductionVarietyService.listAsIotCarbonProductionVarietyDTO(params); + return new Result>().ok(result); + } + + + + @GetMapping("model/{id}") + @Operation(summary="根据行业名称,获取行业子类型列表") + public Result model(@PathVariable("id") Long id){ + return iotCarbonProductionVarietyService.model(id); + } + + + + + @GetMapping("industrySubListByIndustryName") + @Operation(summary="根据行业名称,获取行业子类型列表") + public Result> industrySubListByIndustryName(@RequestParam("industry") String industry){ + List data = iotCarbonProductionVarietyService.industrySubListByIndustryName(industry); + return new Result>().ok(data); + } + + @GetMapping("fUnitList") + @Operation(summary="根据行业名称,获取行业子类型列表") + public Result> fUnitList(){ + List data = iotCarbonProductionVarietyService.fUnitList(); + return new Result>().ok(data); + } + + @GetMapping("history/records") + @Operation(summary="各字段历史记录") + @Parameters({ + @Parameter(name = "field", description = "industry(行业类型), industry_sub(产品分类); f_unit(单位)", required = true) , + }) + public Result> targetFieldHistoryRecords(@Parameter(hidden = true) @RequestParam Map params){ + String field = MapUtils.getString(params, "field"); + List list = iotCarbonProductionVarietyService.getTargetFieldHistoryRecords(field); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonProductionVarietyDTO data = iotCarbonProductionVarietyService.getByIdAs(id, IotCarbonProductionVarietyDTO.class); + data.setCustomDTOS(carbonProductionCustomService.getInfoByProductionId(id)); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionVarietyDTO dto){ + //效验数据 + dto.setId(IdGenerator.nextId()); + return iotCarbonProductionVarietyService.saveIotCarbonProductionVarietyDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionVarietyDTO dto){ + return iotCarbonProductionVarietyService.updateIotCarbonProductionVarietyDTO(dto); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionVarietyService.batchDeleteIotCarbonProductionVarietyEntity(ids); + return new Result<>(); + } + + @PostMapping("generateNorm") + @Operation(summary="根据条件,生成计划数据") + public Result generateNorm(@RequestBody UsageNormParam param){ + String result = iotCarbonProductionVarietyService.generateNorm(param); + return new Result().ok(result); + } + + + @PostMapping("normPage") + @Operation(summary="根据产品编码,分页查询产品计划单耗") + public Result> normPage(@RequestBody NormPageParam param){ + PageData result = iotCarbonProductionVarietyService.normPage(param); + return new Result>().ok(result); + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/IotCarbonProductionVarietyDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/IotCarbonProductionVarietyDTO.java new file mode 100644 index 0000000..12a553d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/IotCarbonProductionVarietyDTO.java @@ -0,0 +1,313 @@ +package com.thing.carbontrack.production.dto; + +import com.thing.carbontrack.custom.dto.IotCarbonProductionCustomDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** +* 产品信息表 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Schema(description = "产品信息表") +public class IotCarbonProductionVarietyDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品编码") + private String code; + @Schema(description = "产品名称") + private String name; + @Schema(description = "产品规格型号") + private String model; + @Schema(description = "产品图片url") + private String url; + @Schema(description = "种类 1成品 2半成品 ") + private String type; + @Schema(description = "最终成品类型") + private String finalType; + @Schema(description = "所属行业/产品所属行业") + private String industry; + @Schema(description = "产品分类/产品所属行业子类型") + private String industrySub; + @Schema(description = "功能单位计量标准数") + private String fsu; + @Schema(description = "功能单位计量单位") + private String fUnit; + @Schema(description = "单价,产品单价") + private BigDecimal unitPrice; + @Schema(description = "产品尺寸") + private String pSize; + @Schema(description = "产品重量") + private BigDecimal pWeight; + @Schema(description = "重量计量单位") + private String wUnit; + @Schema(description = "系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓") + private String boundary; + @Schema(description = "备注") + private String remarks; + @Schema(description = "工序列表") + private String steps; + @Schema(description = "所属企业/租户code") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "产品尺寸单位") + private String pSizeUnit; + @Schema(description = "工序预览图url") + public String stepsUrl; + @Schema(description = "工序拖拉拽json") + private String stepsJson; + + @Schema(description = "产品自定义属性信息") + private List customDTOS; + + public List getCustomDTOS() { + return customDTOS; + } + + public void setCustomDTOS(List customDTOS) { + this.customDTOS = customDTOS; + } + + public String getStepsUrl() { + return stepsUrl; + } + + public void setStepsUrl(String stepsUrl) { + this.stepsUrl = stepsUrl; + } + + public String getStepsJson() { + return stepsJson; + } + + public void setStepsJson(String stepsJson) { + this.stepsJson = stepsJson; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getFinalType() { + return finalType; + } + + public void setFinalType(String finalType) { + this.finalType = finalType; + } + + public String getIndustry() { + return industry; + } + + public void setIndustry(String industry) { + this.industry = industry; + } + + public String getIndustrySub() { + return industrySub; + } + + public void setIndustrySub(String industrySub) { + this.industrySub = industrySub; + } + + public String getFsu() { + return fsu; + } + + public void setFsu(String fsu) { + this.fsu = fsu; + } + + public String getfUnit() { + return fUnit; + } + + public void setfUnit(String fUnit) { + this.fUnit = fUnit; + } + + public BigDecimal getUnitPrice() { + return unitPrice; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } + + public String getpSize() { + return pSize; + } + + public void setpSize(String pSize) { + this.pSize = pSize; + } + + public BigDecimal getpWeight() { + return pWeight; + } + + public void setpWeight(BigDecimal pWeight) { + this.pWeight = pWeight; + } + + public String getwUnit() { + return wUnit; + } + + public void setwUnit(String wUnit) { + this.wUnit = wUnit; + } + + public String getBoundary() { + return boundary; + } + + public void setBoundary(String boundary) { + this.boundary = boundary; + } + + public String getRemarks() { + return remarks; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public String getSteps() { + return steps; + } + + public void setSteps(String steps) { + this.steps = steps; + } + + public Long getTenantCode() { + return tenantCode; + } + + public void setTenantCode(Long tenantCode) { + this.tenantCode = tenantCode; + } + + public Long getCompanyId() { + return companyId; + } + + public void setCompanyId(Long companyId) { + this.companyId = companyId; + } + + public Long getDeptId() { + return deptId; + } + + public void setDeptId(Long deptId) { + this.deptId = deptId; + } + + public Long getCreator() { + return creator; + } + + public void setCreator(Long creator) { + this.creator = creator; + } + + public Long getCreateDate() { + return createDate; + } + + public void setCreateDate(Long createDate) { + this.createDate = createDate; + } + + public Long getUpdater() { + return updater; + } + + public void setUpdater(Long updater) { + this.updater = updater; + } + + public Long getUpdateDate() { + return updateDate; + } + + public void setUpdateDate(Long updateDate) { + this.updateDate = updateDate; + } + + public String getpSizeUnit() { + return pSizeUnit; + } + + public void setpSizeUnit(String pSizeUnit) { + this.pSizeUnit = pSizeUnit; + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormInfo.java new file mode 100644 index 0000000..53eb5ad --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormInfo.java @@ -0,0 +1,12 @@ +package com.thing.carbontrack.production.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class NormInfo { + @Schema(description = "能源品种编码") + private String energyCode; + @Schema(description = "能源单耗标准") + private String norm; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormPageParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormPageParam.java new file mode 100644 index 0000000..5577dec --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/NormPageParam.java @@ -0,0 +1,19 @@ +package com.thing.carbontrack.production.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class NormPageParam { + + @Schema(description = "产品编码") + private List code; + @Schema(description = "页码") + private Integer page; + @Schema(description = "每页条数") + private Integer limit; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionEnergyInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionEnergyInfo.java new file mode 100644 index 0000000..c8bed05 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionEnergyInfo.java @@ -0,0 +1,23 @@ +package com.thing.carbontrack.production.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 能源信息,能源品种,折标媒系数 + */ +@Data +public class ProductionEnergyInfo { + + @Schema(description = "能源编码") + private String energyCode; + + @Schema(description = "碳排放因子值") + private BigDecimal emissionRatio = BigDecimal.ZERO; + + @Schema(description = "折标媒系数值") + private BigDecimal powerCoalRatio = BigDecimal.ZERO; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionNormInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionNormInfo.java new file mode 100644 index 0000000..228b838 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/ProductionNormInfo.java @@ -0,0 +1,48 @@ +package com.thing.carbontrack.production.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ProductionNormInfo { + public static final String CO2_RUNMM = "CO2_runmm"; + public static final String TCE_RUNMM = "tce_runmm"; + + @Schema(description = "产品名称_型号") + private String productionName; + @Schema(description = "产品编码") + private String productionCode; + @Schema(description = "能源类型名称") + private String energyName; + @Schema(description = "能源类型编码") + private String energyCode; + @Schema(description = "单位") + private String unit; + @Schema(description = "时间") + private Long ts; + @Schema(description = "标准单耗") + private String val; + @Schema(description = "序号") + private Integer sort; + + @Schema(description = "是否可以编辑 1可以编辑,2不可以编辑") + private String editable; + + public static Integer createSort(String energyCode){ + if(energyCode.contains("CO2")){ + return 999; + } + if(energyCode.contains("tce")){ + return 1000; + } + return 1; + } + + public static String isEditable(String energyCode){ + if(energyCode.contains("CO2")||energyCode.contains("tce")){ + return "2"; + } + return "1"; + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/UsageNormParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/UsageNormParam.java new file mode 100644 index 0000000..5980b75 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/dto/UsageNormParam.java @@ -0,0 +1,72 @@ +package com.thing.carbontrack.production.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +@Data +@Schema(description = "用量标准生成表单入参") +public class UsageNormParam { + + private static final String NORM = "_normmm"; + @Schema(description = "产品编号") + private String productionCode; + @Schema(description = "年的集合 YYYY") + private List parentDateParam; + @Schema(description = "第二个时间入参月的集合,月mm,只用传两位") + private List subsetDateParam; + @Schema(description = "能源标准用量数据集合") + private List normInfos; + + + /** + * 初始化日期列表集合 + * @return + */ + public static List generateTimeList(List parentDate, List subsetDate){ + List timeList = new ArrayList<>(); + parentDate.forEach(t->{ + if(ObjectUtils.isNotEmpty(subsetDate)){ + subsetDate.forEach(i->{ + timeList.add(generateLocalDateTime(t,i)); + }); + }else { + timeList.add(generateLocalDateTime(t,null)); + } + + }); + return timeList; + } + + /** + * 根据时间类型,时间字符串,生成时间数据 + * @param t + * @param i + * @return + */ + private static LocalDateTime generateLocalDateTime(String t, String i) { + if(StringUtils.isNotEmpty(i)){ + return LocalDateTime.parse(t+"-"+i+ "-01T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME); + }else { + return LocalDateTime.parse(t+"-01-01T00:00:00", DateTimeFormatter.ISO_LOCAL_DATE_TIME); + } + } + + /** + * 根据入参,生成属性编码 + * @param energyCode + * @return + */ + public static String generateAttrCode(String energyCode){ + return energyCode+NORM; + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/entity/IotCarbonProductionVarietyEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/entity/IotCarbonProductionVarietyEntity.java new file mode 100644 index 0000000..7d66d32 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/entity/IotCarbonProductionVarietyEntity.java @@ -0,0 +1,106 @@ +package com.thing.carbontrack.production.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 产品信息表 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Getter +@Setter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_variety") +public class IotCarbonProductionVarietyEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品编码 + */ + private String code; + /** + * 产品名称 + */ + private String name; + /** + * 产品规格型号 + */ + private String model; + /** + * 产品图片url + */ + private String url; + /** + * 种类 1成品 2半成品 + */ + private String type; + /** + * 最终成品类型 + */ + private String finalType; + /** + * 所属行业/产品所属行业 + */ + private String industry; + /** + * 产品分类/产品所属行业子类型 + */ + private String industrySub; + /** + * 功能单位计量标准数 + */ + private String fsu; + /** + * 功能单位计量单位 + */ + private String fUnit; + /** + * 单价,产品单价 + */ + private BigDecimal unitPrice; + /** + * 产品尺寸 + */ + private String pSize; + /** + * 产品重量 + */ + private BigDecimal pWeight; + /** + * 重量计量单位 + */ + private String wUnit; + /** + * 系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓 + */ + private String boundary; + /** + * 备注 + */ + private String remarks; + /** + * 产品尺寸单位 + */ + private String pSizeUnit; + /** + * 工序预览图url + */ + public String stepsUrl; + /** + * 工序拖拉拽json + */ + private String stepsJson; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/mapper/IotCarbonProductionVarietyMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/mapper/IotCarbonProductionVarietyMapper.java new file mode 100644 index 0000000..f8428f1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/mapper/IotCarbonProductionVarietyMapper.java @@ -0,0 +1,21 @@ +package com.thing.carbontrack.production.mapper; + +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 产品信息表 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonProductionVarietyMapper extends PowerBaseMapper { + + List industrySubListByIndustryName(String industry); + + List fUnitList(Long tenantCode); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/IotCarbonProductionVarietyService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/IotCarbonProductionVarietyService.java new file mode 100644 index 0000000..e9dd759 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/IotCarbonProductionVarietyService.java @@ -0,0 +1,55 @@ +package com.thing.carbontrack.production.service; + +import com.thing.carbontrack.production.dto.IotCarbonProductionVarietyDTO; +import com.thing.carbontrack.production.dto.NormPageParam; +import com.thing.carbontrack.production.dto.ProductionNormInfo; +import com.thing.carbontrack.production.dto.UsageNormParam; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 产品信息表 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonProductionVarietyService extends IBaseService { + + PageData getPageDataIotCarbonProductionVarietyDTO(Map params); + + List listAsIotCarbonProductionVarietyDTO(Map params); + + String generateNorm(UsageNormParam param); + + PageData normPage(NormPageParam param); + + IotCarbonProductionVarietyDTO queryByCode(String code); + + List industrySubListByIndustryName(String industry); + + List fUnitList(); + + Long saveWithJson(String productJson, Long tenantCode); + + void updateWithJson(String productJson, Long productId, Long tenantCode); + + List getTargetFieldHistoryRecords(String field); + + void batchDeleteIotCarbonProductionVarietyEntity(Long[] ids); + + Result saveIotCarbonProductionVarietyDTO(IotCarbonProductionVarietyDTO dto); + + Result updateIotCarbonProductionVarietyDTO(IotCarbonProductionVarietyDTO dto); + + IotCarbonProductionVarietyDTO getByCodeName(String code, String name, Long tenantCode); + + Long getIdByCodeName(String code, String name, Long tenantCode); + + Result model(Long id); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/impl/IotCarbonProductionVarietyServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/impl/IotCarbonProductionVarietyServiceImpl.java new file mode 100644 index 0000000..3a9680b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/production/service/impl/IotCarbonProductionVarietyServiceImpl.java @@ -0,0 +1,443 @@ +package com.thing.carbontrack.production.service.impl; + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.dto.*; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.production.mapper.IotCarbonProductionVarietyMapper; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.pub.dto.MaterialJsonBean; +import com.thing.carbontrack.pub.dto.ProcessJsonBean; +import com.thing.carbontrack.pub.dto.ProductJsonBean; +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.PageUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.distinct; +import static com.thing.carbontrack.production.entity.table.IotCarbonProductionVarietyEntityTableDef.IOT_CARBON_PRODUCTION_VARIETY_ENTITY; + +/** + * 产品信息表 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonProductionVarietyServiceImpl extends BaseServiceImpl implements IotCarbonProductionVarietyService { + + private static final String TABLE_SUFFIX = "_external"; + private static final String NORM = "_normmm"; + public static final String CO2_RUNMM = "CO2_runmm"; + public static final String TCE_RUNMM = "tce_runmm"; + + public static final String CO2_ENERGYMM = "CO2_%smm"; + public static final String TCE_ENERGYMM = "tce_%smm"; + + + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + @Autowired + private TsKvService tsKvService; + @Autowired + private IotCarbonRatioService carbonRatioService; + @Autowired + private ThingManageContextService thingManageContextService; + @Autowired + private IotCarbonProcessStepsService iotCarbonProcessStepsService; + @Autowired + private IotCarbonBomService carbonBomService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryColumn code = new QueryColumn("code"); + QueryColumn name = new QueryColumn("name"); + QueryWrapper wrapper = new QueryWrapper(); + String key = MapUtils.getString(params,"key"); + String industry = MapUtils.getString(params,"industry"); + String industry_sub = MapUtils.getString(params,"industrySub"); + String type = MapUtils.getString(params,"type"); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + wrapper.eq("industry",industry, StringUtils.isNotEmpty(industry)); + wrapper.eq("industry_sub",industry_sub, StringUtils.isNotEmpty(industry_sub)); + wrapper.eq("tenant_code",currentTenantCode); + if(StringUtils.isNotEmpty(key)){ + wrapper.and(code.like(key).or(name.like(key))); + } + wrapper.eq("type",type, StringUtils.isNotEmpty(type)); + return wrapper; + } + + + @Override + public PageData getPageDataIotCarbonProductionVarietyDTO(Map params) { + + PageData pageData = this.getPageData(params,IotCarbonProductionVarietyDTO.class); + String isViewStep = MapUtils.getString(params,"isViewStep"); + if(ObjectUtils.isNotEmpty(isViewStep)&&isViewStep.equals("1")){ + pageData.getList().forEach(temp->{ + List stepsDTOS = iotCarbonProcessStepsService.listByMid(temp.getId()); + List stepNames = stepsDTOS.stream().map(IotCarbonProcessStepsDTO::getProcessName).toList(); + temp.setSteps(String.join(",", stepNames)); + + }); + } + + return pageData; + } + + @Override + public List listAsIotCarbonProductionVarietyDTO(Map params) { + return this.listAs(params,IotCarbonProductionVarietyDTO.class); + } + + @Override + public String generateNorm(UsageNormParam param) { + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + Map varietyParam = new HashMap<>(); + varietyParam.put("tenantCode",String.valueOf(currentTenantCode)); + List varietyEntities = carbonEnergyVarietyService.listCarbonEnergyVarietyDTO(varietyParam); + //获取每个能源品种得折标媒系数 + List productionEnergyInfos = new ArrayList<>(); + varietyEntities.forEach(temp->{ + ProductionEnergyInfo info = new ProductionEnergyInfo(); + IotCarbonRatioEntity emissionDto = carbonRatioService.queryOnByEvIdAndType(temp.getId(),"1"); + IotCarbonRatioEntity powerCoalRatioDto = carbonRatioService.queryOnByEvIdAndType(temp.getId(),"2"); + info.setEnergyCode(temp.getCode()); + if(ObjectUtils.isNotEmpty(emissionDto)){ + info.setEmissionRatio(emissionDto.getRatio()); + } + if(ObjectUtils.isNotEmpty(powerCoalRatioDto)){ + info.setPowerCoalRatio(powerCoalRatioDto.getRatio()); + } + productionEnergyInfos.add(info); + }); + Map> energyMap = productionEnergyInfos.stream().collect(Collectors.groupingBy(ProductionEnergyInfo::getEnergyCode)); + + List tsKvDTOS = new ArrayList<>(); + List localTimes = UsageNormParam.generateTimeList(param.getParentDateParam(),param.getSubsetDateParam()); + param.getNormInfos().forEach(info->{ + localTimes.forEach(time->{ + long ts = time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + tsKvDTOS.add(new TsKvDTO(param.getProductionCode(), UsageNormParam.generateAttrCode(info.getEnergyCode()) + ,ts,info.getNorm())); + }); + }); + try { + tsKvService.saveDTOTsKvByTableSuffix(tsKvDTOS,TABLE_SUFFIX); + } catch (Exception e) { + return "数据插入异常"; + } + //插入数据完毕,汇总,计算 + Long startTime = Collections.min(localTimes).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + Long endTime = Collections.max(localTimes).atZone(ZoneOffset.systemDefault()).toInstant().toEpochMilli(); + List tceDto = new ArrayList<>(); + List CO2Dto = new ArrayList<>(); + varietyEntities.forEach(temp->{ + List dtos = tsKvService.findTsKvByCodesAndAttrsWithTableSuffix(List.of(param.getProductionCode()), + List.of(UsageNormParam.generateAttrCode(temp.getCode())),startTime,endTime,Boolean.FALSE,TABLE_SUFFIX); + BigDecimal tceR; + BigDecimal co2R; + ProductionEnergyInfo energyInfo = energyMap.get(temp.getCode()).stream().findFirst().orElse(null); + if(ObjectUtils.isNotEmpty(energyInfo)){ + //折标媒 tce + tceR = energyInfo.getEmissionRatio(); + //碳排 co2 + co2R = energyInfo.getPowerCoalRatio(); + } else { + co2R = BigDecimal.ZERO; + tceR = BigDecimal.ZERO; + } + dtos.forEach(dto->{ + BigDecimal tceResult = tceR.multiply(new BigDecimal(dto.getVal())).setScale(6, RoundingMode.UP); + BigDecimal CO2Result = co2R.multiply(new BigDecimal(dto.getVal())).setScale(6, RoundingMode.UP); + tceDto.add(new TsKvDTO(dto.getThingCode(),String.format(TCE_ENERGYMM,temp.getCode()),dto.getTs(),tceResult.toString())); + CO2Dto.add(new TsKvDTO(dto.getThingCode(),String.format(CO2_ENERGYMM,temp.getCode()),dto.getTs(),CO2Result.toString())); + }); + }); + //汇总tce 每个时间得总量,c02 每个时间得总量 + Map> tceDtoMap = tceDto.stream().collect(Collectors.groupingBy(TsKvDTO::getTs)); + Map> CO2DtoMap = CO2Dto.stream().collect(Collectors.groupingBy(TsKvDTO::getTs)); + List computeResult = new ArrayList<>(); + tceDtoMap.forEach((key, valueList) -> { + BigDecimal sum = valueList.stream() + .map(TsKvDTO::getVal) + .map(IotCarbonProductionVarietyServiceImpl::toBigDecimal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + computeResult.add(new TsKvDTO(param.getProductionCode(),TCE_RUNMM,key,sum.toString())); + }); + CO2DtoMap.forEach((key, valueList) -> { + BigDecimal sum = valueList.stream() + .map(TsKvDTO::getVal) + .map(IotCarbonProductionVarietyServiceImpl::toBigDecimal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + computeResult.add(new TsKvDTO(param.getProductionCode(),CO2_RUNMM,key,sum.toString())); + }); + computeResult.addAll(tceDto); + computeResult.addAll(CO2Dto); + try { + tsKvService.saveDTOTsKvByTableSuffix(computeResult,TABLE_SUFFIX); + } catch (Exception e) { + return "数据插入异常"; + } + return "插入成功"; + } + + @Override + public PageData normPage(NormPageParam param) { + List result = new ArrayList<>(); + List attrCodes = new ArrayList<>(); + + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + Map varietyParam = new HashMap<>(); + varietyParam.put("tenantCode",String.valueOf(currentTenantCode)); + List varietyEntities = carbonEnergyVarietyService.listCarbonEnergyVarietyDTO(varietyParam); + Map attrNameMap = new HashMap<>(); + attrNameMap.put(CO2_RUNMM,"碳排"); + attrNameMap.put(TCE_RUNMM,"能耗"); + + varietyEntities.forEach(temp->{ + attrCodes.add(UsageNormParam.generateAttrCode(temp.getCode())); + attrNameMap.put(temp.getCode(),temp.getName()); + }); + Map uintMap = new HashMap<>(); + uintMap.put(CO2_RUNMM,"CO₂"); + uintMap.put(TCE_RUNMM,"tce"); + attrCodes.forEach(temp->{ + String unit = ""; + Optional dict = thingManageContextService.findDictByCode(temp); + if (dict.isPresent()) { + unit = dict.get().getUnit(); + } + uintMap.put(temp,unit); + }); + attrCodes.addAll(List.of(CO2_RUNMM, TCE_RUNMM)); + + Map productionNameMap = new HashMap<>(); + param.getCode().forEach(temp->{ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotCarbonProductionVarietyEntity::getCode,temp); + String name = null; + try { + name = mapper.selectOneByQuery(wrapper).getName(); + } catch (Exception e) { + name = "未知"; + } + productionNameMap.put(temp,name); + }); + + List dtos = tsKvService.findTsKvByCodesAndAttrsWithTableSuffix(param.getCode(), + attrCodes,null,null,Boolean.FALSE,TABLE_SUFFIX); + dtos.forEach(temp->{ + ProductionNormInfo info = new ProductionNormInfo(); + info.setProductionCode(temp.getThingCode()); + info.setVal(temp.getVal()); + info.setUnit(uintMap.get(temp.getAttrKey())); + info.setTs(temp.getTs()); + info.setEnergyCode(temp.getAttrKey().replace(NORM,"")); + info.setEnergyName(attrNameMap.get(info.getEnergyCode())); + info.setSort(ProductionNormInfo.createSort(temp.getAttrKey())); + info.setProductionName(productionNameMap.get(temp.getThingCode())); + info.setEditable(ProductionNormInfo.isEditable(temp.getAttrKey())); + result.add(info); + }); + result.sort(Comparator.comparing(ProductionNormInfo::getProductionCode) + .thenComparing(ProductionNormInfo::getTs) + .thenComparing(ProductionNormInfo::getSort)); + + List pageList = PageUtils.startPage(result, param.getPage(), param.getLimit()); + return new PageData<>(pageList, result.size()); + } + + @Override + public IotCarbonProductionVarietyDTO queryByCode(String code) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("code",code, StringUtils.isNotEmpty(code)); + return this.getOneAs(wrapper,IotCarbonProductionVarietyDTO.class); + } + + @Override + public List industrySubListByIndustryName(String industry) { + return this.mapper.industrySubListByIndustryName(industry); + } + + @Override + public List fUnitList() { + Long tenantCode = UserContext.getTenantCode(); + return this.mapper.fUnitList(tenantCode); + } + + @Override + public Long saveWithJson(String productJson, Long tenantCode) { + ProductJsonBean productJsonBean = JSONObject.parseObject(productJson, ProductJsonBean.class); + IotCarbonProductionVarietyEntity entity = ConvertUtils.sourceToTarget(productJsonBean, IotCarbonProductionVarietyEntity.class); + + IotCarbonProductionVarietyDTO existOne = getByCodeName(productJsonBean.getCode(), productJsonBean.getName(), tenantCode); + Long productId = Objects.isNull(existOne) ? null : existOne.getId(); + entity.setId(productId); + entity.setTenantCode(tenantCode).setCompanyId(tenantCode).setDeptId(tenantCode); + + saveOrUpdate(entity); + return entity.getId(); + } + + @Override + public void updateWithJson(String productJson, Long productId, Long tenantCode) { + ProductJsonBean productJsonBean = JSONObject.parseObject(productJson, ProductJsonBean.class); + productJsonBean.setId(productId); + IotCarbonProductionVarietyEntity entity = ConvertUtils.sourceToTarget(productJsonBean, IotCarbonProductionVarietyEntity.class); + updateById(entity); + } + + @Override + public List getTargetFieldHistoryRecords(String field) { + List list = + list(QueryWrapper.create().select(distinct(new QueryColumn(field)))); + if(CollectionUtils.isEmpty(list)){ + return Collections.emptyList(); + } + return list.stream() + .filter(Objects::nonNull) + .map( + e -> + switch (field) { + case "industry" -> e.getIndustry(); + case "industry_sub" -> e.getIndustrySub(); + case "f_unit" -> e.getFUnit(); + default -> + throw new IllegalStateException( + "UpSupport Field: " + field); + }) + .toList(); + } + + @Override + public void batchDeleteIotCarbonProductionVarietyEntity(Long[] ids) { + for (Long id : ids) { + //删除工艺工序 + iotCarbonProcessStepsService.deleteByMid(id); + //删除bom + carbonBomService.deleteByMid(id); + //删除产品模型 + this.removeById(id); + } + } + + @Override + public Result saveIotCarbonProductionVarietyDTO(IotCarbonProductionVarietyDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("code",dto.getCode()); + wrapper.eq("name",dto.getName()); + long count = this.count(wrapper); + if(count>1){ + Result result = new Result(); + result.setCode(-1); + result.setData(-1); + result.setMsg("产品已存在!"); + return result; + } + this.saveDto(dto); + return new Result<>().ok(dto.getId()); + } + + @Override + public Result updateIotCarbonProductionVarietyDTO(IotCarbonProductionVarietyDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("code",dto.getCode()); + wrapper.eq("name",dto.getName()); + wrapper.eq("tenant_code",UserContext.getTenantCode()); + IotCarbonProductionVarietyDTO dto1 = this.getOneAs(wrapper,IotCarbonProductionVarietyDTO.class); + + if(!Objects.equals(dto1.getId(), dto.getId())){ + Result result = new Result(); + result.setCode(-1); + result.setData(-1); + result.setMsg("产品已存在!"); + return result; + } + this.updateDto(dto); + return new Result().ok(dto.getId()); + + } + + @Override + public IotCarbonProductionVarietyDTO getByCodeName(String code, String name, Long tenantCode) { + return mapper.selectOneByQueryAs( + QueryWrapper.create() + .eq(IotCarbonProductionVarietyEntity::getCode, code) + .eq(IotCarbonProductionVarietyEntity::getName, name) + .eq(IotCarbonProductionVarietyEntity::getTenantCode, tenantCode) + .limit(1), + IotCarbonProductionVarietyDTO.class); + } + + @Override + public Long getIdByCodeName(String code, String name, Long tenantCode) { + IotCarbonProductionVarietyEntity entity = + mapper.selectOneByQuery( + QueryWrapper.create() + .select(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.ID) + .eq(IotCarbonProductionVarietyEntity::getCode, code) + .eq(IotCarbonProductionVarietyEntity::getName, name) + .eq(IotCarbonProductionVarietyEntity::getTenantCode, tenantCode) + .limit(1)); + return entity.getId(); + } + + @Override + public Result model(Long id) { + //产品模型重新上传 + //获取产品基本信息 + ProductJsonBean productJsonBean = this.getByIdAs(id,ProductJsonBean.class); + //产品bom 基本信息 + List materialJsonBeanList = carbonBomService.queryMaterialJsonBeanListByMid(id); + //产品工艺工序信息 + List processJsonBeanList = iotCarbonProcessStepsService.listProcessJsonBeanByMid(id); + ProductionModelUploadDTO modelUploadDTO = ProductionModelUploadDTO.init(productJsonBean,materialJsonBeanList,processJsonBeanList); + return new Result().ok(modelUploadDTO); + } + + + private static BigDecimal toBigDecimal(String value) { + try { + return new BigDecimal(value); + } catch (NumberFormatException e) { + return BigDecimal.ZERO; + } + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/controller/IotCarbonProductionRecordController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/controller/IotCarbonProductionRecordController.java new file mode 100644 index 0000000..8ff0223 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/controller/IotCarbonProductionRecordController.java @@ -0,0 +1,116 @@ +package com.thing.carbontrack.productionRecord.controller; + +import com.thing.carbontrack.productionRecord.dto.ProductionPage; +import com.thing.carbontrack.productionRecord.dto.ProductionParam; +import com.thing.carbontrack.productionRecord.dto.ProductionReq; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 生产记录 +* +* @author xc +* @since 3.0 2024-05-21 +*/ +@RestController +@RequestMapping("v2/productionRecord/iotcarbonproductionrecord") +@Tag(name="AAA生产记录") +@RequiredArgsConstructor +public class IotCarbonProductionRecordController { + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "mId", description = "产品主键id"), + @Parameter(name = "prType", description = "工单类型"), + @Parameter(name = "prStatus", description = "工单状态"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionRecordService.getPageDataIotCarbonProductionRecordDTO(params, ProductionPage.class); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表查询") + @Parameters({ + @Parameter(name = "prCode", description = "工单编码"), + @Parameter(name = "mId", description = "产品id"), + }) + public Result> list(@RequestParam String prCode,@RequestParam Long mId){ + List list = iotCarbonProductionRecordService.listProductionReq(prCode,mId); + return new Result>().ok(list); + } + + @GetMapping("listByPrCodeAndGroupId") + @Operation(summary="根据工单编码,数据组id,产品id,查看数据,用于工单报工记录修改") + @Parameters({ + @Parameter(name = "prCode", description = "工单编码"), + @Parameter(name = "groupId", description = "报工单组id"), + @Parameter(name = "mId", description = "产品id") + }) + public Result listByPrCodeAndGroupId(@RequestParam String prCode, @RequestParam(required = false) Long groupId, @RequestParam(required = false) Long mId){ + ProductionParam list = iotCarbonProductionRecordService.listByPrCodeAndGroupId(prCode,groupId,mId); + return new Result().ok(list); + } + + @GetMapping("listByPrCode") + @Operation(summary="根据工单编码查看数据,用于工单报工记录新增") + @Parameters({ + @Parameter(name = "prCode", description = "工单编码"), + @Parameter(name = "mid", description = "产品id") + }) + public Result listByPrCode(@RequestParam String prCode,@RequestParam(required = false) Long mId){ + ProductionParam list = iotCarbonProductionRecordService.listByPrCode(prCode,mId); + return new Result().ok(list); + } + + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody ProductionParam dto){ + iotCarbonProductionRecordService.saveProductionParam(dto); + return new Result<>(); + + } + + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody ProductionParam dto){ + iotCarbonProductionRecordService.updateProductionParam(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionRecordService.batchDelete(ids); + return new Result<>(); + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/IotCarbonProductionRecordDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/IotCarbonProductionRecordDTO.java new file mode 100644 index 0000000..943d243 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/IotCarbonProductionRecordDTO.java @@ -0,0 +1,73 @@ +package com.thing.carbontrack.productionRecord.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 生产记录 +* +* @author xc +* @since 3.0 2024-05-21 +*/ +@Data +@Schema(description = "生产记录") +public class IotCarbonProductionRecordDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "生产车间") + private String workName; + @Schema(description = "生产开始时间") + private Date startTime; + @Schema(description = "生产结束时间") + private Date endTime; + @Schema(description = "生产数量") + private Long pNum; + @Schema(description = "成品数量") + private Long finalNum; + @Schema(description = "工艺工序id") + private Long psId; + @Schema(description = "生产对象/物实体编码") + private String thingCode; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "用量") + private BigDecimal dosage; + @Schema(description = "使用比率") + private BigDecimal ratio; + @Schema(description = "工单类型1批次接入,2时段录入") + private String prType; + @Schema(description = "生产记录状态1未完工 2已完工 3计算异常") + private Long prStatus; + @Schema(description = "备注") + private String remarks; + @Schema(description = "企业编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "提交数据组id") + private Long groupId; + @Schema(description = "生产工时") + private BigDecimal prDur; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/OrderParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/OrderParam.java new file mode 100644 index 0000000..81a9a5a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/OrderParam.java @@ -0,0 +1,33 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class OrderParam { + + @Schema(description = "生产记录主键id") + private Long id; + @Schema(description = "工艺工序id") + private Long psId; + @Schema(description = "工艺工序编码") + private String psCode; + @Schema(description = "工艺工序名称") + private String psName; + @Schema(description = "生产对象/物实体编码") + private String thingCode; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源品种编码") + private String evCode; + @Schema(description = "能源品种名称") + private String evName; + @Schema(description = "用量") + private BigDecimal dosage; + @Schema(description = "使用比率") + private BigDecimal ratio; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionOverviewReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionOverviewReq.java new file mode 100644 index 0000000..8f4011c --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionOverviewReq.java @@ -0,0 +1,19 @@ +package com.thing.carbontrack.productionRecord.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Data +public class ProductionOverviewReq { + + + @Schema(description = "工单编码") + private String prCode; + + @Schema(description = "良品数量/批次产量,工单产量") + private Long finalNum; + + @Schema(description = "已出库数量") + private Long outboundNum; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionPage.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionPage.java new file mode 100644 index 0000000..7a9b1f0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionPage.java @@ -0,0 +1,61 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +@Data +public class ProductionPage { + @Schema(description = "工单编码") + private String prCode; + @Schema(description = "开始时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date startTime; + @Schema(description = "结束时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date endTime; + @Schema(description = "生产时长min") + private String min; + @Schema(description = "生产时长hour") + private Integer hour; + @Schema(description = "生产数量") + private Long pNum; + @Schema(description = "良品数量/批次产量") + private Long finalNum; + @Schema(description = "能源工序数量,前端无需关注") + private Long count; + @Schema(description = "产品名称") + private String mName; + @Schema(description = "产品编码") + private String mCode; + @Schema(description = "产品型号") + private String mModel; + @Schema(description = "产品功能单位") + private String fUnit; + @Schema(description = "核算边界") + private String boundary; + @Schema(description = "工单状态") + private String prStatus; + @Schema(description = "物料id") + private Long mId; + @Schema(description = "物料重量") + private BigDecimal pWeight; + @Schema(description = "物料重量单位") + private String wUnit; + + private Long evId; + + private String evName; + + private Long groupId; + @Schema(description = "生产工时") + private Long prDur; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionParam.java new file mode 100644 index 0000000..fe72361 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionParam.java @@ -0,0 +1,48 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import com.thing.carbontrack.Indirect.dto.IotCarbonIndirectEnergyDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +@Data +public class ProductionParam { + + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "产品编码") + private String mCode; + @Schema(description = "产品名称") + private String mName; + @Schema(description = "生产车间") + private String workName; + @Schema(description = "生产开始时间") + private Date startTime; + @Schema(description = "生产结束时间") + private Date endTime; + @Schema(description = "生产工时") + private BigDecimal prDur; + @Schema(description = "生产数量") + private Long pNum; + @Schema(description = "成品数量") + private Long finalNum; + @Schema(description = "工单类型1批次接入,2时段录入") + private String prType; + @Schema(description = "生产记录状态1未完工 2已完工 3计算异常") + private String prStatus; + @Schema(description = "数据组id") + private Long groupId; + @Schema(description = "工序记录列表") + private List orderParams; + @Schema(description = "间接,公摊用能列表") + private List indirectEnergyDTOS; + + private String orderType; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionReq.java new file mode 100644 index 0000000..f8900d7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionReq.java @@ -0,0 +1,33 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +@Data +public class ProductionReq { + + @Schema(description = "开始时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date startTime; + @Schema(description = "结束时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date endTime; + @Schema(description = "生产数量") + private Long pNum; + @Schema(description = "良品数量") + private Long finalNum; + @Schema(description = "报工工序") + private String steps; + @Schema(description = "组id") + private String groupId; + @Schema(description = "生产工单编码") + private String prCode; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionResult.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionResult.java new file mode 100644 index 0000000..3bf66da --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/ProductionResult.java @@ -0,0 +1,60 @@ +package com.thing.carbontrack.productionRecord.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + + +@Data +public class ProductionResult { + + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "产品编码") + private String mCode; + @Schema(description = "产品名称") + private String mName; + @Schema(description = "生产车间") + private String workName; + @Schema(description = "生产开始时间") + private Date startTime; + @Schema(description = "生产结束时间") + private Date endTime; + @Schema(description = "生产数量") + private Long pNum; + @Schema(description = "成品数量") + private Long finalNum; + @Schema(description = "工单类型1批次接入,2时段录入") + private String prType; + @Schema(description = "生产记录状态1未完工 2已完工 3计算异常") + private String prStatus; + @Schema(description = "提交数据组id") + private Long groupId; + @Schema(description = "生产工时") + private BigDecimal prDur; + + @Schema(description = "生产记录主键id") + private Long id; + @Schema(description = "工艺工序id") + private Long psId; + @Schema(description = "工艺工序编码") + private String psCode; + @Schema(description = "工艺工序名称") + private String psName; + @Schema(description = "生产对象/物实体编码") + private String thingCode; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源品种编码") + private String evCode; + @Schema(description = "能源品种名称") + private String evName; + @Schema(description = "用量") + private BigDecimal dosage; + @Schema(description = "使用比率") + private BigDecimal ratio; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/QuantityDto.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/QuantityDto.java new file mode 100644 index 0000000..f4fadc5 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/QuantityDto.java @@ -0,0 +1,11 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import lombok.Data; + +@Data +public class QuantityDto { + private Long pNum; + private Long finalNum; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/RecordReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/RecordReq.java new file mode 100644 index 0000000..a683186 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/dto/RecordReq.java @@ -0,0 +1,13 @@ +package com.thing.carbontrack.productionRecord.dto; + + +import lombok.Data; + +@Data +public class RecordReq { + private Long mId; + + private String prCode; + + private Long num; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/entity/IotCarbonProductionRecordEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/entity/IotCarbonProductionRecordEntity.java new file mode 100644 index 0000000..345da16 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/entity/IotCarbonProductionRecordEntity.java @@ -0,0 +1,96 @@ +package com.thing.carbontrack.productionRecord.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 生产记录 + * + * @author xc + * @since 3.0 2024-05-21 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_record") +public class IotCarbonProductionRecordEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 生产记录编码 + */ + private String prCode; + /** + * 产品id + */ + private Long m_id; + /** + * 生产车间 + */ + private String workName; + /** + * 生产开始时间 + */ + private Date startTime; + /** + * 生产结束时间 + */ + private Date endTime; + /** + * 生产数量/本产品生产数量 + */ + private Long pNum; + /** + * 成品数量/全场产量 + */ + private Long finalNum; + /** + * 工艺工序id + */ + private Long psId; + /** + * 生产对象/物实体编码 + */ + private String thingCode; + /** + * 能源品种id + */ + private Long evId; + /** + * 用量 + */ + private BigDecimal dosage; + /** + * 使用比率 + */ + private BigDecimal ratio; + /** + * 工单类型1批次接入,2时段录入 + */ + private String prType; + /** + * 生产记录状态1未完工 2已完工 3计算异常 + */ + private Long prStatus; + /** + * 备注 + */ + private String remarks; + /** + * 提交数据组id + */ + private Long groupId; + /** + * 生产工时 + */ + private BigDecimal prDur; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/mapper/IotCarbonProductionRecordMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/mapper/IotCarbonProductionRecordMapper.java new file mode 100644 index 0000000..f50f580 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/mapper/IotCarbonProductionRecordMapper.java @@ -0,0 +1,44 @@ +package com.thing.carbontrack.productionRecord.mapper; + +import com.thing.carbontrack.productionRecord.dto.*; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** +* 生产记录 +* +* @author xc +* @since 3.0 2024-05-21 +*/ +@Mapper +public interface IotCarbonProductionRecordMapper extends PowerBaseMapper { + + Integer getPageDataIotCarbonProductionRecordDTOCount(Map params); + + List getPageDataIotCarbonProductionRecordDTO(Map params); + + List listProductionReq(String prCode,Long mId); + + List listByPrCodeAndGroupId(String prCode, Long groupId,Long mId); + + List getRecordReqList(); + + Integer updateByMidAndPrCode(Long mId, String prCode); + + List getRecordReqListByStatus(); + + void updatePrStatus(Long prStatus, Long mId, String prCode); + + List productionListByMid(Long mId); + + void updatePrStatusByTimeAndMid(List mIds, Date startTime, Date endTime); + + QuantityDto getQuantityByMidAndPrCode(Long mId, String prCode); + + String getPrTypeByMidAndPrCode(Long mId, String prCode); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/IotCarbonProductionRecordService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/IotCarbonProductionRecordService.java new file mode 100644 index 0000000..bfffb60 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/IotCarbonProductionRecordService.java @@ -0,0 +1,53 @@ +package com.thing.carbontrack.productionRecord.service; + +import com.thing.carbontrack.productionRecord.dto.*; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.useConfig.dto.RecalculateByTimeDTO; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 生产记录 + * + * @author xc + * @since 3.0 2024-05-21 + */ +public interface IotCarbonProductionRecordService extends IBaseService { + + PageData getPageDataIotCarbonProductionRecordDTO(Map params, Class productionPageClass); + + List listProductionReq(String prCode,Long mId); + + ProductionParam listByPrCodeAndGroupId(String prCode, Long groupId,Long mId); + + ProductionParam listByPrCode(String prCode,Long mId); + + void deleteByPrCode(String prCode,Long mId); + + void saveProductionParam(ProductionParam dto); + + void updateProductionParam(ProductionParam dto); + + List getListByStatus(String status); + + List getListByParam(Long mId,String prCode,Long groupId); + + List getRecordReqList(); + + void updateByMidAndPrCode(Long mId, String prCode); + + List getRecordReqListByStatus(); + + void updatePrStatus(Long prStatus, Long mId, String prCode); + + List productionListByMid(Long mId); + + void updatePrStatusByTimeAndMid(RecalculateByTimeDTO dto); + + QuantityDto getQuantityByMidAndPrCode(Long mId, String prCode); + + String getPrTypeByMidAndPrCode(Long mId, String prCode); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/impl/IotCarbonProductionRecordServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/impl/IotCarbonProductionRecordServiceImpl.java new file mode 100644 index 0000000..e53ae3d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionRecord/service/impl/IotCarbonProductionRecordServiceImpl.java @@ -0,0 +1,338 @@ +package com.thing.carbontrack.productionRecord.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.carbontrack.Indirect.service.IotCarbonIndirectEnergyService; +import com.thing.carbontrack.event.standardcal.ProductionProcessEvent; +import com.thing.carbontrack.outbound.mapper.IotCarbonOutboundConfigMapper; +import com.thing.carbontrack.productionRecord.dto.*; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.productionRecord.mapper.IotCarbonProductionRecordMapper; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.carbontrack.useConfig.dto.RecalculateByTimeDTO; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 生产记录 + * + * @author xc + * @since 3.0 2024-05-21 + */ +@Service +public class IotCarbonProductionRecordServiceImpl extends BaseServiceImpl implements IotCarbonProductionRecordService { + + @Autowired + private IotCarbonIndirectEnergyService iotCarbonIndirectEnergyService; + @Autowired + private IotCarbonProcessStepsService iotCarbonProcessStepsService; + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + @Autowired + private IotCarbonOutboundConfigMapper carbonOutboundConfigMapper; + + + @Autowired + private ApplicationEventPublisher applicationEventPublisher; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public PageData getPageDataIotCarbonProductionRecordDTO(Map params, Class productionPageClass) { + Long page = MapUtils.getLong(params,"page"); + Long mId = MapUtils.getLong(params,"m_id"); + Long limit = MapUtils.getLong(params,"limit"); + Integer prStatus = MapUtils.getInteger(params,"prStatus"); + params.put("page",page-1); + params.put("mId",mId); + params.put("limit",limit); + params.put("prStatus",prStatus); + Integer count = this.mapper.getPageDataIotCarbonProductionRecordDTOCount(params); + if (count == 0) { + return new PageData<>(Collections.emptyList(), 0); + } + List pageList = this.mapper.getPageDataIotCarbonProductionRecordDTO(params); + pageList.forEach(temp->{ + temp.setMin(String.valueOf((temp.getEndTime().getTime()-temp.getStartTime().getTime())/ (60 * 1000))); + }); + return new PageData<>(pageList,count); + } + + @Override + public List listProductionReq(String prCode,Long mId) { + List reqs = this.mapper.listProductionReq(prCode,mId); + reqs.forEach(temp->{ + String agg = temp.getSteps(); + if(ObjectUtils.isNotEmpty(agg)){ + String result = Arrays.stream(agg.split(",")) + .distinct() + .collect(Collectors.joining(",")); + temp.setSteps(result); + }else { + temp.setSteps("暂无"); + } + }); + return reqs; + } + + + @Override + public ProductionParam listByPrCodeAndGroupId(String prCode, Long groupId,Long mId) { + ProductionParam resultInfo = new ProductionParam(); + List tempResult = this.mapper.listByPrCodeAndGroupId(prCode,groupId,mId); + if(ObjectUtils.isNotEmpty(tempResult)){ + ProductionResult result = tempResult.stream().findFirst().orElse(new ProductionResult()); + resultInfo= ConvertUtils.sourceToTarget(result,ProductionParam.class); + List reqs = ConvertUtils.sourceToTarget(tempResult,OrderParam.class); + resultInfo.setOrderParams(reqs); + } + resultInfo.setIndirectEnergyDTOS(iotCarbonIndirectEnergyService.getListByPrCodeAndGroupId(prCode,groupId)); + return resultInfo; + } + + @Override + public ProductionParam listByPrCode(String prCode,Long mId) { + ProductionParam result = new ProductionParam(); + List tempResult = this.mapper.listByPrCodeAndGroupId(prCode,null,mId); + //工序id+能源id分组 + List reqs = ConvertUtils.sourceToTarget(tempResult,OrderParam.class); + Map haveOrder = this.groupByPidAndEvId(reqs); + + List stepsDTOS = iotCarbonProcessStepsService.listByMid(mId); + List orderParams = new ArrayList<>(); + stepsDTOS.forEach(temp->{ + List evIds = Arrays.stream(temp.getEvIds().split(",")) + .distinct().toList(); + evIds.forEach(evId->{ + OrderParam param = haveOrder.get(temp.getId()+"-"+evId); + if(ObjectUtils.isEmpty(param)){ + //不存在的,初始化 + param = new OrderParam(); + this.buildOrderParam(temp,evId,param); + } + orderParams.add(param); + }); + }); + result.setOrderParams(orderParams); + return result; + } + + @Override + public void deleteByPrCode(String prCode, Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId); + wrapper.eq("pr_code",prCode); + this.remove(wrapper); + } + + @Override + @Transactional + public void saveProductionParam(ProductionParam dto) { + BigDecimal prDur; + if(ObjectUtils.isNotEmpty(dto.getPrDur())){ + prDur = dto.getPrDur(); + }else if(dto.getStartTime()!=null&&dto.getEndTime()!=null){ + prDur = BigDecimal.valueOf((dto.getEndTime().getTime() - dto.getStartTime().getTime())/ (1000 * 60 * 60)); + } else { + prDur = dto.getPrDur(); + } + Long groupId= System.currentTimeMillis(); + String prCode = dto.getPrCode(); + Set psIds = new HashSet<>(); + dto.getOrderParams().forEach(temp->{ + IotCarbonProductionRecordDTO dto1 = ConvertUtils.sourceToTarget(dto,IotCarbonProductionRecordDTO.class); + dto1.setGroupId(groupId); + dto1.setEvId(temp.getEvId()); + dto1.setDosage(temp.getDosage()); + dto1.setPsId(temp.getPsId()); + dto1.setThingCode(temp.getThingCode()); + dto1.setRatio(temp.getRatio()); + dto1.setPrStatus(1L); + dto1.setPrDur(prDur); + psIds.add(temp.getPsId()); + this.saveOrUpdateDtoInfo(dto1); + }); + dto.getIndirectEnergyDTOS().forEach(temp->{ + temp.setPrCode(prCode); + temp.setGroupId(groupId); + temp.setStepsNum(psIds.size()); + temp.setPrStatus(1L); + temp.setM_id(dto.getM_id()); + temp.setPrType(dto.getPrType()); + iotCarbonIndirectEnergyService.saveDtoOrUpdateDtoInfo(temp); + }); + applicationEventPublisher.publishEvent(new ProductionProcessEvent(this,"2")); + } + + private void saveOrUpdateDtoInfo(IotCarbonProductionRecordDTO dto1) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",dto1.getM_id()); + wrapper.eq("pr_code",dto1.getPrCode()); + wrapper.eq("ps_id",dto1.getPsId()); + wrapper.eq("ev_id",dto1.getEvId()); + IotCarbonProductionRecordEntity recordEntity = this.getOne(wrapper); + if(ObjectUtils.isNotEmpty(recordEntity)){ + dto1.setId(recordEntity.getEvId()); + this.updateDto(dto1); + }else { + this.saveDto(dto1); + } + } + + @Override + public void updateProductionParam(ProductionParam dto) { + Long groupId= dto.getGroupId(); + Set psIds = new HashSet<>(); + dto.getOrderParams().forEach(temp->{ + IotCarbonProductionRecordDTO dto1 = ConvertUtils.sourceToTarget(dto,IotCarbonProductionRecordDTO.class); + dto1.setId(temp.getId()); + dto1.setEvId(temp.getEvId()); + dto1.setDosage(temp.getDosage()); + dto1.setPsId(temp.getPsId()); + dto1.setThingCode(temp.getThingCode()); + dto1.setRatio(temp.getRatio()); + dto1.setGroupId(groupId); + dto1.setPrStatus(1L); + psIds.add(temp.getPsId()); + this.updateById(ConvertUtils.sourceToTarget(dto1, IotCarbonProductionRecordEntity.class)); + }); + dto.getIndirectEnergyDTOS().forEach(temp->{ + if(ObjectUtils.isEmpty(temp.getId())){ + temp.setPrCode(dto.getPrCode()); + temp.setGroupId(groupId); + temp.setStepsNum(psIds.size()); + temp.setPrStatus(1L); + temp.setM_id(dto.getM_id()); + temp.setPrType(dto.getPrType()); + iotCarbonIndirectEnergyService.saveDto(temp); + }else { + temp.setGroupId(groupId); + temp.setStepsNum(psIds.size()); + temp.setPrStatus(1L); + iotCarbonIndirectEnergyService.updateById(ConvertUtils.sourceToTarget(temp, IotCarbonIndirectEnergyEntity.class)); + } + + }); + applicationEventPublisher.publishEvent(new ProductionProcessEvent(this,"2")); + } + + @Override + public List getListByStatus(String status) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("pr_status",Long.valueOf(status)); + return this.list(wrapper); + } + + @Override + public List getListByParam(Long mId, String prCode, Long groupId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId); + wrapper.eq("pr_code",prCode); + wrapper.eq("group_id",groupId); + return this.list(wrapper); + } + + @Override + public List getRecordReqList() { + return this.mapper.getRecordReqList(); + } + + @Override + public void updateByMidAndPrCode(Long mId, String prCode) { + this.mapper.updateByMidAndPrCode(mId,prCode); + } + + @Override + public List getRecordReqListByStatus() { + return this.mapper.getRecordReqListByStatus(); + } + + @Override + public void updatePrStatus(Long prStatus, Long mId, String prCode) { + this.mapper.updatePrStatus(prStatus,mId,prCode); + } + + + + @Override + public List productionListByMid(Long mId) { + List reqs = mapper.productionListByMid(mId); + //获取已出库数量 + reqs.forEach(temp->{ + Long outboundNum = carbonOutboundConfigMapper.getOutboundNumByMidAndPrCode(mId,temp.getPrCode()); + temp.setOutboundNum(outboundNum==null?0:outboundNum); + }); + return reqs; + } + + @Override + public void updatePrStatusByTimeAndMid(RecalculateByTimeDTO dto) { + this.mapper.updatePrStatusByTimeAndMid(dto.getMId(),dto.getStartTime(),dto.getEndTime()); + } + + @Override + public QuantityDto getQuantityByMidAndPrCode(Long mId, String prCode) { + return this.mapper.getQuantityByMidAndPrCode(mId,prCode); + } + + @Override + public String getPrTypeByMidAndPrCode(Long mId, String prCode) { + return this.mapper.getPrTypeByMidAndPrCode(mId,prCode); + } + + + /** + * 构建 OrderParam + * @param stepsDTO + * @param evId + * @param param + */ + private void buildOrderParam(IotCarbonProcessStepsDTO stepsDTO,String evId,OrderParam param){ + param.setPsId(stepsDTO.getId()); + param.setPsCode(stepsDTO.getProcessCode()); + param.setPsName(stepsDTO.getProcessName()); + param.setEvId(Long.valueOf(evId)); + CarbonEnergyVarietyEntity varietyEntity = carbonEnergyVarietyService.getById(Long.valueOf(evId)); + if(ObjectUtils.isNotEmpty(varietyEntity)){ + param.setEvCode(varietyEntity.getCode()); + param.setEvName(varietyEntity.getName()); + } + } + + + /** + * List reqs 按psId+evId 分组 每组只保留一条数据 + * @param reqs + * @return + */ + private Map groupByPidAndEvId(List reqs){ + return reqs.stream() + .collect(Collectors.groupingBy( + orderParam -> orderParam.getPsId() + "-" + orderParam.getEvId(), + Collectors.collectingAndThen( + Collectors.toList(), + group -> group.get(0) + ) + )); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/controller/IotCarbonProductionResultController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/controller/IotCarbonProductionResultController.java new file mode 100644 index 0000000..2899aba --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/controller/IotCarbonProductionResultController.java @@ -0,0 +1,152 @@ +package com.thing.carbontrack.productionResult.controller; + +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.common.core.enumeration.CarbonLifecycleEnum; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +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; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** +* 生产记录核算结果 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/productionResult/iotcarbonproductionresult") +@Tag(name="AAA生产记录核算结果") +@RequiredArgsConstructor +public class IotCarbonProductionResultController { + + private final IotCarbonProductionResultService iotCarbonProductionResultService; + + @GetMapping("simple/result") + @Operation(summary="批次产品碳足迹-简单汇总") + @Parameters({ + @Parameter(name = "productId", description = "产品Id", required = true), + @Parameter(name = "start", description = "开始时间:yyyy-MM-dd HH:mm:ss", required = true), + @Parameter(name = "end", description = "结束时间: yyyy-MM-dd HH:mm:ss", required = true), + @Parameter(name = "shareStatus", description = "共享状态:1-未共享,2-已共享"), + }) + public Result> getSimpleAggResult(@Parameter(hidden = true) @RequestParam Map params){ + Long productId = MapUtils.getLong(params, "productId"); + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + String shareStatus = MapUtils.getString(params, "shareStatus"); + if (Objects.isNull(productId)) { + return new Result>().ok(Collections.emptyList()); + } + List res = iotCarbonProductionResultService.getSimpleAggResult(productId, start, end, shareStatus); + return new Result>().ok(res); + } + + + @GetMapping("single/lot/detail") + @Operation(summary="批次产品碳足迹-单批次各生命周期数据") + @Parameters({ + @Parameter(name = "productId", description = "产品Id", required = true), + @Parameter(name = "prCode", description = "生产编号", required = true), + @Parameter(name = "dataType", description = "展示数据类型:0-基础信息, 1-原材料采购与运输, 2-生产制造, 3-产品运输, 4-产品使用, 5-废弃处理", required = true), + }) + public Result getDetail(@Parameter(hidden = true) @RequestParam Map params){ + Long productId = MapUtils.getLong(params, "productId"); + String prCode = MapUtils.getString(params, "prCode"); + Integer dataType = MapUtils.getInteger(params, "dataType"); + if (Objects.isNull(productId) || Objects.isNull(prCode) || Objects.isNull(dataType)) { + throw new IllegalArgumentException("缺少参数"); + } + if (Objects.equals(dataType, 0)) { + LotCarbonBaseInfo res = iotCarbonProductionResultService.getDetailOfBaseInfo(productId, prCode); + return new Result<>().ok(res); + } + CarbonLifecycleEnum carbonLifecycle = CarbonLifecycleEnum.match(dataType); + switch (carbonLifecycle) { + case MATERIAL_PURCHASE_TRANSPORT -> { + LotCarbonMPT res = iotCarbonProductionResultService.getDetailOfMPT(productId, prCode, null, null); + return new Result<>().ok(res); + } + case PRODUCT_MANUFACTURE -> { + LotCarbonPM res = iotCarbonProductionResultService.getDetailOfPM(productId, prCode, null, null); + return new Result<>().ok(res); + } + case PRODUCT_TRANSPORT -> { + LotCarbonPT res = iotCarbonProductionResultService.getDetailOfPT(productId, prCode, null, null); + return new Result<>().ok(res); + } + case PRODUCT_USAGE -> { + LotCarbonPU res = iotCarbonProductionResultService.getDetailOfPU(productId, prCode, null, null); + return new Result<>().ok(res); + } + case DISPOSE -> { + LotCarbonDispose res = iotCarbonProductionResultService.getDetailOfDispose(productId, prCode, null, null); + return new Result<>().ok(res); + } + default -> { + return null; + } + } + } + + @GetMapping("batch/lot/detail") + @Operation(summary="碳足迹核算结果-多批次各生命周期数据") + @Parameters({ + @Parameter(name = "productId", description = "产品Id", required = true), + @Parameter(name = "start", description = "开始时间", required = true), + @Parameter(name = "end", description = "结束时间", required = true), + @Parameter(name = "dataType", description = "展示数据类型:0-基础信息, 1-原材料采购与运输, 2-生产制造, 3-产品运输, 4-产品使用, 5-废弃处理", required = true), + }) + public Result getBatchDetail(@Parameter(hidden = true) @RequestParam Map params){ + Long productId = MapUtils.getLong(params, "productId"); + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + Integer dataType = MapUtils.getInteger(params, "dataType"); + if (Objects.isNull(productId)) { + return new Result<>(); + } + if (Objects.equals(dataType, 0)) { + LotCarbonBaseInfoOnYear res = iotCarbonProductionResultService.getDetailOfBaseInfoOnYear(productId); + return new Result<>().ok(res); + } + CarbonLifecycleEnum carbonLifecycle = CarbonLifecycleEnum.match(dataType); + switch (carbonLifecycle) { + case MATERIAL_PURCHASE_TRANSPORT -> { + LotCarbonMPT res = iotCarbonProductionResultService.getDetailOfMPT(productId, null, start, end); + return new Result<>().ok(res); + } + case PRODUCT_MANUFACTURE -> { + LotCarbonPM res = iotCarbonProductionResultService.getDetailOfPM(productId, null, start, end); + return new Result<>().ok(res); + } + case PRODUCT_TRANSPORT -> { + LotCarbonPT res = iotCarbonProductionResultService.getDetailOfPT(productId, null, start, end); + return new Result<>().ok(res); + } + case PRODUCT_USAGE -> { + LotCarbonPU res = iotCarbonProductionResultService.getDetailOfPU(productId, null, start, end); + return new Result<>().ok(res); + } + case DISPOSE -> { + LotCarbonDispose res = iotCarbonProductionResultService.getDetailOfDispose(productId, null, start, end); + return new Result<>().ok(res); + } + default -> { + return null; + } + } + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/AggCarbon.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/AggCarbon.java new file mode 100644 index 0000000..ec34fc1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/AggCarbon.java @@ -0,0 +1,222 @@ +package com.thing.carbontrack.productionResult.dto; + +import static com.thing.common.core.enumeration.CarbonLifecycleEnum.*; +import static com.thing.common.core.utils.AggUtil.sum; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import org.apache.commons.collections4.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/6/7 14:27 + * @description 碳足迹聚合数据 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AggCarbon { + public static final String TOTAL_KEY = "产品碳足迹"; + public static final String MIN = "min"; + public static final String MAX = "max"; + public static final String AVG = "avg"; + public static final String SUM = "sum"; + + /** + * 碳足迹类型: + * 1 原料碳排,原料运输碳排 + * 2 生产 + * 3 公摊/ 间接 + * 4 产品运输 + * 5 产品使用 + * 6 产品废弃,产品废弃运输 + * 碳足报告上传的时候,其他类型,直接中文名称录入 + */ + private String carbonType; + private String prCode; + @JsonIgnore + private Long productId; + + private BigDecimal usageAvg; + private BigDecimal carbonAvg; + private BigDecimal fCarbonAvg; + + public AggCarbon( + String carbonType, + String prCode, + BigDecimal usageAvg, + BigDecimal carbonAvg, + BigDecimal fCarbonAvg) { + this.carbonType = carbonType; + this.prCode = prCode; + this.usageAvg = usageAvg; + this.carbonAvg = carbonAvg; + this.fCarbonAvg = fCarbonAvg; + } + + public BigDecimal getUsageAvg() { + return usageAvg == null ? null : usageAvg.setScale(2, RoundingMode.HALF_UP); + } + + public BigDecimal getCarbonAvg() { + return carbonAvg == null ? null : carbonAvg.setScale(2, RoundingMode.HALF_UP); + } + + public BigDecimal getFCarbonAvg() { + return fCarbonAvg == null ? null : fCarbonAvg.setScale(2, RoundingMode.HALF_UP); + } + + /** + * 原料获取:carbon = carbonType1.carbonAvg + * 生产制造:carbon = carbonType2.carbonAvg + carbonType3.carbonAvg + * 产品使用:carbon = carbonType5.carbonAvg + * 废弃处理:carbon = carbonType6.carbonAvg + * 产品运输:carbon = carbonType1.usageAvg + carbonType4.carbonAvg + carbonType6.fCarbonAvg + */ + public static Map> agg(List aggCarbonList) { + if (CollectionUtils.isEmpty(aggCarbonList)) { + return Collections.emptyMap(); + } + Map> res = new HashMap<>(3); + Map> prCodeGroup = aggCarbonList.stream().collect(Collectors.groupingBy(AggCarbon::getPrCode)); + List aggCarbonLifecycleList = new ArrayList<>(); + prCodeGroup.forEach( + (prCode, list) -> { + Map typeMap = + list.stream() + .collect( + Collectors.toMap( + AggCarbon::getCarbonType, + Function.identity(), + AggCarbon::add)); + AggCarbon resOfType1 = Optional.ofNullable(typeMap.get("1")).orElse(new AggCarbon()); + AggCarbon resOfType2 = Optional.ofNullable(typeMap.get("2")).orElse(new AggCarbon()); + AggCarbon resOfType3 = Optional.ofNullable(typeMap.get("3")).orElse(new AggCarbon()); + AggCarbon resOfType4 = Optional.ofNullable(typeMap.get("4")).orElse(new AggCarbon()); + AggCarbon resOfType5 = Optional.ofNullable(typeMap.get("5")).orElse(new AggCarbon()); + AggCarbon resOfType6 = Optional.ofNullable(typeMap.get("6")).orElse(new AggCarbon()); + + AggCarbonLifecycle aggCarbonLifecycle = new AggCarbonLifecycle(); + aggCarbonLifecycle.setPrCode(prCode); + aggCarbonLifecycle.setMptCarbon(resOfType1.getCarbonAvg()); + aggCarbonLifecycle.setPmCarbon(sum(resOfType2.getCarbonAvg(), resOfType3.getCarbonAvg())); + aggCarbonLifecycle.setPuCarbon(resOfType5.getCarbonAvg()); + aggCarbonLifecycle.setDisposeCarbon(resOfType6.getCarbonAvg()); + aggCarbonLifecycle.setPtCarbon(sum(resOfType1.getUsageAvg(), resOfType4.getCarbonAvg(), resOfType6.getFCarbonAvg())); + aggCarbonLifecycle.setTotalCarbon(aggCarbonLifecycle.summingTotal()); + + aggCarbonLifecycleList.add(aggCarbonLifecycle); + }); + aggCarbonLifecycleList.sort(Comparator.comparing(AggCarbonLifecycle::getTotalCarbon)); + AggCarbonLifecycle sumAggCarbon = + aggCarbonLifecycleList.stream() + .reduce(new AggCarbonLifecycle(), AggCarbonLifecycle::add, (x, y) -> x); + AggCarbonLifecycle minAggCarbon = aggCarbonLifecycleList.get(0); + AggCarbonLifecycle maxAggCarbon = aggCarbonLifecycleList.get(aggCarbonLifecycleList.size() - 1); + AggCarbonLifecycle avgAggCarbon = doAvg(aggCarbonLifecycleList); + + Map minMap = minAggCarbon.toMap(); + Map maxMap = maxAggCarbon.toMap(); + Map avgMap = avgAggCarbon.toMap(); + Map sumMap = sumAggCarbon.toMap(); + + res.put(MIN, minMap); + res.put(MAX, maxMap); + res.put(AVG, avgMap); + res.put(SUM, sumMap); + return res; + } + + @Data + @Accessors(chain = true) + public static class AggCarbonLifecycle { + private String prCode; + private BigDecimal totalCarbon; + + private BigDecimal mptCarbon; + private BigDecimal pmCarbon; + private BigDecimal puCarbon; + private BigDecimal disposeCarbon; + private BigDecimal ptCarbon; + + public BigDecimal summingTotal() { + return sum(mptCarbon, pmCarbon, puCarbon, disposeCarbon, ptCarbon); + } + + public Map toMap() { + Map map = new HashMap<>(); + map.put(MATERIAL_PURCHASE_TRANSPORT.getDesc(), mptCarbon); + map.put(PRODUCT_MANUFACTURE.getDesc(), pmCarbon); + map.put(PRODUCT_USAGE.getDesc(), puCarbon); + map.put(DISPOSE.getDesc(), disposeCarbon); + map.put(PRODUCT_TRANSPORT.getDesc(), ptCarbon); + map.put(TOTAL_KEY, totalCarbon); + return map; + } + + public AggCarbonLifecycle add(AggCarbonLifecycle other) { + setMptCarbon(sum(getMptCarbon(), other.getMptCarbon())); + setPmCarbon(sum(getPmCarbon(), other.getPmCarbon())); + setPuCarbon(sum(getPuCarbon(), other.getPuCarbon())); + setDisposeCarbon(sum(getDisposeCarbon(), other.getDisposeCarbon())); + setPtCarbon(sum(getPtCarbon(), other.getPtCarbon())); + setTotalCarbon(summingTotal()); + return this; + } + } + + private static AggCarbonLifecycle doAvg(List list){ + BigDecimal prCount = BigDecimal.valueOf(list.size()); + // 计算所有的总值 + AggCarbonLifecycle res = + list.stream() + .reduce( + new AggCarbonLifecycle(), + (prev, next) -> { + prev.setPrCode(next.getPrCode()); + prev.setTotalCarbon(sum(prev.getTotalCarbon(), next.getTotalCarbon())); + + prev.setMptCarbon(sum(prev.getMptCarbon(), next.getMptCarbon())); + prev.setPmCarbon(sum(prev.getPmCarbon(), next.getPmCarbon())); + prev.setPuCarbon(sum(prev.getPuCarbon(), next.getPuCarbon())); + prev.setDisposeCarbon(sum(prev.getDisposeCarbon(), next.getDisposeCarbon())); + prev.setPtCarbon(sum(prev.getPtCarbon(), next.getPtCarbon())); + return prev; + }, + (a, b) -> a); + + // 计算均值 + res.setMptCarbon(divide(res.getMptCarbon(), prCount)); + res.setPmCarbon(divide(res.getPmCarbon(), prCount)); + res.setPuCarbon(divide(res.getPuCarbon(), prCount)); + res.setDisposeCarbon(divide(res.getDisposeCarbon(), prCount)); + res.setPtCarbon(divide(res.getPtCarbon(), prCount)); + res.setTotalCarbon(res.summingTotal()); + + return res; + } + + private static BigDecimal divide(BigDecimal value, BigDecimal count) { + if (Objects.isNull(value)) { + return null; + } + return value.divide(count, 2, RoundingMode.HALF_UP); + } + + private AggCarbon add(AggCarbon other){ + this.setUsageAvg(sum(this.getUsageAvg(), other.getUsageAvg())); + this.setCarbonAvg(sum(this.getCarbonAvg(), other.getCarbonAvg())); + this.setFCarbonAvg(sum(this.getFCarbonAvg(), other.getFCarbonAvg())); + return this; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/DisposeDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/DisposeDetail.java new file mode 100644 index 0000000..ccb70f6 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/DisposeDetail.java @@ -0,0 +1,61 @@ +package com.thing.carbontrack.productionResult.dto; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 废弃处理详情(dispose) + */ +@Data +public class DisposeDetail { + + /** 产品编码 */ + private String productCode; + /** 产品名称 */ + private String productName; + /** 产品单位 */ + private String productUnit; + /** 销售数量 */ + private Long salesNum; + /** 产品重量 */ + private BigDecimal productWight; + /** 产品重量单位 */ + private String productWightUnit; + /** 运输方式Id */ + private Long transportId; + /** 运输方式 */ + private String transportType; + /** 运输距离单位 */ + private String transportUnit; + /** 运输距离 */ + private BigDecimal transportDistance; + /**运输碳排总量*/ + private BigDecimal transportCarBon; + /**单件运输碳排*/ + private BigDecimal transportCarBonAvg; + /**运输对应的排放因子库值*/ + private BigDecimal transportRatio; + + + + + + + /** 能源品种id */ + private Long evId; + /** 能源品种 */ + private String evName; + /** 能源品种单位 */ + private String evUnit; + /** 能源用量 */ + private BigDecimal evUsage; + /** 能源用量平均值 */ + private BigDecimal evUsageAvg; + /**能源用量碳排*/ + private BigDecimal evUsageCarbon; + /** 能源用量碳排平均值 */ + private BigDecimal evUsageCarbonAvg; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/IotCarbonProductionResultDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/IotCarbonProductionResultDTO.java new file mode 100644 index 0000000..bc27def --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/IotCarbonProductionResultDTO.java @@ -0,0 +1,126 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.Optional; + +/** +* 生产记录核算结果 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "生产记录核算结果") +public class IotCarbonProductionResultDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "生产数量") + private Long num; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源品种名称") + private Long evName; + @Schema(description = "总用量") + private BigDecimal usage; + @Schema(description = "总碳排") + private BigDecimal carbon; + @Schema(description = "总能耗") + private BigDecimal tce; + @Schema(description = "详情json,一个类型,一个能源,形成一个详情json") + private String json; + @Schema(description = " 1 原料碳排,原料运输碳排\n" + + "\t 2 生产\n" + + "\t 3 公摊/间接\n" + + "\t 4 产品运输\n" + + "\t 5 产品使用\n" + + "\t 6 产品废弃,产品废弃运输\n" + + "\t 碳足报告上传得时候,其他类型,直接中文名称录入") + private String carbonType; + @Schema(description = "结果类型 1结果数据 2过程数据") + private String resultType; + @Schema(description = "产品核算边界 1.摇篮到大门,2摇篮到坟墓") + private String boundary; + @Schema(description = "工单开始时间") + private Date startTime; + @Schema(description = "工单完工时间") + private Date finishTime; + @Schema(description = "生产耗时") + private Integer prDur; + @Schema(description = "产品计量单位") + private String fUnit; + @Schema(description = "产品重量") + private BigDecimal p_weight; + @Schema(description = "产品重量计量单位") + private String w_unit; + @Schema(description = "产品单件用能") + private BigDecimal usage_avg; + @Schema(description = "产品单件碳排") + private BigDecimal carbon_avg; + @Schema(description = "产成品数量") + private Long finalNum; + @Schema(description = "同步状态,1未同步,2已同步 发生修改,反转为未同步") + private String sync_status; + @Schema(description = "物料名称") + private String m_name; + @Schema(description = "物料编码") + private String m_code; + @Schema(description = "物料型号") + private String m_model; + @Schema(description = "产品运输的时候,提交的数据组id 同一组数据 数量只聚合一次") + private Long group_id; + @Schema(description = "废弃运输里程") + private BigDecimal f_usage; + @Schema(description = "废弃运输碳排") + private BigDecimal f_carbon; + @Schema(description = "废弃运输里程平均值,不平均,= 废弃运输里程") + private BigDecimal f_usage_avg; + @Schema(description = "废弃运输碳排平均值") + private BigDecimal f_carbon_avg; + @Schema(description = "废弃运输类型id") + private Long f_ev_id; + @Schema(description = "废弃运输类型名称") + private String f_ev_name; + @Schema(description = "使用年限") + private Integer life; + + public BigDecimal getUsage_avg() { + return Optional.ofNullable(usage_avg).orElse(BigDecimal.ZERO); + } + + public BigDecimal getCarbon_avg() { + return Optional.ofNullable(carbon_avg).orElse(BigDecimal.ZERO); + } + + public BigDecimal getF_carbon_avg() { + return Optional.ofNullable(f_carbon_avg).orElse(BigDecimal.ZERO); + } + + public Integer getPrDur() { + return Optional.ofNullable(prDur).orElse(0); + } + + public Long getNum() { + return Optional.ofNullable(num).orElse(0L); + } + + public Long getFinalNum() { + return Optional.ofNullable(finalNum).orElse(0L); + } + + public BigDecimal getCarbon() { + return Optional.ofNullable(carbon).orElse(BigDecimal.ZERO); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfo.java new file mode 100644 index 0000000..8423698 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfo.java @@ -0,0 +1,54 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-基础信息 + */ +@Data +@Schema(description = "批次产品碳足迹-基础信息") +public class LotCarbonBaseInfo { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品编号(料号)") + private String productCode; + + @Schema(description = "工单编号") + private String prCode; + + @Schema(description = "生产工时") + private Integer prDur; + + @Schema(description = "完工时间") + private String finishTime; + + @Schema(description = "批次产量") + private Long num; + + @Schema(description = "产品碳足迹总值") + private BigDecimal carbonTotal; + + @Schema(description = "产品碳足迹各生命周期的值") + private Map carbonLifeCycleMap; + + + public Integer getPrDur() { + return Optional.ofNullable(prDur).orElse(0); + } + + public Long getNum() { + return Optional.ofNullable(num).orElse(0L); + } + + public BigDecimal getCarbonTotal() { + return Optional.ofNullable(carbonTotal).orElse(BigDecimal.ZERO); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfoOnYear.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfoOnYear.java new file mode 100644 index 0000000..b9cc356 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonBaseInfoOnYear.java @@ -0,0 +1,71 @@ +package com.thing.carbontrack.productionResult.dto; + +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Map; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-当年碳足迹基础数据汇总 + */ +@Data +@Schema(description = "批次产品碳足迹-当年碳足迹基础数据汇总") +public class LotCarbonBaseInfoOnYear { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品图片url") + private String productUrl; + + @Schema(description = "所属行业") + private String industryType; + + @Schema(description = "产品分类") + private String productType; + + @Schema(description = "产品尺寸") + private String productionSize; + + @Schema(description = "产品尺寸单位") + private String productionSizeUnit; + + @Schema(description = "产品重量") + private BigDecimal productionWeight; + + @Schema(description = "产品重量单位") + private String productionWeightUnit; + + @Schema(description = "功能单位") + private String functionUnit; + + @Schema(description = "核算边界:1.摇篮到大门,2摇篮到坟墓") + private String boundary; + + @Schema(description = "今年平均产品碳足迹") + private Map carbonAvgMap; + + @Schema(description = "今年最大产品碳足迹") + private Map carbonMaxMap; + + @Schema(description = "今年最小产品碳足迹") + private Map carbonMinMap; + + public static LotCarbonBaseInfoOnYear init(IotCarbonProductionVarietyEntity production){ + LotCarbonBaseInfoOnYear res = new LotCarbonBaseInfoOnYear(); + res.setProductName(production.getName()); + res.setProductUrl(production.getUrl()); + res.setIndustryType(production.getIndustrySub()); + res.setProductType(production.getType()); + res.setProductionSize(production.getPSize()); + res.setProductionSizeUnit(production.getPSizeUnit()); + res.setProductionWeight(production.getPWeight()); + res.setProductionWeightUnit(production.getWUnit()); + res.setFunctionUnit(production.getFUnit()); + res.setBoundary(production.getBoundary()); + return res; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonDispose.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonDispose.java new file mode 100644 index 0000000..55bb75a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonDispose.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-废弃处理 + */ +@Data +@Schema(description = "批次产品碳足迹-废弃处理") +public class LotCarbonDispose { + @Schema(description = "基础信息") + private List baseInfoList; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonMPT.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonMPT.java new file mode 100644 index 0000000..850cbfe --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonMPT.java @@ -0,0 +1,21 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-原料采购与运输 + */ +@Data +@Schema(description = "批次产品碳足迹-原料采购与运输") +public class LotCarbonMPT { + @Schema(description = "基础信息") + private List baseInfoList; + + @Schema(description = "详情") + private List details; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPM.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPM.java new file mode 100644 index 0000000..d9077ac --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPM.java @@ -0,0 +1,23 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-生产制造 + */ +@Data +public class LotCarbonPM { + @Schema(description = "基础信息列表") + private List baseInfoList; + + @Schema(description = "生产消耗详情") + private List productDetails; + + @Schema(description = "公摊/间接消耗详情") + private List shareDetails; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPT.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPT.java new file mode 100644 index 0000000..ee717b4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPT.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-产品运输 + */ +@Data +@Schema(description = "批次产品碳足迹-产品运输") +public class LotCarbonPT { + @Schema(description = "基础信息") + private List baseInfoList; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPU.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPU.java new file mode 100644 index 0000000..a245be9 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonPU.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/6/6 11:17 + * @description 批次产品碳足迹-产品使用 + */ +@Data +@Schema(description = "批次产品碳足迹-产品使用") +public class LotCarbonPU { + @Schema(description = "基础信息") + private List baseInfoList; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonR.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonR.java new file mode 100644 index 0000000..4097c5d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonR.java @@ -0,0 +1,205 @@ +package com.thing.carbontrack.productionResult.dto; + +import com.alibaba.fastjson.JSONArray; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; + +import com.thing.common.core.utils.AggUtil; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.util.Objects; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/6/6 13:45 + * @description 批次产品碳足迹-生产核算结果 + */ +@Data +@NoArgsConstructor +@SuppressWarnings("Duplicates") +@Schema(description = "批次产品碳足迹-生产核算结果") +public class LotCarbonR { + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品编码") + private String productCode; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "产品重量") + private BigDecimal productWeight; + + @Schema(description = "产品功能单位") + private String functionUnit; + + @Schema(description = "产品重量单位") + private String weightUnit; + + @Schema(description = "工单号") + private String prCode; + + @Schema(description = "生产时长,单位:h") + private Integer prDur; + + @Schema(description = "生产数量") + private Long num; + + @Schema(description = "成品(良品)数量") + private Long finalNum; + + @Schema(description = "开工时间: yyyy-MM-dd HH:mm:ss") + private String startTime; + + @Schema(description = "完工时间: yyyy-MM-dd HH:mm:ss") + private String finishTime; + + @Schema(description = "能源品种id|运输方式id") + private Long evId; + + @Schema(description = "能源品种|运输方式") + private String evName; + + @Schema(description = "碳排类型") + private String carbonType; + + @Schema(description = "碳排放 kgCO2e") + private BigDecimal carbon; + + @Schema(description = "平均碳排放 kgCO2e") + private BigDecimal carbonAvg; + + @Schema(description = "能耗,注:在carbonType=1时,该字段表示运输碳排") + private BigDecimal usage; + + @Schema(description = "能耗平均用量,注:在carbonType=1时,该字段表示平均运输碳排") + private BigDecimal usageAvg; + + @Schema(description = "目前仅用于产品运输页,用于标识合并时是否聚合数据(前端逻辑)") + private Long groupId; + + @Schema(description = "使用年限") + private Integer life; + + @Schema(description = "核算详情") + private JSONArray details; + + /* ---------------------- 废弃运输 ---------------------- */ + @Schema(description = "废弃运输类型id") + private Long fqEvId; + + @Schema(description = "废弃运输类型名称") + private String fqEvName; + + @Schema(description = "废弃运输里程") + private BigDecimal fqUsage; + + @Schema(description = "废弃运输里程平均值") + private BigDecimal fqUsageAvg; + + @Schema(description = "废弃运输碳排") + private BigDecimal fqCarbon; + + @Schema(description = "废弃运输碳排平均值") + private BigDecimal fqCarbonAvg; + + + public LotCarbonR(IotCarbonProductionResultEntity result) { + if (Objects.nonNull(result)) { + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + this.productName = result.getM_name(); + this.productCode = result.getM_code(); + this.productModel = result.getM_model(); + this.productWeight = result.getP_weight(); + this.functionUnit = result.getFUnit(); + this.weightUnit = result.getW_unit(); + this.prCode = result.getPrCode(); + this.prDur = result.getPrDur(); + this.num = result.getNum(); + this.finalNum = result.getFinalNum(); + this.startTime = sdf.format(result.getStartTime()); + this.finishTime = sdf.format(result.getFinishTime()); + this.evId = result.getEvId(); + this.evName = result.getEvName(); + this.carbonType = result.getCarbonType(); + this.carbon = round(result.getCarbon()); + this.carbonAvg = round(result.getCarbon_avg()); + this.usage = result.getUsage(); + this.usageAvg = result.getUsage_avg(); + this.groupId = result.getGroup_id(); + this.fqUsage = result.getF_usage(); + this.fqCarbon = result.getF_carbon(); + this.fqUsageAvg = result.getF_usage_avg(); + this.fqCarbonAvg = result.getF_carbon_avg(); + this.fqEvId = result.getF_ev_id(); + this.fqEvName = result.getF_ev_name(); + this.life = result.getLife(); + if (StringUtils.isNotBlank(result.getJson())) { + this.details = JSONArray.parseArray(result.getJson()); + } + } + } + + private BigDecimal round(BigDecimal value){ + return Optional.ofNullable(value).orElse(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_UP); + } + + public LotCarbonR add(LotCarbonR other){ + if (!Objects.equals(getProductCode(), other.getProductCode())) { + return this; + } + setNum(AggUtil.sum(getNum(), other.getNum())); + setFinalNum(AggUtil.sum(getFinalNum(), other.getFinalNum())); + setPrDur(AggUtil.sum(getPrDur(), other.getPrDur())); + setCarbon(AggUtil.sum(getCarbon(), other.getCarbon())); + setUsage(AggUtil.sum(getUsage(), other.getUsage())); + getDetails().addAll(other.getDetails()); + return this; + } + + public LotCarbonR refreshAvg() { + refreshCarbonAvg(); + refreshUsageAvg(); + refreshFqCarbonAvg(); + refreshFqUsageAvg(); + return this; + } + + public LotCarbonR refreshCarbonAvg() { + setCarbonAvg(divide(getCarbon(), BigDecimal.valueOf(getFinalNum()))); + return this; + } + + public LotCarbonR refreshUsageAvg() { + setUsageAvg(divide(getUsage(), BigDecimal.valueOf(getFinalNum()))); + return this; + } + + public void refreshFqCarbonAvg() { + setFqCarbonAvg(divide(getFqCarbon(), BigDecimal.valueOf(getFinalNum()))); + } + + public void refreshFqUsageAvg() { + setFqUsageAvg(divide(getFqUsage(), BigDecimal.valueOf(getFinalNum()))); + } + + public LotCarbonR clearDetail() { + setDetails(null); + return this; + } + + private static BigDecimal divide(BigDecimal value, BigDecimal count) { + if (Objects.isNull(value)) { + return null; + } + return value.divide(count, 2, RoundingMode.HALF_UP); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonSimpleAgg.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonSimpleAgg.java new file mode 100644 index 0000000..dfbe573 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/LotCarbonSimpleAgg.java @@ -0,0 +1,101 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/6/7 10:35 + * @description 批次产品碳足迹-简单聚合结果 + */ +@Data +@Schema(description = "批次产品碳足迹-简单聚合结果") +@NoArgsConstructor +public class LotCarbonSimpleAgg { + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品型号") + private String productModel; + + @Schema(description = "产品功能单位") + private String functionUnit; + + @Schema(description = "工单号") + private String prCode; + + @Schema(description = "完工时间: yyyy-MM-dd") + private String finishTime; + + @Schema(description = "生产数量") + private Long num; + + @Schema(description = "核算边界") + private String boundary; + + @Schema(description = "数据类型") + private String resultType; + + @Schema(description = "碳足迹值,kgCO2e") + private BigDecimal carbon; + + public LotCarbonSimpleAgg(List resultDTOList) { + IotCarbonProductionResultDTO resultDTO = resultDTOList.get(0); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + this.productId = resultDTO.getM_id(); + this.productName = resultDTO.getM_name(); + this.productModel = resultDTO.getM_model(); + this.functionUnit = resultDTO.getFUnit(); + this.prCode = resultDTO.getPrCode(); + this.finishTime = sdf.format(resultDTO.getFinishTime()); + this.num = resultDTO.getFinalNum(); + this.boundary = resultDTO.getBoundary(); + this.resultType = resultDTO.getResultType(); + this.carbon = calculateCarbon(resultDTOList); + + } + + @SuppressWarnings("Duplicates") + private BigDecimal calculateCarbon(List resultDTOList) { + if(CollectionUtils.isEmpty(resultDTOList)){ + return BigDecimal.ZERO; + } + Map carbonTypeMap = + resultDTOList.stream() + .collect( + Collectors.toMap( + IotCarbonProductionResultDTO::getCarbonType, + Function.identity(), + (v1, v2) -> v1)); + + IotCarbonProductionResultDTO resultOfType1 = Optional.ofNullable(carbonTypeMap.get("1")).orElse(new IotCarbonProductionResultDTO()); + IotCarbonProductionResultDTO resultOfType2 = Optional.ofNullable(carbonTypeMap.get("2")).orElse(new IotCarbonProductionResultDTO()); + IotCarbonProductionResultDTO resultOfType3 = Optional.ofNullable(carbonTypeMap.get("3")).orElse(new IotCarbonProductionResultDTO()); + IotCarbonProductionResultDTO resultOfType4 = Optional.ofNullable(carbonTypeMap.get("4")).orElse(new IotCarbonProductionResultDTO()); + IotCarbonProductionResultDTO resultOfType5 = Optional.ofNullable(carbonTypeMap.get("5")).orElse(new IotCarbonProductionResultDTO()); + IotCarbonProductionResultDTO resultOfType6 = Optional.ofNullable(carbonTypeMap.get("6")).orElse(new IotCarbonProductionResultDTO()); + + return resultOfType1.getCarbon_avg() + .add(resultOfType1.getUsage_avg()) + .add(resultOfType2.getCarbon_avg()) + .add(resultOfType3.getCarbon_avg()) + .add(resultOfType4.getCarbon_avg()) + .add(resultOfType5.getCarbon_avg()) + .add(resultOfType6.getCarbon_avg()) + .add(resultOfType6.getF_carbon_avg()); + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/MptDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/MptDetail.java new file mode 100644 index 0000000..c121743 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/MptDetail.java @@ -0,0 +1,71 @@ +package com.thing.carbontrack.productionResult.dto; + +import com.thing.common.core.utils.AggUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 原材料采购与运输详情(material_purchase_transport) + */ +@Data +@Schema(description = "原材料采购与运输详情") +public class MptDetail { + @Schema(description = "原料类别") + private String materialType; + + @Schema(description = "原料编码") + private String materialCode; + + @Schema(description = "原料名称") + private String materialName; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "物料用量") + private BigDecimal dosage; + + @Schema(description = "物料重量") + private BigDecimal weight; + + @Schema(description = "重量重量计量单位") + private String w_unit; + + @Schema(description = "原料获取碳排") + private BigDecimal acquireCarbon; + + @Schema(description = "供应商名称") + private String supplierName; + + @Schema(description = "供应商地址") + private String supplierAddress; + + @Schema(description = "运输方式") + private String transportType; + + @Schema(description = "运输距离") + private BigDecimal transportKm; + + @Schema(description = "运输碳排放") + private BigDecimal transportUse; + + private Long num; + + public MptDetail add(MptDetail other) { + if (Objects.equals(getMaterialCode(), other.getMaterialCode())) { + setDosage(AggUtil.sum(totalDosage(), other.totalDosage())); + setAcquireCarbon(AggUtil.sum(getAcquireCarbon(), other.getAcquireCarbon())); + setTransportUse(AggUtil.sum(getTransportUse(), other.getTransportUse())); + } + return this; + } + + private BigDecimal totalDosage(){ + return getDosage().multiply(BigDecimal.valueOf(num)); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PIndirectDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PIndirectDetail.java new file mode 100644 index 0000000..2fa5fb0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PIndirectDetail.java @@ -0,0 +1,67 @@ +package com.thing.carbontrack.productionResult.dto; + +import com.thing.common.core.utils.AggUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Optional; + +/** 间接用能碳排,详情json */ +@Data +@Schema(description = "公摊消耗与排放详情") +public class PIndirectDetail { + /** 工序序号 */ + @Schema(description = "工序序号") + private Integer orderNum; + + /** 工序名称 */ + @Schema(description = "工序名称") + private String processName; + + /** 工序编码 */ + @Schema(description = "工序编码") + private String processCode; + + @Schema(description = "能源品种id") + private Long evId; + + @Schema(description = "能源品种名称") + private String evName; + + @Schema(description = "当前工序的能耗") + private BigDecimal evUsage; + + @Schema(description = "当前工序在当前能源品种下的的碳排") + private BigDecimal carbon; + + @Schema(description = "当前工序的当前能源品种能耗平均值") + private BigDecimal evUsageAvg; + + @Schema(description = "当前工序在当前能源品种下的的碳排平均值") + private BigDecimal carbonAvg; + + @Schema(description = "碳排类型") + private String carbonType; + + @Schema(description = "过程序号") + private String ieCode; + + @Schema(description = "过程名称") + private String ieName; + + public BigDecimal getCarbonAvg(){ + return round(carbonAvg); + } + + private BigDecimal round(BigDecimal value){ + return Optional.ofNullable(value).orElse(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_UP); + } + + public PIndirectDetail add(PIndirectDetail other) { + setEvUsage(AggUtil.sum(getEvUsage(), other.getEvUsage())); + setCarbon(AggUtil.sum(getCarbon(), other.getCarbon())); + return this; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmDetail.java new file mode 100644 index 0000000..97a9760 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmDetail.java @@ -0,0 +1,38 @@ +package com.thing.carbontrack.productionResult.dto; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 生产制造详情(product_manufacture) + */ +@Data +public class PmDetail { + /** 工单号 */ + private String prCode; + + /** 生产开始时间:yyyy-MM-dd HH:mm:ss */ + private String startTime; + + /** 生产结束时间:yyyy-MM-dd HH:mm:ss */ + private String finishTime; + + /** 产品单位 */ + private String productUnit; + + /** 产品数量 */ + private Integer productNum; + + /** 良品数量 */ + private Integer goodProductNum; + + /** 能耗量 */ + private BigDecimal evUsage; + + /** 生产工序数据详情 */ + private List processDetailList; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmProcessDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmProcessDetail.java new file mode 100644 index 0000000..69409e8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PmProcessDetail.java @@ -0,0 +1,62 @@ +package com.thing.carbontrack.productionResult.dto; + +import com.thing.common.core.utils.AggUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 生产核算结果工序数据信息 + */ +@Data +@Schema(description = "生产消耗与排放详情") +public class PmProcessDetail { + @Schema(description = "工序序号") + private Integer orderNum; + + @Schema(description = "工序名称") + private String processName; + + @Schema(description = "工序编码") + private String processCode; + + @Schema(description = "能源品种id") + private Long evId; + + @Schema(description = "能源品种名称") + private String evName; + + @Schema(description = "当前工序的能耗") + private BigDecimal evUsage; + + @Schema(description = "当前工序在当前能源品种下的的碳排") + private BigDecimal carbon; + + @Schema(description = "当前工序的当前能源品种能耗平均值") + private BigDecimal evUsageAvg; + + @Schema(description = "当前工序在当前能源品种下的的碳排平均值") + private BigDecimal carbonAvg; + + @Schema(description = "碳排类型") + private String carbonType; + + public BigDecimal getCarbonAvg(){ + return round(carbonAvg); + } + + private BigDecimal round(BigDecimal value){ + return Optional.ofNullable(value).orElse(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_UP); + } + + public PmProcessDetail add(PmProcessDetail other){ + setEvUsage(AggUtil.sum(getEvUsage(), other.getEvUsage())); + setCarbon(AggUtil.sum(getCarbon(), other.getCarbon())); + return this; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PtDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PtDetail.java new file mode 100644 index 0000000..1ac9dfe --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PtDetail.java @@ -0,0 +1,63 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 产品运输详情(product_transport) + */ +@Data +@Schema(description = "产品运输详情") +public class PtDetail { + @Schema(description = "产品编号") + private String productCode; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品单位") + private String productUnit; + + @Schema(description = "产品重量") + private BigDecimal productWight; + + @Schema(description = "产品重量单位") + private String productWightUnit; + + @Schema(description = "工单编号") + private String prCode; + + @Schema(description = "销售数量") + private Integer salesNum; + + @Schema(description = "客户名称") + private String customerName; + + @Schema(description = "客户地址") + private String customerAddr; + + @Schema(description = "运输方式") + private String transportType; + + @Schema(description = "运输方式id") + private Long transportId; + + @Schema(description = "运输距离") + private BigDecimal transportDistance; + + @Schema(description = "运输距离单位") + private String transportUnit; + + @Schema(description = "运输碳排总量") + private BigDecimal transportCarBon; + + @Schema(description = "单件运输碳排") + private BigDecimal transportCarBonAvg; + + @Schema(description = "提交数据组id") + private Long groupId; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PuDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PuDetail.java new file mode 100644 index 0000000..00eef6b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/dto/PuDetail.java @@ -0,0 +1,52 @@ +package com.thing.carbontrack.productionResult.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author siyang + * @date 2024/5/31 13:10 + * @description 产品使用详情(product_usage) + */ +@Data +@Schema(description = "产品使用详情") +public class PuDetail { + @Schema(description = "产品编码") + private String productName; + + @Schema(description = "产品名称") + private String productCode; + + @Schema(description = "产品单位") + private String productUnit; + + @Schema(description = "工单完工时间") + private Date finishTime; + + @Schema(description = "能源品种id") + private Long evId; + + @Schema(description = "能源品种名称") + private String evName; + + @Schema(description = "销售数量") + private Long salesNum; + + @Schema(description = "产品使用年限") + private Integer serviceLife; + + @Schema(description = "消耗量") + private BigDecimal consumption; + + @Schema(description = "平均消耗量") + private BigDecimal consumptionAvg; + + @Schema(description = "产品使用导致的碳排量") + private BigDecimal usageCarbon; + + @Schema(description = "平均产品使用碳排") + private BigDecimal carbonAvg; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/entity/IotCarbonProductionResultEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/entity/IotCarbonProductionResultEntity.java new file mode 100644 index 0000000..1edaee8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/entity/IotCarbonProductionResultEntity.java @@ -0,0 +1,223 @@ +package com.thing.carbontrack.productionResult.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.keygen.KeyGenerators; +import com.thing.carbontrack.productionRecord.dto.ProductionPage; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.ObjectUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 生产记录核算结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_result") +public class IotCarbonProductionResultEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + /** + * 产品id + */ + private Long m_id; + /** + * 生产记录编码 + */ + private String prCode; + /** + * 生产数量 + */ + private Long num; + /** + * 能源品种id + */ + private Long evId; + /** + * 能源品种名称 + */ + private String evName; + /** + * 总用量 + */ + private BigDecimal usage; + /** + * 总碳排 + */ + private BigDecimal carbon; + /** + * 总能耗 + */ + private BigDecimal tce; + /** + 1 原料碳排,原料运输碳排 + 2 生产 + 3 公摊/间接 + 4 产品运输 + 5 产品使用 + 6 产品废弃,产品废弃运输 + 碳足报告上传得时候,其他类型,直接中文名称录入 + */ + private String carbonType; + /** + * 结果类型 1结果数据 2过程数据 + */ + private String resultType; + /** + * 产品核算边界 1.摇篮到大门,2摇篮到坟墓 + */ + private String boundary; + /** + * 工单开始时间 + */ + private Date startTime; + /** + * 工单完工时间 + */ + private Date finishTime; + /** + * 工单生产耗时,单位:h + */ + private Integer prDur; + + /** + * 详情json,一个类型,一个能源,形成一个详情json + */ + private String json; + /** + * 产品计量单位 + */ + private String fUnit; + + /** + * 产品重量 + */ + private BigDecimal p_weight; + /** + * 产品重量计量单位 + */ + private String w_unit; + /** + * 产品单件用能 + */ + private BigDecimal usage_avg; + /** + *产品单件碳排 + */ + private BigDecimal carbon_avg; + /** + *产成品数量 + */ + private Long finalNum; + /** + * 同步状态 1 未同步,2已同步 + */ + private String sync_status; + /** + * 物料名称 + */ + private String m_name; + /** + * 物料编码 + */ + private String m_code; + /** + * 物料型号 + */ + private String m_model; + /** + * 产品运输的时候,提交的数据组id 同一组数据 数量只聚合一次 + */ + private Long group_id; + /** + * 废弃运输里程 + */ + private BigDecimal f_usage; + /** + * 废弃运输碳排 + */ + private BigDecimal f_carbon; + /** + * 废弃运输里程平均值,不平均,= 废弃运输里程 + */ + private BigDecimal f_usage_avg; + /** + * 废弃运输碳排平均值 + */ + private BigDecimal f_carbon_avg; + /** + * 废弃运输类型id + */ + private Long f_ev_id; + /** + * 废弃运输类型名称 + */ + private String f_ev_name; + /** + * 使用年限 + */ + private Integer life; + /** + * + * @param info 基本信息 + * @param type + * @param carbon + * @param usage + * @param usage_avg + * @param carbon_avg + * @return + */ + public static IotCarbonProductionResultEntity initDto(ProductionPage info,String type,BigDecimal carbon,BigDecimal usage, + BigDecimal usage_avg, BigDecimal carbon_avg){ + IotCarbonProductionResultEntity entity = new IotCarbonProductionResultEntity(); + entity.setM_id(info.getMId()); + entity.setPrCode(info.getPrCode()); + entity.setNum(info.getPNum()); + entity.setFinalNum(info.getFinalNum()); + entity.setStartTime(info.getStartTime()); + entity.setFinishTime(info.getEndTime()); + entity.setBoundary(info.getBoundary()); + entity.setFUnit(info.getFUnit()); + entity.setW_unit(info.getWUnit()); + entity.setP_weight(info.getPWeight()); + if(ObjectUtils.isEmpty(info.getPrDur())){ + long prDur = (info.getEndTime().getTime() - info.getStartTime().getTime())/ (1000 * 60 * 60); + entity.setPrDur((int) prDur); + }else { + entity.setPrDur(info.getPrDur().intValue()); + } + + entity.setSync_status("1"); + entity.setEvId(info.getEvId()); + entity.setEvName(info.getEvName()); + entity.setM_name(info.getMName()); + entity.setM_code(info.getMCode()); + entity.setM_model(info.getMModel()); + entity.setGroup_id(info.getGroupId()); + + entity.setUsage(usage); + entity.setUsage_avg(usage_avg); + entity.setCarbon(carbon); + entity.setCarbon_avg(carbon_avg); + entity.setCarbonType(type); + return entity; + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/mapper/IotCarbonProductionResultMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/mapper/IotCarbonProductionResultMapper.java new file mode 100644 index 0000000..5b2eed6 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/mapper/IotCarbonProductionResultMapper.java @@ -0,0 +1,31 @@ +package com.thing.carbontrack.productionResult.mapper; + +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.screen.dto.SimpleProductionCarbon; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** + * 生产记录核算结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Mapper +public interface IotCarbonProductionResultMapper extends PowerBaseMapper { + + List getLatestCarbonRank( + @Param("keywords") String keywords, + @Param("carbonTypes") List carbonTypes, + @Param("start") Date start, + @Param("end") Date end); + + List getCarbonTrend( + @Param("productId") Long productId, @Param("carbonTypes") List carbonTypes); + + Long productCount(); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/IotCarbonProductionResultService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/IotCarbonProductionResultService.java new file mode 100644 index 0000000..79229b0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/IotCarbonProductionResultService.java @@ -0,0 +1,53 @@ +package com.thing.carbontrack.productionResult.service; + +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.screen.dto.ProductCarbonLifecycle; +import com.thing.carbontrack.screen.dto.ProductCarbonLifecycleDetail; +import com.thing.carbontrack.screen.dto.ProductInfo; +import com.thing.carbontrack.screen.dto.SimpleProductionCarbon; +import com.thing.common.orm.service.IBaseService; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 生产记录核算结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonProductionResultService extends IBaseService { + + List getSimpleAggResult(Long productId, String start, String end, String shareStatus); + + LotCarbonBaseInfo getDetailOfBaseInfo(Long productId, String prCode); + + LotCarbonBaseInfoOnYear getDetailOfBaseInfoOnYear(Long productId); + + LotCarbonMPT getDetailOfMPT(Long productId, String prCode, String start, String end); + + LotCarbonPM getDetailOfPM(Long productId, String prCode, String start, String end); + + LotCarbonPT getDetailOfPT(Long productId, String prCode, String start, String end); + + LotCarbonPU getDetailOfPU(Long productId, String prCode, String start, String end); + + LotCarbonDispose getDetailOfDispose(Long productId, String prCode, String start, String end); + + List getLatestCarbonRank(Map params); + + List getCarbonTrend(Long productId, String boundary); + + List getCarbonLifecycleOverview(Map params); + + List getCarbonLifecycleDetail(Map params); + + ProductInfo getCarbonProductInfo(Long productId, String prCode); + + IotCarbonProductionReportDTO generateReport(Long productId, Integer boundaryType, Date start, Date end, Long tenantCode); + + Long productCount(); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/impl/IotCarbonProductionResultServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/impl/IotCarbonProductionResultServiceImpl.java new file mode 100644 index 0000000..32ebc06 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/productionResult/service/impl/IotCarbonProductionResultServiceImpl.java @@ -0,0 +1,726 @@ +package com.thing.carbontrack.productionResult.service.impl; + +import static com.mybatisflex.core.query.QueryMethods.*; +import static com.thing.carbontrack.productionResult.entity.table.IotCarbonProductionResultEntityTableDef.IOT_CARBON_PRODUCTION_RESULT_ENTITY; +import static com.thing.common.core.enumeration.CarbonLifecycleEnum.*; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import com.thing.carbontrack.production.mapper.IotCarbonProductionVarietyMapper; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.productionResult.mapper.IotCarbonProductionResultMapper; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.report.dto.IotCarbonResultReport; +import com.thing.carbontrack.screen.dto.ProductCarbonLifecycle; +import com.thing.carbontrack.screen.dto.ProductCarbonLifecycleDetail; +import com.thing.carbontrack.screen.dto.ProductInfo; +import com.thing.carbontrack.screen.dto.SimpleProductionCarbon; +import com.thing.common.core.enumeration.CarbonLifecycleEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.service.SysTenantDetailService; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 生产记录核算结果 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +public class IotCarbonProductionResultServiceImpl + extends BaseServiceImpl + implements IotCarbonProductionResultService { + + private final IotCarbonProductionVarietyMapper carbonProductionVarietyMapper; + private final SysTenantDetailService sysTenantDetailService; + + + /** 碳足迹核算类型与生命周期对应关系 */ + private static final Map> CARBON_TYPE_GROUP = new HashMap<>(); + /** 碳足迹数据聚合计算的关系 */ + private static final Map> CARBON_AGG_GROUP = new HashMap<>(); + /** 系统边界对应的碳足迹核算类型 */ + private static final Map> BOUNDARY_CARBON_TYPE_GROUP = new HashMap<>(); + + static { + CARBON_TYPE_GROUP.put(MATERIAL_PURCHASE_TRANSPORT, Set.of("1")); + CARBON_TYPE_GROUP.put(PRODUCT_MANUFACTURE, Set.of("2", "3")); + CARBON_TYPE_GROUP.put(PRODUCT_TRANSPORT, Set.of("4")); + CARBON_TYPE_GROUP.put(PRODUCT_USAGE, Set.of("5")); + CARBON_TYPE_GROUP.put(DISPOSE, Set.of("6")); + } + + static { + CARBON_AGG_GROUP.put(MATERIAL_PURCHASE_TRANSPORT, Set.of("1")); + CARBON_AGG_GROUP.put(PRODUCT_MANUFACTURE, Set.of("2", "3")); + CARBON_AGG_GROUP.put(PRODUCT_TRANSPORT, Set.of("1","4","6")); + CARBON_AGG_GROUP.put(PRODUCT_USAGE, Set.of("5")); + CARBON_AGG_GROUP.put(DISPOSE, Set.of("6")); + } + + static { + BOUNDARY_CARBON_TYPE_GROUP.put("1", Arrays.asList("1", "2")); + BOUNDARY_CARBON_TYPE_GROUP.put("2", Arrays.asList("1", "2", "3", "4", "5", "6")); + } + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + @Override + @SneakyThrows + public List getSimpleAggResult(Long productId, String start, String end, String shareStatus) { + List res = new ArrayList<>(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + aggProductResult( + productId, + StringUtils.isBlank(start) ? null : sdf.parse(start), + StringUtils.isBlank(end) ? null : sdf.parse(end), + shareStatus, + IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_TYPE) + .stream() + .collect(Collectors.groupingBy(IotCarbonProductionResultDTO::getPrCode)) + .forEach((prCode, list) -> res.add(new LotCarbonSimpleAgg(list))); + res.sort( + Comparator.comparing(LotCarbonSimpleAgg::getProductName) + .thenComparing(LotCarbonSimpleAgg::getFinishTime)); + return res; + } + + /** + * 原料获取:carbon = carbonType1.carbonAvg + * 生产制造:carbon = carbonType2.carbonAvg + carbonType3.carbonAvg + * 产品使用:carbon = carbonType5.carbonAvg + * 废弃处理:carbon = carbonType6.carbonAvg + * 产品运输:carbon = carbonType1.usageAvg + carbonType4.carbonAvg + carbonType6.fCarbonAvg + */ + @Override + public LotCarbonBaseInfo getDetailOfBaseInfo(Long productId, String prCode) { + LotCarbonBaseInfo res = new LotCarbonBaseInfo(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + List productionResultList = + aggProductResult(productId, prCode, IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_TYPE); + if (CollectionUtils.isEmpty(productionResultList)) { + return res; + } + // 产品基本信息 + IotCarbonProductionResultDTO first = productionResultList.get(0); + res.setProductName(first.getM_name()); + res.setProductCode(first.getM_code()); + res.setPrCode(prCode); + + List aggCarbons = convert2AggCarbon(productionResultList); + Map carbonLifeCycleMap = AggCarbon.agg(aggCarbons).get(AggCarbon.MIN); + res.setCarbonLifeCycleMap(carbonLifeCycleMap); + + // 生产聚合信息 + for (IotCarbonProductionResultDTO productionResult : productionResultList) { + if (Objects.isNull(res.getFinishTime()) + && Objects.nonNull(productionResult.getFinishTime())) { + res.setFinishTime(sdf.format(productionResult.getFinishTime())); + } + res.setNum(productionResult.getFinalNum()); + res.setPrDur(res.getPrDur() + productionResult.getPrDur()); + } + BigDecimal totalCarbon = carbonLifeCycleMap.get(AggCarbon.TOTAL_KEY); + res.setCarbonTotal(totalCarbon); + + return res; + } + + /** + * 原料获取:carbonType1.carbonAvg + * 生产制造:carbonType2.carbonAvg + carbonType3.carbonAvg + * 产品使用:carbonType5.carbonAvg + * 废弃处理:carbonType6.carbonAvg + * 产品运输:carbonType1.usageAvg + carbonType4.carbonAvg + carbonType6.fCarbonAvg + */ + @Override + public LotCarbonBaseInfoOnYear getDetailOfBaseInfoOnYear(Long productId) { + IotCarbonProductionVarietyEntity production = carbonProductionVarietyMapper.selectOneById(productId); + if (Objects.isNull(production)) { + return new LotCarbonBaseInfoOnYear(); + } + LotCarbonBaseInfoOnYear res = LotCarbonBaseInfoOnYear.init(production); + Date start = new Date(DateTimeUtils.yearStartTs()); + Date end = new Date(DateTimeUtils.yearEndTs()); + + List aggCarbons = aggCarbon(productId, start, end); + Map> aggMap = AggCarbon.agg(aggCarbons); + + res.setCarbonAvgMap(aggMap.get(AggCarbon.AVG)); + res.setCarbonMaxMap(aggMap.get(AggCarbon.MAX)); + res.setCarbonMinMap(aggMap.get(AggCarbon.MIN)); + + return res; + } + + @Override + public LotCarbonMPT getDetailOfMPT(Long productId, String prCode, String start, String end) { + LotCarbonMPT res = new LotCarbonMPT(); + List list = listByCarbonLifecycle(MATERIAL_PURCHASE_TRANSPORT, productId, prCode, start, end); + if (CollectionUtils.isEmpty(list)) { + return res; + } + List lotCarbonList = list.stream().map(LotCarbonR::new).toList(); + LotCarbonR lotCarbonR = + lotCarbonList.stream() + .reduce(LotCarbonR::add) + .orElse(new LotCarbonR()) + .refreshCarbonAvg() + .refreshUsageAvg() + .clearDetail(); + + List detailList = new ArrayList<>(); + for (IotCarbonProductionResultEntity item : list) { + String detailJson = item.getJson(); + if(StringUtils.isBlank(detailJson)){ + continue; + } + List details = JSONArray.parseArray(detailJson, MptDetail.class); + details.forEach(e -> e.setNum(item.getFinalNum())); + detailList.addAll(details); + } + + List detailResults = + detailList.stream() + .collect( + Collectors.groupingBy( + MptDetail::getMaterialCode, + Collectors.collectingAndThen( + Collectors.toList(), + details -> + details.stream() + .reduce(MptDetail::add) + .orElse(new MptDetail())))) + .values() + .stream() + .toList(); + + res.setBaseInfoList(List.of(lotCarbonR)); + res.setDetails(detailResults); + return res; + } + + @Override + public LotCarbonPM getDetailOfPM(Long productId, String prCode, String start, String end) { + LotCarbonPM res = new LotCarbonPM(); + List list = listByCarbonLifecycle(PRODUCT_MANUFACTURE, productId, prCode, start, end); + if (CollectionUtils.isEmpty(list)) { + return res; + } + + List records = + list.stream() + .map(LotCarbonR::new) + .collect( + Collectors.groupingBy( + e -> e.getCarbonType() + "_" + e.getEvId(), + Collectors.collectingAndThen( + Collectors.toList(), + l -> + l.stream() + .reduce(LotCarbonR::add) + .orElse(new LotCarbonR()) + .refreshCarbonAvg() + .refreshUsageAvg()))) + .values() + .stream() + .toList(); + res.setBaseInfoList(records); + + // 按碳排类型分组 + Map> carbonTypeDetailMap = + records.stream() + .collect( + Collectors.groupingBy( + LotCarbonR::getCarbonType, + Collectors.mapping( + LotCarbonR::getDetails, Collectors.toList()))); + + // 生产消耗与排放 + List pmJsonArrayList = carbonTypeDetailMap.get("2"); + List pmProcessDetailResult = new ArrayList<>(); + List pmProcessDetails = + pmJsonArrayList.stream() + .map(arr -> arr.toJavaList(PmProcessDetail.class)) + .flatMap(Collection::stream) + .sorted(Comparator.comparing(PmProcessDetail::getOrderNum)) + .toList(); + pmProcessDetails.stream() + .collect( + Collectors.groupingBy( + e -> + e.getProcessName() + + "_" + + e.getProcessCode() + + "_" + + e.getEvId())) + .forEach( + (k, v) -> { + Optional opt = v.stream().reduce(PmProcessDetail::add); + opt.ifPresent(pmProcessDetailResult::add); + }); + res.setProductDetails(pmProcessDetailResult); + + // 公摊消耗与排放 + List shareJsonArrayList = carbonTypeDetailMap.get("3"); + List pIndirectDetailResult = new ArrayList<>(); + List pIndirectDetails = + shareJsonArrayList.stream() + .map(arr -> arr.toJavaList(PIndirectDetail.class)) + .flatMap(Collection::stream) + .sorted(Comparator.comparing(PIndirectDetail::getOrderNum)) + .toList(); + pIndirectDetails.stream() + .collect( + Collectors.groupingBy( + e -> + e.getProcessName() + + "_" + + e.getProcessCode() + + "_" + + e.getIeName() + + "_" + + e.getEvId())) + .forEach( + (k, v) -> { + Optional opt = v.stream().reduce(PIndirectDetail::add); + opt.ifPresent(pIndirectDetailResult::add); + }); + res.setShareDetails(pIndirectDetailResult); + + return res; + } + + @Override + public LotCarbonPT getDetailOfPT(Long productId, String prCode, String start, String end) { + LotCarbonPT res = new LotCarbonPT(); + List records = getRecords(PRODUCT_TRANSPORT, productId, prCode, start, end); + res.setBaseInfoList(records); + return res; + } + + @Override + public LotCarbonPU getDetailOfPU(Long productId, String prCode, String start, String end) { + LotCarbonPU res = new LotCarbonPU(); + List records = getRecords(PRODUCT_USAGE, productId, prCode, start, end); + res.setBaseInfoList(records); + return res; + } + + @Override + public LotCarbonDispose getDetailOfDispose(Long productId, String prCode, String start, String end) { + LotCarbonDispose res = new LotCarbonDispose(); + List records = getRecords(DISPOSE, productId, prCode, start, end); + res.setBaseInfoList(records); + return res; + } + + @Override + public List getLatestCarbonRank(Map params) { + String keywords = MapUtils.getString(params, "keywords"); + Integer month = MapUtils.getInteger(params, "month"); + String boundary = MapUtils.getString(params, "boundary"); + List carbonTypes = BOUNDARY_CARBON_TYPE_GROUP.get(boundary); + Date start, end; + if (Objects.nonNull(month)) { + // 指定月份查询 + LocalDateTime targetMonth = LocalDateTime.now().withMonth(month); + LocalDateTime monthStart = DateTimeUtils.getMonthStart(targetMonth); + LocalDateTime monthEnd = DateTimeUtils.getMonthEnd(targetMonth); + start = DateTimeUtils.toDate(monthStart); + end = DateTimeUtils.toDate(monthEnd); + return mapper.getLatestCarbonRank(keywords, carbonTypes, start, end); + } else { + // 默认查最近两个月,如果没数据,则全表查询 + LocalDateTime monthEnd = DateTimeUtils.getMonthEnd(LocalDateTime.now()); + LocalDateTime monthStart = DateTimeUtils.getMonthStart(monthEnd.minusMonths(1)); + start = DateTimeUtils.toDate(monthStart); + end = DateTimeUtils.toDate(monthEnd); + List result = mapper.getLatestCarbonRank(keywords, carbonTypes, start, end); + if (CollectionUtils.isEmpty(result)) { + return mapper.getLatestCarbonRank(keywords, carbonTypes, null, null); + } + return result; + } + } + + @Override + public List getCarbonTrend(Long productId, String boundary) { + List carbonTypes = BOUNDARY_CARBON_TYPE_GROUP.get(boundary); + List list = mapper.getCarbonTrend(productId, carbonTypes); + list.sort(Comparator.comparing(SimpleProductionCarbon::getPrDate)); + return list; + } + + @Override + public List getCarbonLifecycleOverview(Map params) { + Long productId = MapUtils.getLong(params, "productId"); + String prCode = MapUtils.getString(params, "prCode"); + String boundary = MapUtils.getString(params, "boundary"); + + if(ObjectUtils.isEmpty(boundary)){ + try { + boundary = carbonProductionVarietyMapper.selectOneById(productId).getBoundary(); + } catch (Exception e) { + boundary = "2"; + } + } + List carbonTypes = BOUNDARY_CARBON_TYPE_GROUP.get(boundary); + + List res = new ArrayList<>(); + List entities = listBy(productId, prCode, carbonTypes); + List dtoList = ConvertUtils.sourceToTarget(entities, IotCarbonProductionResultDTO.class); + List aggCarbons = convert2AggCarbon(dtoList); + Map carbonLifeCycleMap = AggCarbon.agg(aggCarbons).get(AggCarbon.MIN); + BigDecimal total = carbonLifeCycleMap.get(AggCarbon.TOTAL_KEY); + carbonLifeCycleMap.forEach( + (k, v) -> { + if (k.equals(AggCarbon.TOTAL_KEY) || Objects.isNull(v)) { + return; + } + ProductCarbonLifecycle lifecycle = new ProductCarbonLifecycle(productId, k, v); + lifecycle.setPercent( + v.multiply(BigDecimal.valueOf(100)) + .divide(total, 4, RoundingMode.HALF_UP)); + res.add(lifecycle); + }); + + return res; + } + + @Override + public List getCarbonLifecycleDetail(Map params) { + Long productId = MapUtils.getLong(params, "productId"); + String prCode = MapUtils.getString(params, "prCode"); + Integer stage = Optional.ofNullable(MapUtils.getInteger(params, "stage")).orElse(1); + String boundary = MapUtils.getString(params, "boundary"); + if(ObjectUtils.isEmpty(boundary)){ + try { + boundary = carbonProductionVarietyMapper.selectOneById(productId).getBoundary(); + } catch (Exception e) { + boundary = "2"; + } + } + List boundaryCarbonTypes = BOUNDARY_CARBON_TYPE_GROUP.get(boundary); + + CarbonLifecycleEnum carbonLifecycleEnum = match(stage); + if (Objects.isNull(carbonLifecycleEnum)) { + return List.of(); + } + Set aggCarbonTypes = CARBON_AGG_GROUP.get(carbonLifecycleEnum); + // 系统边界的碳足迹核算类型与本次指定生命周期的碳足迹核算类型取交集 + Set carbonTypes = + aggCarbonTypes.stream() + .filter(boundaryCarbonTypes::contains) + .collect(Collectors.toSet()); + if (CollectionUtils.isEmpty(carbonTypes)) { + return List.of(); + } + + List list = listBy(productId, prCode, carbonTypes); + return list.stream() + .map( + entity -> { + String detailJson = entity.getJson(); + if (StringUtils.isBlank(detailJson)) { + return null; + } + return convert2LifecycleDetail( + carbonLifecycleEnum, + productId, + detailJson, + entity.getCarbonType(), + entity.getFinalNum()); + }) + .filter(Objects::nonNull) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + @Override + public ProductInfo getCarbonProductInfo(Long productId, String prCode) { + IotCarbonProductionVarietyEntity product = carbonProductionVarietyMapper.selectOneById(productId); + return new ProductInfo(product); + } + + @Override + public IotCarbonProductionReportDTO generateReport(Long productId, Integer boundaryType, Date start, Date end, Long tenantCode) { + IotCarbonProductionReportDTO res = new IotCarbonProductionReportDTO(); + + List productionResultList = + aggProductResult(productId, null, IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_TYPE); + if (CollectionUtils.isEmpty(productionResultList)) { + return res; + } + String boundary = productionResultList.get(0).getBoundary(); + + List aggCarbons = convert2AggCarbon(productionResultList); + Map carbonLifeCycleMap = AggCarbon.agg(aggCarbons).get(AggCarbon.AVG); + + SysTenantDetailEntity tenant = sysTenantDetailService.getById(tenantCode); + IotCarbonResultReport resultReport = IotCarbonResultReport.convert(carbonLifeCycleMap); + + BigDecimal totalCarbon = carbonLifeCycleMap.get(AggCarbon.TOTAL_KEY); + carbonLifeCycleMap.remove(AggCarbon.TOTAL_KEY); + // 碳足迹各阶段占比 + JSONObject carbonStageValue = new JSONObject(); + if (totalCarbon.compareTo(BigDecimal.ZERO) > 0) { + carbonStageValue.put(MATERIAL_PURCHASE_TRANSPORT.getDesc(), resultReport.calcMptPercent()); + carbonStageValue.put(PRODUCT_MANUFACTURE.getDesc(), resultReport.calcPmPercent()); + carbonStageValue.put(PRODUCT_TRANSPORT.getDesc(), resultReport.calcPtPercent()); + carbonStageValue.put(PRODUCT_USAGE.getDesc(), resultReport.calcPuPercent()); + carbonStageValue.put(DISPOSE.getDesc(), resultReport.calcDisposePercent()); + } + + // 查询产品基本信息 + IotCarbonProductionVarietyEntity product = carbonProductionVarietyMapper.selectOneById(productId); + + // 报告开始时间为当前日期,有效期默认到1年后的昨天 + LocalDateTime now = LocalDateTime.now(); + LocalDateTime validEndTime = now.plusYears(1).minusDays(1); + + // 组装最终结果 + res.setProductId(productId); + res.setProductName(product.getName()); + res.setProductModel(product.getModel()); + res.setFunctionUnit(product.getFUnit()); + res.setBoundary(boundary); + res.setBoundaryType(boundaryType); + res.setBoundaryStart(start); + res.setBoundaryEnd(end); + res.setTotalCarbon(totalCarbon); + res.setStagePercentJson(carbonStageValue.toJSONString()); + res.setCreateDate(DateTimeUtils.parse(now)); + res.setValidEndTime(DateTimeUtils.parse(validEndTime)); + res.setCompanyName(tenant.getName()); + return res; + } + + @Override + public Long productCount() { + return mapper.productCount(); + } + + private List convert2AggCarbon(List list) { + return list.stream() + .map( + dto -> + new AggCarbon( + dto.getCarbonType(), + dto.getPrCode(), + dto.getUsage_avg(), + dto.getCarbon_avg(), + dto.getF_carbon_avg())) + .collect(Collectors.toList()); + } + + private List convert2LifecycleDetail(CarbonLifecycleEnum carbonLifecycleEnum, Long productId, String detailJson, String carbonType, Long finalNum){ + return switch (carbonLifecycleEnum) { + case MATERIAL_PURCHASE_TRANSPORT -> { + List details = JSONArray.parseArray(detailJson, MptDetail.class); + yield ProductCarbonLifecycleDetail.fromMpt(productId, details); + } + case PRODUCT_MANUFACTURE -> { + if (Objects.equals(carbonType, "2")) { + List details = JSONArray.parseArray(detailJson, PmProcessDetail.class); + yield ProductCarbonLifecycleDetail.fromPmProcess(productId, details, finalNum); + } else { + List details = JSONArray.parseArray(detailJson, PIndirectDetail.class); + yield ProductCarbonLifecycleDetail.fromPIndirect(productId, details, finalNum); + } + } + case PRODUCT_TRANSPORT -> { + switch (carbonType) { + case "1" -> { + List details = JSONArray.parseArray(detailJson, MptDetail.class); + yield ProductCarbonLifecycleDetail.fromMptForTransport(productId, details); + } + case "4" -> { + List details = JSONArray.parseArray(detailJson, PtDetail.class); + yield ProductCarbonLifecycleDetail.fromPt(productId, details); + } + case "6" -> { + List details = JSONArray.parseArray(detailJson, DisposeDetail.class); + yield ProductCarbonLifecycleDetail.fromDisposeForTransport(productId, details); + } + default -> { + yield null; + } + } + } + case PRODUCT_USAGE -> { + List details = JSONArray.parseArray(detailJson, PuDetail.class); + yield ProductCarbonLifecycleDetail.fromPu(productId, details, finalNum); + } + case DISPOSE -> { + List details = JSONArray.parseArray(detailJson, DisposeDetail.class); + yield ProductCarbonLifecycleDetail.fromDispose(productId, details); + } + }; + } + + private List listBy( + Long productId, String prCode, Collection carbonTypes) { + return list( + QueryWrapper.create() + .eq(IotCarbonProductionResultEntity::getM_id, productId) + .eq(IotCarbonProductionResultEntity::getPrCode, prCode) + .in( + IotCarbonProductionResultEntity::getCarbonType, + carbonTypes, + CollectionUtils.isNotEmpty(carbonTypes))); + } + + private List aggProductResult(Long productId, Date start, Date end, String shareStatus, QueryColumn... groupColumns) { + return aggProductResult(productId, null, start, end, shareStatus, groupColumns); + } + + private List aggProductResult(Long productId, String prCode, QueryColumn... groupColumns) { + return aggProductResult(productId, prCode, null, null, null, groupColumns); + } + + private List aggProductResult( + Long productId, String prCode, Date start, Date end, String shareStatus, QueryColumn... groupColumns) { + // 通用查询列 + Set defaultSelectColumns = new HashSet<>(); + defaultSelectColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_ID); + defaultSelectColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_CODE); + defaultSelectColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.PR_CODE); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_NAME).as(IotCarbonProductionResultDTO::getM_name)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_MODEL).as(IotCarbonProductionResultDTO::getM_model)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.F_UNIT).as(IotCarbonProductionResultDTO::getFUnit)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.BOUNDARY).as(IotCarbonProductionResultDTO::getBoundary)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.FINISH_TIME).as(IotCarbonProductionResultDTO::getFinishTime)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.NUM).as(IotCarbonProductionResultDTO::getNum)); + defaultSelectColumns.add(min(IOT_CARBON_PRODUCTION_RESULT_ENTITY.FINAL_NUM).as(IotCarbonProductionResultDTO::getFinalNum)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.USAGE).as(IotCarbonProductionResultDTO::getUsage)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON).as(IotCarbonProductionResultDTO::getCarbon)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_AVG).as(IotCarbonProductionResultDTO::getCarbon_avg)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.USAGE_AVG).as(IotCarbonProductionResultDTO::getUsage_avg)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.F_CARBON_AVG).as(IotCarbonProductionResultDTO::getF_carbon_avg)); + defaultSelectColumns.add(sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.PR_DUR).as(IotCarbonProductionResultDTO::getPrDur)); + + // 通用分组列 + Set defaultGroupColumns = new HashSet<>(); + defaultGroupColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_ID); + defaultGroupColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.M_CODE); + defaultGroupColumns.add(IOT_CARBON_PRODUCTION_RESULT_ENTITY.PR_CODE); + + // 新增查询列和分组列 + Set defaultSelectColNames = defaultSelectColumns.stream().map(QueryColumn::getName).collect(Collectors.toSet()); + Set defaultGroupColNames = defaultGroupColumns.stream().map(QueryColumn::getName).collect(Collectors.toSet()); + for (QueryColumn groupColumn : groupColumns) { + String newGroupColName = groupColumn.getName(); + if (!defaultGroupColNames.contains(newGroupColName)) { + defaultGroupColumns.add(groupColumn); + } + if (!defaultSelectColNames.contains(newGroupColName)) { + defaultSelectColumns.add(groupColumn); + } + } + + // 将全量分组列转为数组,别介意入参长度为0,实际会创建合理长度的数组 + QueryColumn[] actualGroupColumns = defaultGroupColumns.toArray(new QueryColumn[0]); + + QueryWrapper queryWrapper = + QueryWrapper.create() + .select(defaultSelectColumns) + .from(IOT_CARBON_PRODUCTION_RESULT_ENTITY) + .eq(IotCarbonProductionResultEntity::getM_id, productId, Objects::nonNull) + .eq(IotCarbonProductionResultEntity::getPrCode, prCode, Objects::nonNull) + .eq(IotCarbonProductionResultEntity::getSync_status, shareStatus, Objects::nonNull) + .between( + IotCarbonProductionResultEntity::getFinishTime, + start, + end, + Objects.nonNull(start) && Objects.nonNull(end)) + .groupBy(actualGroupColumns); + return mapper.selectListByQueryAs(queryWrapper, IotCarbonProductionResultDTO.class); + } + + private List aggCarbon(Long productId, Date start, Date end) { + return mapper.selectListByQueryAs( + QueryWrapper.create() + .select( + IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + IOT_CARBON_PRODUCTION_RESULT_ENTITY.PR_CODE, + sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.USAGE_AVG).as(AggCarbon::getUsageAvg), + sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_AVG).as(AggCarbon::getCarbonAvg), + sum(IOT_CARBON_PRODUCTION_RESULT_ENTITY.F_CARBON_AVG).as(AggCarbon::getFCarbonAvg)) + .from(IOT_CARBON_PRODUCTION_RESULT_ENTITY) + .eq(IotCarbonProductionResultEntity::getM_id, productId) + .between( + IotCarbonProductionResultEntity::getFinishTime, + start, + end, + Objects.nonNull(start) && Objects.nonNull(end)) + .groupBy( + IOT_CARBON_PRODUCTION_RESULT_ENTITY.CARBON_TYPE, + IOT_CARBON_PRODUCTION_RESULT_ENTITY.PR_CODE), + AggCarbon.class); + } + + private List getRecords( + CarbonLifecycleEnum carbonLifecycle, Long productId, String prCode, String start, String end) { + List list = + listByCarbonLifecycle(carbonLifecycle, productId, prCode, start, end); + if (list.isEmpty()) { + return new ArrayList<>(); + } + LotCarbonR recordR = + list.stream() + .map(LotCarbonR::new) + .reduce(LotCarbonR::add) + .orElse(new LotCarbonR()) + .refreshAvg(); + return List.of(recordR); + } + + @SneakyThrows + private List listByCarbonLifecycle( + CarbonLifecycleEnum carbonLifecycle, Long productId, String prCode, String start, String end){ + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + QueryWrapper wrapper = + QueryWrapper.create() + .in(IotCarbonProductionResultEntity::getCarbonType, CARBON_TYPE_GROUP.get(carbonLifecycle)) + .eq(IotCarbonProductionResultEntity::getM_id, productId) + .eq(IotCarbonProductionResultEntity::getPrCode, prCode, StringUtils::isNotBlank); + if(Objects.nonNull(start) && Objects.nonNull(end)){ + Date startDate = sdf.parse(start); + Date endDate = sdf.parse(end); + wrapper.and(IOT_CARBON_PRODUCTION_RESULT_ENTITY.START_TIME.between(startDate, endDate) + .or(IOT_CARBON_PRODUCTION_RESULT_ENTITY.FINISH_TIME.between(startDate, endDate))); + } + return list(wrapper); + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ConfigController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ConfigController.java new file mode 100644 index 0000000..802c11b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ConfigController.java @@ -0,0 +1,29 @@ +package com.thing.carbontrack.pub.controller; + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("v2/carbon/config") +@Tag(name="公共配置") +@RequiredArgsConstructor +public class ConfigController { + @Value("${carbon.syncButton:false}") + private Boolean syncButton; + + + @GetMapping("syncButton") + @Operation(summary="判断是否有同步按钮") + public Result syncButton(){ + return new Result().ok(syncButton); + } + + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ModelCommunityController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ModelCommunityController.java new file mode 100644 index 0000000..260ad47 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/controller/ModelCommunityController.java @@ -0,0 +1,122 @@ +package com.thing.carbontrack.pub.controller; + + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.pub.service.ProductionModelService; +import com.thing.carbontrack.share.dto.IotCarbonShareDTO; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +import static com.thing.sys.security.context.UserContext.getTenantCode; + + +@RestController +@RequestMapping("v2/carbon/model") +@Tag(name="产品工艺模型社区,企业侧") +@RequiredArgsConstructor +public class ModelCommunityController { + + private final ProductionModelService productionModelService; + + private final IotCarbonShareService carbonShareService; + + private final IotCarbonProductionVarietyService carbonProductionVarietyService; + + private final IotCarbonBomService carbonBomService; + + private final IotCarbonProcessStepsService processStepsService; + + @GetMapping("industry/types") + @Operation(summary="行业一级分类") + public String industryCategories(){ + return productionModelService.fetchProductionIndustryTypes(getAccessToken()); + } + + + @GetMapping("industry/categories") + @Operation(summary="产品二级分类") + @Parameters({ + @Parameter(name = "industryType", description = "行业一级分类") + }) + public String types(@Parameter(hidden = true) @RequestParam Map params){ + String industryType = MapUtils.getString(params,"industryType"); + return productionModelService.fetchProductionIndustryCategories(industryType,getAccessToken()); + } + + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "keywords", description = "关键词"), + @Parameter(name = "industryCategory", description = "行业分类"), + @Parameter(name = "productType", description = "产品类型") + }) + public String page(@Parameter(hidden = true) @RequestParam Map params){ + params.putIfAbsent("orderField", "create_date"); + params.put("showInCommunity", 1); + + return productionModelService.fetchProductionModelPage(params,getAccessToken()); + } + + + @GetMapping("{id}") + @Operation(summary="信息") + public String get(@PathVariable("id") Long id){ + return productionModelService.fetchProductionModelDetail(id,getAccessToken()); + } + + + @GetMapping("down/{id}") + @Operation(summary="下载") + public Result down(@PathVariable("id") Long id){ + Long tenantCode = UserContext.getTenantCode(); + try { + String resultStr = productionModelService.fetchProductionModelDetail(id,getAccessToken()); + JSONObject object = JSONObject.parseObject(resultStr); + JSONObject info = object.getJSONObject("data"); + String productJsonStr = info.getString("productJson"); + Long pid = carbonProductionVarietyService.saveWithJson(productJsonStr, tenantCode); + carbonBomService.saveOrUpdateWithJson(info.getString("materialJson"), pid); + processStepsService.saveOrUpdateWithJson(info.getString("processJson"), pid); + } catch (Exception e) { + Result result = new Result<>(); + result.setCode(-1); + result.setData("下载失败!"); + result.setMsg("下载失败!"); + return result; + } + + return new Result().ok("下载成功!"); + } + + + private String getAccessToken(){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", getTenantCode()); + IotCarbonShareDTO dto = carbonShareService.getOneAs(wrapper, IotCarbonShareDTO.class); + if(ObjectUtils.isNotEmpty(dto)){ + return dto.getToken(); + } + return null; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/MaterialJsonBean.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/MaterialJsonBean.java new file mode 100644 index 0000000..44e5bea --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/MaterialJsonBean.java @@ -0,0 +1,36 @@ +package com.thing.carbontrack.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + + + + +@Data +public class MaterialJsonBean implements Serializable { + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "最终产品id") + private Long productId; + @Schema(description = "父物料id") + private Long pid; + @Schema(description = "原料编码") + private String mcode; + @Schema(description = "原料名称") + private String mname; + @Schema(description = "原料类别") + private String m_type; + @Schema(description = "原料单位/功能单元") + private String unit; + @Schema(description = "原料重量") + private BigDecimal weight; + @Schema(description = "重量重量计量单位") + private String w_unit; + @Schema(description = "原料用量") + private BigDecimal dosage; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProcessJsonBean.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProcessJsonBean.java new file mode 100644 index 0000000..646b358 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProcessJsonBean.java @@ -0,0 +1,27 @@ +package com.thing.carbontrack.pub.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class ProcessJsonBean implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "物料id") + private Long mid; + @Schema(description = "序号") + private Long orderNum; + @Schema(description = "工艺工序编码") + private String processCode; + @Schema(description = "工艺工序名称") + private String processName; + @Schema(description = "能源介质id,依赖于能源品种基本信息,多个编码以英文逗号分割") + private String evIds; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductJsonBean.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductJsonBean.java new file mode 100644 index 0000000..1f364ed --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductJsonBean.java @@ -0,0 +1,139 @@ +package com.thing.carbontrack.pub.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +@Getter +@Data +public class ProductJsonBean implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品编码") + private String code; + @Schema(description = "产品名称") + private String name; + @Schema(description = "产品规格型号") + private String model; + @Schema(description = "产品图片url") + private String url; + @Schema(description = "种类 1成品 2半成品 ") + private String type; + @Schema(description = "最终成品类型") + private String finalType; + @Schema(description = "所属行业/产品所属行业") + private String industry; + @Schema(description = "产品分类/产品所属行业子类型") + private String industrySub; + @Schema(description = "功能单位计量标准数") + private String fsu; + @Schema(description = "功能单位计量单位") + private String fUnit; + @Schema(description = "单价,产品单价") + private BigDecimal unitPrice; + @Schema(description = "产品尺寸") + private String pSize; + @Schema(description = "产品重量") + private BigDecimal pWeight; + @Schema(description = "重量计量单位") + private String wUnit; + @Schema(description = "系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓") + private String boundary; + @Schema(description = "备注") + private String remarks; + @Schema(description = "产品尺寸单位") + private String pSizeUnit; + @Schema(description = "工序预览图url") + private String stepsUrl; + @Schema(description = "工序拖拉拽json") + private String stepsJson; + + public void setId(Long id) { + this.id = id; + } + + public void setCode(String code) { + this.code = code; + } + + public void setName(String name) { + this.name = name; + } + + public void setModel(String model) { + this.model = model; + } + + public void setUrl(String url) { + this.url = url; + } + + public void setType(String type) { + this.type = type; + } + + public void setFinalType(String finalType) { + this.finalType = finalType; + } + + public void setIndustry(String industry) { + this.industry = industry; + } + + public void setIndustrySub(String industrySub) { + this.industrySub = industrySub; + } + + public void setFsu(String fsu) { + this.fsu = fsu; + } + + public void setfUnit(String fUnit) { + this.fUnit = fUnit; + } + + public void setUnitPrice(BigDecimal unitPrice) { + this.unitPrice = unitPrice; + } + + public void setpSize(String pSize) { + this.pSize = pSize; + } + + public void setpWeight(BigDecimal pWeight) { + this.pWeight = pWeight; + } + + public void setwUnit(String wUnit) { + this.wUnit = wUnit; + } + + public void setBoundary(String boundary) { + this.boundary = boundary; + } + + public void setRemarks(String remarks) { + this.remarks = remarks; + } + + public void setpSizeUnit(String pSizeUnit) { + this.pSizeUnit = pSizeUnit; + } + + public void setStepsUrl(String stepsUrl) { + this.stepsUrl = stepsUrl; + } + + public void setStepsJson(String stepsJson) { + this.stepsJson = stepsJson; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionModelUploadDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionModelUploadDTO.java new file mode 100644 index 0000000..09e10e8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionModelUploadDTO.java @@ -0,0 +1,81 @@ +package com.thing.carbontrack.pub.dto; + +import com.alibaba.fastjson.JSONObject; +import lombok.Data; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/22 14:43 + * @description 模型上传数据格式 + */ +@Data +public class ProductionModelUploadDTO { + /** 产品id */ + private Long productId; + + /** 产品编码 */ + private String productCode; + /** 产品编码 */ + private String url; + /** 产品名称 */ + private String productName; + + /** 产品型号 */ + private String productModel; + + /** 功能单位 */ + private String functionUnit; + + /** 产品基本信息json */ + private String productJson; + + /** 原料信息json */ + private String materialJson; + + /** 产品工艺工序json */ + private String processJson; + + /** 行业一级分类 */ + private String industryType; + + /** 行业二级分类 */ + private String industryCategory; + + /**产品类型 */ + private String productType; + /**工序预览图 */ + private String stepsUrl; + /**工序拖拉拽json */ + private String stepsJson; + + + /** + * 初始化 产品模型 企业token +产品id 存在,则是修改 + * @param productJsonBea + * @param materialJsonBeanList + * @param processJsonBeanList + * @return + */ + public static ProductionModelUploadDTO init(ProductJsonBean productJsonBea, List materialJsonBeanList,List processJsonBeanList ){ + ProductionModelUploadDTO info = new ProductionModelUploadDTO(); + info.setProductId(productJsonBea.getId()); + info.setProductCode(productJsonBea.getCode()); + info.setProductName(productJsonBea.getName()); + info.setProductModel(productJsonBea.getModel()); + info.setFunctionUnit(productJsonBea.getFUnit()); + info.setIndustryType(productJsonBea.getIndustry()); + info.setIndustryCategory(productJsonBea.getIndustrySub()); + info.setProductType(productJsonBea.getIndustrySub()); + info.setStepsUrl(productJsonBea.getStepsUrl()); + info.setStepsJson(productJsonBea.getStepsJson()); + info.setMaterialJson(JSONObject.toJSONString(materialJsonBeanList)); + info.setProcessJson(JSONObject.toJSONString(processJsonBeanList)); + info.setProductJson(JSONObject.toJSONString(productJsonBea)); + info.setUrl(productJsonBea.getUrl()); + return info; + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionResultSyncDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionResultSyncDTO.java new file mode 100644 index 0000000..7aa7ff1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/dto/ProductionResultSyncDTO.java @@ -0,0 +1,173 @@ +package com.thing.carbontrack.pub.dto; + +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import lombok.Data; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; + +/** + * @author siyang + * @date 2024/5/22 14:43 + * @description 碳足迹核算数据同步对象 + */ +@Data +public class ProductionResultSyncDTO { + + /**源数据id*/ + private Long oriId; + + /** 产品id */ + private Long productId; + + /** 产品料号 */ + private String productCode; + + /** 产品型号 */ + private String productModel; + + /** 产品名称 */ + private String productName; + + /** 产品重量 */ + private BigDecimal productWeight; + + /** 功能单位 */ + private String functionUnit; + + /** 产品重量计量单位 */ + private String weightUnit; + + /** 生产记录编码 */ + private String prCode; + + /** 生产耗时,单位:h */ + private Integer prDur; + + /** 生产数量 */ + private Long num; + + /** 成品数量 */ + private Long finalNum; + + /** 能源品种id */ + private Long evId; + + /** 能源品种名称 */ + private String evName; + + /** 能耗总用量 */ + private BigDecimal usage; + + /** 能耗平均用量 */ + private BigDecimal usageAvg; + + /** 总碳排 */ + private BigDecimal carbon; + + /** 平均碳排 */ + private BigDecimal carbonAvg; + + /** 总能耗 */ + private BigDecimal tce; + + /** 碳排类型 1 原料 2 原料运输 3 产品生产 4 产品公摊/间接 5 产品运输 6 产品使用 7 产品废弃 8 产品废弃运输 */ + private String carbonType; + + /** 结果类型:1-结果数据 2-过程数据 */ + private String resultType; + + /** 系统边界: 1-摇篮到大门,2-摇篮到坟墓 */ + private String boundary; + + /** 开工时间: yyyy-MM-dd HH:mm:ss */ + private String startTime; + + /** 完工时间: yyyy-MM-dd HH:mm:ss */ + private String finishTime; + + /** 详情json,一个类型,一个能源,形成一个详情json */ + private String json; + + + /** + * 产品运输的时候,提交的数据组id 同一组数据 数量只聚合一次 + */ + private Long groupId; + /** + * 废弃运输里程 + */ + private BigDecimal fqUsage; + /** + * 废弃运输碳排 + */ + private BigDecimal fqCarbon; + /** + * 废弃运输里程平均值,不平均,= 废弃运输里程 + */ + private BigDecimal fqUsageAvg; + /** + * 废弃运输碳排平均值 + */ + private BigDecimal fqCarbonAvg; + /** + * 废弃运输类型id + */ + private Long fqEvId; + /** + * 废弃运输类型名称 + */ + private String fqEvName; + /** + * 使用年限 + */ + private Integer life; + /** + * 数据源类型:1-实采,2-填报 + */ + private String sourceType; + + public static ProductionResultSyncDTO init(IotCarbonProductionResultEntity entity,String sourceType){ + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + ProductionResultSyncDTO dto = new ProductionResultSyncDTO(); + dto.setOriId(entity.getId()); + dto.setProductId(entity.getM_id()); + dto.setProductCode(entity.getM_code()); + dto.setProductName(entity.getM_name()); + dto.setProductModel(entity.getM_model()); + dto.setProductWeight(entity.getP_weight()); + dto.setFunctionUnit(entity.getFUnit()); + dto.setWeightUnit(entity.getW_unit()); + dto.setPrCode(entity.getPrCode()); + dto.setPrDur(entity.getPrDur()); + dto.setNum(entity.getNum()); + dto.setFinalNum(entity.getFinalNum()); + dto.setEvId(entity.getEvId()); + dto.setEvName(entity.getEvName()); + dto.setUsage(entity.getUsage()); + dto.setUsageAvg(entity.getUsage_avg()); + dto.setCarbon(entity.getCarbon()); + dto.setCarbonAvg(entity.getCarbon_avg()); + dto.setCarbonType(entity.getCarbonType()); + dto.setResultType(entity.getResultType()); + dto.setBoundary(entity.getBoundary()); + dto.setStartTime(format.format(entity.getStartTime())); + dto.setFinishTime(format.format(entity.getFinishTime())); + if(entity.getResultType().equals("2")){ + dto.setJson(entity.getJson()); + }else { + dto.setJson(null); + } + dto.setGroupId(entity.getGroup_id()); + dto.setFqCarbon(entity.getF_carbon()); + dto.setFqUsage(entity.getF_usage()); + dto.setFqCarbonAvg(entity.getF_carbon_avg()); + dto.setFqUsageAvg(entity.getF_usage_avg()); + dto.setFqEvId(entity.getF_ev_id()); + dto.setFqEvName(entity.getF_ev_name()); + dto.setLife(entity.getLife()); + dto.setSourceType(sourceType); + return dto; + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/CertificatePointsUpdateRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/CertificatePointsUpdateRequest.java new file mode 100644 index 0000000..9824ac6 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/CertificatePointsUpdateRequest.java @@ -0,0 +1,61 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 证书上传积分更新请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class CertificatePointsUpdateRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/certificate/sync?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 碳足迹证书对象 */ + private IotCarbonCertificateDTO certificate; + + public CertificatePointsUpdateRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + return CarbonPubRequestUtil.encryptData(certificate); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorDetailRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorDetailRequest.java new file mode 100644 index 0000000..bdcfa91 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorDetailRequest.java @@ -0,0 +1,63 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.HashMap; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 使用国网碳排因子请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class MaterialFactorDetailRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/material/factor/detail?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 碳排因子id */ + private String materialFactorId; + + public MaterialFactorDetailRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + Map params = new HashMap<>(); + params.put("materialFactorId", materialFactorId); + return params; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorPageRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorPageRequest.java new file mode 100644 index 0000000..c31ca58 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/MaterialFactorPageRequest.java @@ -0,0 +1,85 @@ +package com.thing.carbontrack.pub.request; + +import cn.hutool.core.map.MapUtil; +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网碳排因子库请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class MaterialFactorPageRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/material/factor/page?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + private Integer page = 1; + + private Integer limit = 10; + + /** 产品名称,支持中英文模糊搜索 */ + private String productName; + + public MaterialFactorPageRequest(Map params) { + if (params.containsKey("page")) { + this.page = MapUtil.getInt(params, "page"); + } + if (params.containsKey("limit")) { + this.limit = MapUtil.getInt(params, "limit"); + } + if (params.containsKey("productName")) { + this.productName = MapUtil.getStr(params, "productName"); + } + } + + public MaterialFactorPageRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + Map params = new HashMap<>(); + params.put("page", page); + params.put("limit", limit); + if (Objects.nonNull(productName)) { + params.put("productName", productName); + } + return params; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryCategoryListRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryCategoryListRequest.java new file mode 100644 index 0000000..20a1ebf --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryCategoryListRequest.java @@ -0,0 +1,66 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网产品工艺类型请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionIndustryCategoryListRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/model/industry/categories?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 行业类型(一级) */ + private String industryType; + + public ProductionIndustryCategoryListRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + Map params = new HashMap<>(); + if (Objects.nonNull(industryType)) { + params.put("industryType", industryType); + } + return params; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryTypeListRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryTypeListRequest.java new file mode 100644 index 0000000..d7cfdc4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionIndustryTypeListRequest.java @@ -0,0 +1,57 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网产品工艺行业分类列表请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionIndustryTypeListRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/model/industry/types?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + public ProductionIndustryTypeListRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + return null; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelDetailRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelDetailRequest.java new file mode 100644 index 0000000..5d023df --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelDetailRequest.java @@ -0,0 +1,79 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网产品工艺类型请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionModelDetailRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/model/detail?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 模型id */ + private Long id; + + /** 产品编码 */ + private String code; + + /** 产品名称 */ + private String name; + + public ProductionModelDetailRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + Map params = new HashMap<>(); + if (Objects.nonNull(id)) { + params.put("id", id); + } + if (StringUtils.isNotBlank(code)) { + params.put("code", code); + } + if (StringUtils.isNotBlank(name)) { + params.put("name", name); + } + return params; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelPageRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelPageRequest.java new file mode 100644 index 0000000..d005a67 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelPageRequest.java @@ -0,0 +1,94 @@ +package com.thing.carbontrack.pub.request; + +import cn.hutool.core.map.MapUtil; +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网产品工艺类型请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionModelPageRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/model/page?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + private Integer page = 1; + + private Integer limit = 10; + + /** 行业类型(二级) */ + private String industryCategory; + + /** 产品类型 */ + private String productType; + + public ProductionModelPageRequest(Map params) { + if (params.containsKey("page")) { + this.page = MapUtil.getInt(params, "page"); + } + if (params.containsKey("limit")) { + this.limit = MapUtil.getInt(params, "limit"); + } + if (params.containsKey("industryCategory")) { + this.industryCategory = MapUtil.getStr(params, "industryCategory"); + } + if (params.containsKey("productType")) { + this.productType = MapUtil.getStr(params, "productType"); + } + } + + public ProductionModelPageRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + Map params = new HashMap<>(); + params.put("page", page); + params.put("limit", limit); + if (Objects.nonNull(industryCategory)) { + params.put("industryCategory", industryCategory); + } + if (Objects.nonNull(productType)) { + params.put("productType", productType); + } + return params; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelUploadRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelUploadRequest.java new file mode 100644 index 0000000..39f9d00 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionModelUploadRequest.java @@ -0,0 +1,61 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 14:53 + * @description 国网产品工艺类型请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionModelUploadRequest extends ApiRequest { + @Serial private static final long serialVersionUID = 2976048462446221262L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/model/upload?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 上传对象数据格式 */ + private ProductionModelUploadDTO model; + + public ProductionModelUploadRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + return CarbonPubRequestUtil.encryptData(model); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionResultSyncRequest.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionResultSyncRequest.java new file mode 100644 index 0000000..d0a85cc --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/request/ProductionResultSyncRequest.java @@ -0,0 +1,63 @@ +package com.thing.carbontrack.pub.request; + +import com.thing.carbontrack.pub.dto.ProductionResultSyncDTO; +import com.thing.carbontrack.pub.utils.CarbonPubRequestUtil; +import com.thing.common.core.rest.dto.ApiRequest; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.List; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/28 16:45 + * @description 碳足迹核算结果数据同步请求 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@NoArgsConstructor +public class ProductionResultSyncRequest extends ApiRequest { + + @Serial private static final long serialVersionUID = 7589884374492428848L; + + private static final String urlFormat = + "%s://%s/thing/v2/carbon/pub/production/result/sync?accessToken=%s×tamp=%s&sign=%s"; + + /** 企业授权令牌 */ + private String accessToken; + + /** 签名密钥,要藏好 */ + private String apiSecret; + + /** 核算结果 */ + private List result; + + public ProductionResultSyncRequest fillUrl(String schema, String hostPort) { + long ts = System.currentTimeMillis(); + String url = + String.format( + urlFormat, + schema, + hostPort, + accessToken, + ts, + CarbonPubRequestUtil.calculateSign(accessToken, apiSecret, ts)); + setUrl(url); + return this; + } + + @Override + public Map getHeader() { + return null; + } + + @Override + public Map getBody() { + return CarbonPubRequestUtil.encryptData(result); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/DefaultPostResponse.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/DefaultPostResponse.java new file mode 100644 index 0000000..056459b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/DefaultPostResponse.java @@ -0,0 +1,23 @@ +package com.thing.carbontrack.pub.response; + +import com.thing.common.core.rest.dto.ApiResponse; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * @author siyang + * @date 2024/5/22 14:52 + * @description + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class DefaultPostResponse extends ApiResponse { + @Serial private static final long serialVersionUID = -8565007537926551042L; + + @Override + public boolean failed() { + return getCodeNumber() != 0; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/EncryptedResponse.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/EncryptedResponse.java new file mode 100644 index 0000000..ce7657d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/response/EncryptedResponse.java @@ -0,0 +1,39 @@ +package com.thing.carbontrack.pub.response; + +import com.thing.common.core.enumeration.RsaKeyType; +import com.thing.common.core.rest.dto.ApiResponse; +import com.thing.common.core.utils.EncryptUtils; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/14 15:13 + * @description 请求国网数据服务得到的加密的响应结果 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class EncryptedResponse extends ApiResponse { + @Serial private static final long serialVersionUID = 8513990428529338432L; + + private String encryptedAesKey; + + private String data; + + @Override + public boolean failed() { + return getCode() != null && !Objects.equals(getCode(), "0"); + } + + public String getDecryptedData() { + if (failed()) { + return toString(); + } + String pemPrivateRsaKey = EncryptUtils.getPemRsaKeyFromResource(RsaKeyType.PRIVATE); + String aesKey = EncryptUtils.rsaDecrypt(encryptedAesKey, pemPrivateRsaKey); + return EncryptUtils.aesDecrypt(data, aesKey); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/CertificateSyncService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/CertificateSyncService.java new file mode 100644 index 0000000..4065e80 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/CertificateSyncService.java @@ -0,0 +1,15 @@ +package com.thing.carbontrack.pub.service; + + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; + +/** + * @author siyang + * @date 2024/5/14 15:22 + * @description 对接国网侧碳足迹证书服务接口 + */ +public interface CertificateSyncService { + + void sync(IotCarbonCertificateDTO certificate, String accessToken, String hostPort); + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/MaterialFactorService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/MaterialFactorService.java new file mode 100644 index 0000000..5435d0d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/MaterialFactorService.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.pub.service; + + +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 15:22 + * @description 对接国网侧碳排因子库服务接口 + */ +public interface MaterialFactorService { + + String fetchMaterialFactorPage(Map params, String accessToken); + + String fetchMaterialFactorDetail(String materialFactorId, String accessToken); + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionModelService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionModelService.java new file mode 100644 index 0000000..933cd30 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionModelService.java @@ -0,0 +1,29 @@ +package com.thing.carbontrack.pub.service; + +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; + +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/21 15:53 + * @description 国网侧产品工艺社区数据服务 + */ +public interface ProductionModelService { + + String fetchProductionIndustryTypes(String accessToken); + + String fetchProductionIndustryCategories(String industryType, String accessToken); + + String fetchProductionModelPage(Map params, String accessToken); + + String fetchProductionModelDetail(Long id, String accessToken); + + String fetchProductionModelDetail(String code, String name, String accessToken); + + boolean uploadProductionModel(ProductionModelUploadDTO model, String accessToken); + + boolean uploadProductionModel(ProductionModelUploadDTO model, String hostPort, String accessToken); + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionResultService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionResultService.java new file mode 100644 index 0000000..2da8974 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/ProductionResultService.java @@ -0,0 +1,18 @@ +package com.thing.carbontrack.pub.service; + +import com.thing.carbontrack.pub.dto.ProductionResultSyncDTO; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/21 15:53 + * @description 国网侧核算数据服务接口 + */ +public interface ProductionResultService { + + boolean uploadProductionResult(List result, String accessToken); + + boolean uploadProductionResult(List result, String hostPort, String accessToken); + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/BaseCarbonApiService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/BaseCarbonApiService.java new file mode 100644 index 0000000..2ba97eb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/BaseCarbonApiService.java @@ -0,0 +1,49 @@ +package com.thing.carbontrack.pub.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.rest.service.ApiService; +import com.thing.sys.security.context.UserContext; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author siyang + * @date 2024/5/21 14:50 + * @description + */ +@Service +public class BaseCarbonApiService { + @Resource + protected ApiService apiService; + + @Resource + private IotCarbonShareService shareService; + + @Value("${carbon.pub.schema:http}") + protected String schema; + + @Value("${carbon.api.secret}") + protected String apiSecret; + + protected String getDefaultHostPort() { + return getHostPortByName(); + } + + protected String getHostPortByName() { + IotCarbonShareEntity entity = + shareService.getOne( + QueryWrapper.create().eq(IotCarbonShareEntity::getTenantCode, UserContext.getRealTenantCode()).limit(1)); + if (Objects.isNull(entity)) { + throw new SysException("请先配置数据共享"); + } + return entity.getUrl(); + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/CertificateSyncServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/CertificateSyncServiceImpl.java new file mode 100644 index 0000000..edd7ed7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/CertificateSyncServiceImpl.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.pub.service.impl; + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.pub.request.CertificatePointsUpdateRequest; +import com.thing.carbontrack.pub.response.DefaultPostResponse; +import com.thing.carbontrack.pub.service.CertificateSyncService; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +/** + * @author siyang + * @date 2024/6/24 10:30 + * @description + */ +@Service +public class CertificateSyncServiceImpl extends BaseCarbonApiService implements CertificateSyncService { + + @Override + public void sync(IotCarbonCertificateDTO certificate, String accessToken, String hostPort) { + CertificatePointsUpdateRequest request = + new CertificatePointsUpdateRequest() + .setCertificate(certificate) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, hostPort); + apiService.apiCall(request, HttpMethod.POST, DefaultPostResponse.class); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/MaterialFactorServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/MaterialFactorServiceImpl.java new file mode 100644 index 0000000..897ccae --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/MaterialFactorServiceImpl.java @@ -0,0 +1,44 @@ +package com.thing.carbontrack.pub.service.impl; + +import com.thing.carbontrack.pub.request.MaterialFactorDetailRequest; +import com.thing.carbontrack.pub.request.MaterialFactorPageRequest; +import com.thing.carbontrack.pub.response.EncryptedResponse; +import com.thing.carbontrack.pub.service.MaterialFactorService; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/14 15:23 + * @description 对接国网侧碳排因子库服务实现 + */ +@Service +public class MaterialFactorServiceImpl extends BaseCarbonApiService implements MaterialFactorService { + + @Override + public String fetchMaterialFactorPage(Map params, String accessToken) { + MaterialFactorPageRequest request = + new MaterialFactorPageRequest(params) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public String fetchMaterialFactorDetail(String materialFactorId, String accessToken) { + MaterialFactorDetailRequest request = + new MaterialFactorDetailRequest() + .setMaterialFactorId(materialFactorId) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionModelServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionModelServiceImpl.java new file mode 100644 index 0000000..7ebb9cc --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionModelServiceImpl.java @@ -0,0 +1,110 @@ +package com.thing.carbontrack.pub.service.impl; + +import com.thing.carbontrack.pub.dto.ProductionModelUploadDTO; +import com.thing.carbontrack.pub.request.*; +import com.thing.carbontrack.pub.response.DefaultPostResponse; +import com.thing.carbontrack.pub.response.EncryptedResponse; +import com.thing.carbontrack.pub.service.ProductionModelService; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/21 16:03 + * @description 国网侧产品工艺社区数据服务实现 + */ +@Service +public class ProductionModelServiceImpl extends BaseCarbonApiService implements ProductionModelService { + + @Override + public String fetchProductionIndustryTypes(String accessToken) { + ProductionIndustryTypeListRequest request = + new ProductionIndustryTypeListRequest() + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public String fetchProductionIndustryCategories(String industryType, String accessToken) { + ProductionIndustryCategoryListRequest request = + new ProductionIndustryCategoryListRequest() + .setIndustryType(industryType) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public String fetchProductionModelPage(Map params, String accessToken) { + ProductionModelPageRequest request = + new ProductionModelPageRequest(params) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public String fetchProductionModelDetail(Long id, String accessToken) { + ProductionModelDetailRequest request = + new ProductionModelDetailRequest() + .setId(id) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public String fetchProductionModelDetail(String code, String name, String accessToken) { + ProductionModelDetailRequest request = + new ProductionModelDetailRequest() + .setCode(code) + .setName(name) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + EncryptedResponse response = + apiService.apiCall(request, HttpMethod.GET, EncryptedResponse.class); + return response.getDecryptedData(); + } + + @Override + public boolean uploadProductionModel(ProductionModelUploadDTO model, String accessToken) { + ProductionModelUploadRequest request = + new ProductionModelUploadRequest() + .setModel(model) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + DefaultPostResponse response = + apiService.apiCall(request, HttpMethod.POST, DefaultPostResponse.class); + return !response.failed(); + } + + @Override + public boolean uploadProductionModel(ProductionModelUploadDTO model, String hostPort, String accessToken) { + ProductionModelUploadRequest request = + new ProductionModelUploadRequest() + .setModel(model) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, hostPort); + DefaultPostResponse response = + apiService.apiCall(request, HttpMethod.POST, DefaultPostResponse.class); + return !response.failed(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionResultServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionResultServiceImpl.java new file mode 100644 index 0000000..76fa126 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/service/impl/ProductionResultServiceImpl.java @@ -0,0 +1,45 @@ +package com.thing.carbontrack.pub.service.impl; + +import com.thing.carbontrack.pub.dto.ProductionResultSyncDTO; +import com.thing.carbontrack.pub.request.ProductionResultSyncRequest; +import com.thing.carbontrack.pub.response.DefaultPostResponse; +import com.thing.carbontrack.pub.service.ProductionResultService; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * @author siyang + * @date 2024/5/28 16:44 + * @description 国网侧碳足迹核算数据服务接口实现 + */ +@Service +public class ProductionResultServiceImpl extends BaseCarbonApiService implements ProductionResultService { + + @Override + public boolean uploadProductionResult(List result, String accessToken) { + ProductionResultSyncRequest request = + new ProductionResultSyncRequest() + .setResult(result) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, getDefaultHostPort()); + DefaultPostResponse response = + apiService.apiCall(request, HttpMethod.POST, DefaultPostResponse.class); + return !response.failed(); + } + + @Override + public boolean uploadProductionResult(List result, String hostPort, String accessToken) { + ProductionResultSyncRequest request = + new ProductionResultSyncRequest() + .setResult(result) + .setAccessToken(accessToken) + .setApiSecret(apiSecret) + .fillUrl(schema, hostPort); + DefaultPostResponse response = + apiService.apiCall(request, HttpMethod.POST, DefaultPostResponse.class); + return !response.failed(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/utils/CarbonPubRequestUtil.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/utils/CarbonPubRequestUtil.java new file mode 100644 index 0000000..1783b98 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/pub/utils/CarbonPubRequestUtil.java @@ -0,0 +1,38 @@ +package com.thing.carbontrack.pub.utils; + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.enumeration.RsaKeyType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.EncryptUtils; + +import java.util.HashMap; +import java.util.Map; + +/** + * @author siyang + * @date 2024/5/28 16:49 + * @description 国网侧数据请求工具 + */ +public class CarbonPubRequestUtil { + + public static Map encryptData(Object data) { + Map params = new HashMap<>(); + try { + String modelJson = JSONObject.toJSONString(data); + String aesKey = EncryptUtils.createAesKey(192); + String encryptContent = EncryptUtils.aesEncrypt(modelJson, aesKey); + String pemPublicRsaKey = EncryptUtils.getPemRsaKeyFromResource(RsaKeyType.PUBLIC); + String encryptedAesKey = EncryptUtils.rsaEncrypt(aesKey, pemPublicRsaKey); + params.put("encryptedAesKey", encryptedAesKey); + params.put("data", encryptContent); + return params; + } catch (Exception e) { + throw new SysException(e.getMessage()); + } + } + + public static String calculateSign(String accessToken, String apiSecret, Long timestamp) { + String secretStr = accessToken + "." + apiSecret + "." + timestamp; + return EncryptUtils.md5Encrypt(secretStr).toUpperCase(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/controller/IotCarbonRatioController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/controller/IotCarbonRatioController.java new file mode 100644 index 0000000..bf89ea0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/controller/IotCarbonRatioController.java @@ -0,0 +1,201 @@ +package com.thing.carbontrack.ratio.controller; + +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.dto.IotCarbonRatioDTO; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.tenant.dto.SysTenantDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** +* iot_carbon_ratio +* +* @author xc +* @since 3.0 2024-05-11 +*/ +@RestController +@RequestMapping("v2/iotcarbonratio") +@Tag(name="AAA折标煤碳排因子基本信息 1碳排,2折标媒") +@RequiredArgsConstructor +public class IotCarbonRatioController { + + @Autowired + private IotCarbonRatioService iotCarbonRatioService; + + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒"), + @Parameter(name = "district", description = "适用区域"), + @Parameter(name = "time", description = "时间"), + @Parameter(name = "key", description = "模糊搜索关键字"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonRatioService.pageIotCarbonRatioDTO(params, IotCarbonRatioDTO.class); + + page.getList().forEach(temp->{ + CarbonEnergyVarietyEntity entity = carbonEnergyVarietyService.getById(temp.getEvId()); + if (ObjectUtils.isNotEmpty(entity)){ + temp.setEvName(entity.getName()); + } + }); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="根据企业编码code以及配置类型查询企业所有配置,查询不到返回默认企业的配置") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒") + }) + public Result> list(@RequestParam Map params){ + List page = iotCarbonRatioService.listIotCarbonRatioDTOs(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonRatioDTO data = iotCarbonRatioService.getByIdAs(id, IotCarbonRatioDTO.class); + CarbonEnergyVarietyEntity entity = carbonEnergyVarietyService.getById(data.getEvId()); + if (ObjectUtils.isNotEmpty(entity)){ + data.setEvName(entity.getName()); + } + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonRatioDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonRatioService.saveDtoIotCarbonRatioDTO(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonRatioDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonRatioService.updateDtoIotCarbonRatioDTO(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonRatioService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("batchSave") + @Operation(summary="批量保存") + public Result batchSave(@RequestBody List dtos){ + List entityList = ConvertUtils.sourceToTarget(dtos,IotCarbonRatioEntity.class); + iotCarbonRatioService.saveBatch(entityList); + return new Result().ok("保存成功"); + } + + @GetMapping("tenantList") + @Operation(summary="侧边栏所有有数据的企业列表") + @Parameters({ + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒") + }) + public Result> tenantList(@RequestParam String type){ + List dtos =iotCarbonRatioService.tenantList(type); + return new Result>().ok(dtos); + } + + @GetMapping("delTenant") + @Operation(summary="根据企业编码以及类型,删除企业所有配置") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒") + }) + public Result delTenant(@RequestParam Long tenantCode,@RequestParam String type){ + iotCarbonRatioService.delTenant(tenantCode,type); + return new Result().ok("删除成功"); + } + + + @GetMapping("getAllUnit") + @Operation(summary="获取选中企业下的所有单位") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒") + }) + public Result> getAllUnit(@RequestParam Long tenantCode,@RequestParam String type){ + Set result = iotCarbonRatioService.getAllUnit(tenantCode,type); + return new Result>().ok(result); + } + + + @GetMapping("getAllDistrict") + @Operation(summary="获取选中企业下的所有区域") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "type", description = "配置类型必传 1碳排,2折标媒") + }) + public Result> getAllDistrict(@RequestParam Long tenantCode,@RequestParam String type){ + Set result = iotCarbonRatioService.getAllDistrict(tenantCode,type); + return new Result>().ok(result); + } + + + @GetMapping("getRatio") + @Operation(summary="获取所有运输配置") + public Result> getRatio(){ + return new Result>().ok(iotCarbonRatioService.getRatio()); + } + + + + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotCarbonRatioService.listAs(params, IotCarbonRatioDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "iot_carbon_ratio", list, IotCarbonRatioExcel.class); + *} + */ + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/CarBonRatioResult.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/CarBonRatioResult.java new file mode 100644 index 0000000..cd65e98 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/CarBonRatioResult.java @@ -0,0 +1,13 @@ +package com.thing.carbontrack.ratio.dto; + + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CarBonRatioResult { + private Long crId; + private String name; + private BigDecimal ratio; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/IotCarbonRatioDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/IotCarbonRatioDTO.java new file mode 100644 index 0000000..83e93f4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/dto/IotCarbonRatioDTO.java @@ -0,0 +1,62 @@ +package com.thing.carbontrack.ratio.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* iot_carbon_ratio +* +* @author xc +* @since 3.0 2024-05-11 +*/ +@Data +@Schema(description = "折标煤碳排因子基本信息") +public class IotCarbonRatioDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "计量单位") + private String unit; + @Schema(description = "折标媒/碳排系数") + private BigDecimal ratio; + @Schema(description = "来源") + private String source; + @Schema(description = "备注") + private String remarks; + @Schema(description = "类型,1碳排,2折标媒") + private String type; + @Schema(description = "适用时间范围起始日期") + private Date beginTime; + @Schema(description = "用时间范围截止日期") + private Date endTime; + @Schema(description = "所属企业/租户code") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "适用地区") + private String district; + + @Schema(description = "过程名称") + private String evName; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/entity/IotCarbonRatioEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/entity/IotCarbonRatioEntity.java new file mode 100644 index 0000000..977ece8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/entity/IotCarbonRatioEntity.java @@ -0,0 +1,65 @@ +package com.thing.carbontrack.ratio.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * iot_carbon_ratio + * + * @author xc + * @since 3.0 2024-05-11 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_ratio") +public class IotCarbonRatioEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 能源品种id + */ + private Long evId; + /** + * 计量单位 + */ + private String unit; + /** + * 折标媒/碳排系数 + */ + private BigDecimal ratio; + /** + * 来源 + */ + private String source; + /** + * 备注 + */ + private String remarks; + /** + * 类型,1碳排,2折标媒 + */ + private String type; + + /** + * 适用时间范围起始日期 + */ + private Date beginTime; + /** + * 适用时间范围截止日期 + */ + private Date endTime; + /** + * 适用地区 + */ + private String district; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/mapper/IotCarbonRatioMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/mapper/IotCarbonRatioMapper.java new file mode 100644 index 0000000..0b8b4db --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/mapper/IotCarbonRatioMapper.java @@ -0,0 +1,26 @@ +package com.thing.carbontrack.ratio.mapper; + +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.dto.IotCarbonRatioDTO; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* iot_carbon_ratio +* +* @author xc +* @since 3.0 2024-05-11 +*/ +@Mapper +public interface IotCarbonRatioMapper extends PowerBaseMapper { + + List getRatio(); + + CarBonRatioResult getRatioByEvIdAndTime(Long evId, String endTime); + + List getLatestRatioByEvIds(@Param("evIds") List evIds); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/IotCarbonRatioService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/IotCarbonRatioService.java new file mode 100644 index 0000000..ad9a79d --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/IotCarbonRatioService.java @@ -0,0 +1,46 @@ +package com.thing.carbontrack.ratio.service; + +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.dto.IotCarbonRatioDTO; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.tenant.dto.SysTenantDTO; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * iot_carbon_ratio + * + * @author xc + * @since 3.0 2024-05-11 + */ +public interface IotCarbonRatioService extends IBaseService { + + PageData pageIotCarbonRatioDTO(Map params, Class iotCarbonRatioDTOClass); + + List listIotCarbonRatioDTOs(Map params); + + List tenantList(String type); + + void delTenant(Long tenantCode, String type); + + Set getAllUnit(Long tenantCode, String type); + + void saveDtoIotCarbonRatioDTO(IotCarbonRatioDTO dto); + + void updateDtoIotCarbonRatioDTO(IotCarbonRatioDTO dto); + + IotCarbonRatioEntity queryOnByEvIdAndType(Long evId,String type); + + List getRatio(); + + CarBonRatioResult getRatioByEvIdAndTime(Long evId, Date endTime); + + Set getAllDistrict(Long tenantCode, String type); + + List getLatestRatioByEvIds(List evIds); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/impl/IotCarbonRatioServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/impl/IotCarbonRatioServiceImpl.java new file mode 100644 index 0000000..08072f4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/ratio/service/impl/IotCarbonRatioServiceImpl.java @@ -0,0 +1,272 @@ +package com.thing.carbontrack.ratio.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.mapper.CarbonEnergyVarietyMapper; +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.dto.IotCarbonRatioDTO; +import com.thing.carbontrack.ratio.entity.IotCarbonRatioEntity; +import com.thing.carbontrack.ratio.mapper.IotCarbonRatioMapper; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantGroupService; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * iot_carbon_ratio + * + * @author xc + * @since 3.0 2024-05-11 + */ +@Service +public class IotCarbonRatioServiceImpl extends BaseServiceImpl implements IotCarbonRatioService { + + @Autowired + SysTenantMapper sysTenantDao; + + @Autowired + SysTenantGroupService sysTenantGroupService; + + @Autowired + private CarbonEnergyVarietyMapper carbonEnergyVarietyMapper; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + Long tenantCode = MapUtils.getLong(params,"tenantCode"); + String type = MapUtils.getString(params,"type"); + String district = MapUtils.getString(params,"district"); + + if(ObjectUtil.isNotEmpty(tenantCode)){ + wrapper.eq("tenant_code", tenantCode); + }else { + wrapper.eq("tenant_code", 1001L); + } + String key = MapUtils.getString(params,"key"); + if(ObjectUtil.isNotEmpty(key)){ + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.like("name",key); + + List entities= carbonEnergyVarietyMapper.selectListByQuery(queryWrapper); + Set ids = new HashSet<>(); + entities.forEach(temp->{ + ids.add(temp.getId()); + }); + wrapper.in("ev_id",ids); + } + String time = MapUtils.getString(params,"time"); + if(ObjectUtils.isNotEmpty(time)){ + SimpleDateFormat format = new SimpleDateFormat("yyyy"); + try { + Date date = format.parse(time); + SimpleDateFormat formats = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + wrapper.and("'"+formats.format(date)+"' BETWEEN begin_time AND end_time"); + } catch (ParseException ignored) { + } + } + wrapper.eq("type", type); + wrapper.eq("district",district, ObjectUtils.isNotEmpty(district)); + + + return wrapper; + } + + + @Override + public PageData pageIotCarbonRatioDTO(Map params, Class iotCarbonRatioDTOClass) { + return getPageData(params,IotCarbonRatioDTO.class); + } + + @Override + public List listIotCarbonRatioDTOs(Map params) { + Long tenantCode = MapUtils.getLong(params,"tenantCode"); + List dtoList = listAs(params,IotCarbonRatioDTO.class); + if(ObjectUtil.isNull(dtoList)||dtoList.isEmpty()){ + params.put("tenantCode","1001"); + dtoList = listAs(params,IotCarbonRatioDTO.class); + dtoList.forEach(temp->{ + temp.setId(null); + temp.setTenantCode(tenantCode); + temp.setCreator(SecurityUser.getUserId()); + temp.setCreateDate(System.currentTimeMillis()); + }); + List entityList = ConvertUtils.sourceToTarget(dtoList,IotCarbonRatioEntity.class); + this.saveBatch(entityList); + } + return dtoList; } + + @Override + public List tenantList(String type) { + Map params = new HashMap<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + List tenantCodeList; + List resultTenantCodeList= new ArrayList<>(); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + tenantCodeList=sysTenantGroupService.getChildren(tenantCode); + tenantCodeList.add(tenantCode); + tenantCodeList.add(1001L); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("type",type); + wrapper.in("tenant_code",tenantCodeList).select("tenant_code"); + mapper.selectListByQuery(wrapper).forEach(temp->{ + resultTenantCodeList.add(temp.getTenantCode()); + }); + }else { + this.mapper.selectListByQuery(QueryWrapper.create().eq("type",type).and("tenant_code is not null").select("tenant_code")).forEach(temp->{ + resultTenantCodeList.add(temp.getTenantCode()); + }); + } + if(resultTenantCodeList.isEmpty()){ + resultTenantCodeList.add(-1L); + } + params.put("tenantCodeList",resultTenantCodeList); + return sysTenantDao.queryList(params).stream().sorted(Comparator.comparingLong(SysTenantDTO::getTenantCode)).collect(Collectors.toList()); + + } + + @Override + public void delTenant(Long tenantCode, String type) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code",tenantCode); + wrapper.eq("type",type); + this.mapper.deleteByQuery(wrapper); + } + + @Override + public Set getAllUnit(Long tenantCode, String type) { + Set result = new HashSet<>(); + Map param = new HashMap<>(); + param.put("tenant_code",tenantCode); + param.put("type",type); + this.listByMap(param).forEach(temp->{ + if(StringUtils.isNotEmpty(temp.getUnit())){ + result.add(temp.getUnit()); + } + }); + return result; + } + + @Override + public Set getAllDistrict(Long tenantCode, String type) { + Set result = new HashSet<>(); + Map param = new HashMap<>(); + param.put("tenant_code",tenantCode); + param.put("type",type); + this.listByMap(param).forEach(temp->{ + if(StringUtils.isNotEmpty(temp.getDistrict())){ + result.add(temp.getDistrict()); + } + }); + return result; + } + + @Override + public List getLatestRatioByEvIds(List evIds) { + return mapper.getLatestRatioByEvIds(evIds); + } + + + @Override + public void saveDtoIotCarbonRatioDTO(IotCarbonRatioDTO dto) { + checkBusinessProperties(dto); + long currentTime = System.currentTimeMillis(); + dto.setCreateDate(currentTime); + dto.setUpdateDate(currentTime); + saveDto(dto); + } + + + @Override + public void updateDtoIotCarbonRatioDTO(IotCarbonRatioDTO dto) { + checkBusinessProperties(dto); + long currentTime = System.currentTimeMillis(); + dto.setUpdateDate(currentTime); + updateDto(dto); + } + + @Override + public IotCarbonRatioEntity queryOnByEvIdAndType(Long evId, String type) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("ev_id",evId); + wrapper.eq("type",type); + wrapper.orderBy("end_time",Boolean.FALSE); + return this.getOne(wrapper); + } + + @Override + public List getRatio() { + return this.mapper.getRatio(); + } + + @Override + public CarBonRatioResult getRatioByEvIdAndTime(Long evId, Date endTime) { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + CarBonRatioResult result = this.mapper.getRatioByEvIdAndTime(evId,format.format(endTime)); + if(ObjectUtil.isEmpty(result)){ + result= this.mapper.getRatioByEvIdAndTime(evId,null); + } + return result; + } + + + + + /** 校验当前能源价格对象的业务属性: 同一个属性,适用日期不允许存在交叉 */ + private void checkBusinessProperties(IotCarbonRatioDTO dto) { + QueryColumn BEGIN_TIME = new QueryColumn("begin_time"); + QueryColumn END_TIME = new QueryColumn("end_time"); + + Set ids = + mapper + .selectListByQuery( + QueryWrapper.create() + .eq(IotCarbonRatioEntity::getEvId, dto.getEvId()) + .eq(IotCarbonRatioEntity::getTenantCode, dto.getTenantCode(), Objects.nonNull(dto.getTenantCode())) + .eq(IotCarbonRatioEntity::getType,dto.getType()) + .and( + (BEGIN_TIME.le(dto.getBeginTime()) + .and(END_TIME.ge(dto.getBeginTime()))) + .or(BEGIN_TIME.le(dto.getEndTime()) + .and(END_TIME.ge(dto.getEndTime()))))) + .stream() + .map(IotCarbonRatioEntity::getId) + .collect(Collectors.toSet()); + + if (Objects.isNull(dto.getId()) && !ids.isEmpty()) { + throw new SysException("适用日期存在交叉,请修改"); + } + // 存在id,则说明是更新操作,判断除了自身之外是否还有其他 + if (Objects.nonNull(dto.getId())) { + int otherCount = + ids.stream() + .filter(id -> !Objects.equals(id, dto.getId())) + .collect(Collectors.toSet()) + .size(); + if (otherCount > 0) { + throw new SysException("适用日期存在交叉,请修改"); + } + } + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportConfigController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportConfigController.java new file mode 100644 index 0000000..e6ee3f8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportConfigController.java @@ -0,0 +1,87 @@ +package com.thing.carbontrack.report.controller; + +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.service.IotCarbonProductionReportConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 碳足迹报告配置 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@RestController +@RequestMapping("v2/carbon/track/report/config") +@Tag(name="碳足迹报告配置【企业侧】") +@RequiredArgsConstructor +public class IotCarbonProductionReportConfigController { + + private final IotCarbonProductionReportConfigService iotCarbonProductionReportConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionReportConfigService.getPageData(params, IotCarbonProductionReportConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonProductionReportConfigDTO data = iotCarbonProductionReportConfigService.getByIdAs(id, IotCarbonProductionReportConfigDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionReportConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonProductionReportConfigService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionReportConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProductionReportConfigService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionReportConfigService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportController.java new file mode 100644 index 0000000..6399d13 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/controller/IotCarbonProductionReportController.java @@ -0,0 +1,122 @@ +package com.thing.carbontrack.report.controller; + +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.report.service.IotCarbonProductionReportService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** +* 产品碳足迹报告 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@RestController +@RequestMapping("v2/carbon/track/report") +@Tag(name="产品碳足迹报告【企业侧】") +@RequiredArgsConstructor +public class IotCarbonProductionReportController { + + private final IotCarbonProductionReportService iotCarbonProductionReportService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productIds", description = "产品id列表,按逗号分隔"), + @Parameter(name = "start", description = "报告日期范围开始: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "end", description = "报告日期范围结束: yyyy-MM-dd HH:mm:ss"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionReportService.getPageData(params, IotCarbonProductionReportDTO.class); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "productIds", description = "产品id列表,按逗号分隔"), + @Parameter(name = "start", description = "报告日期范围开始: yyyy-MM-dd HH:mm:ss"), + @Parameter(name = "end", description = "报告日期范围结束: yyyy-MM-dd HH:mm:ss"), + }) + public Result> list(@Parameter(hidden = true) @RequestParam Map params){ + List list = iotCarbonProductionReportService.listAs(params, IotCarbonProductionReportDTO.class); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + Map params = new HashMap<>(1); + params.put("id", id); + List reports = iotCarbonProductionReportService.listAs(params, IotCarbonProductionReportDTO.class); + IotCarbonProductionReportDTO data = reports.get(0); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionReportDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonProductionReportService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionReportDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProductionReportService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionReportService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("generate/view") + @Operation(summary="生成报告预览") + @LogOperation("生成报告预览") + public Result generateReport(@RequestBody IotCarbonProductionReportConfigDTO config){ + IotCarbonProductionReportDTO data = iotCarbonProductionReportService.generateReport(config); + String reportCode = + iotCarbonProductionReportService.generateReportCode( + null, data.getBoundaryStart(), data.getBoundaryEnd()); + data.setReportCode(reportCode); + return new Result().ok(data); + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportConfigDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportConfigDTO.java new file mode 100644 index 0000000..83b356c --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportConfigDTO.java @@ -0,0 +1,34 @@ +package com.thing.carbontrack.report.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 碳足迹报告配置 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@Data +@Schema(description = "碳足迹报告配置") +public class IotCarbonProductionReportConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "数据边界类型:1-自然月;2-自然年;3-自定义") + private Integer boundaryType; + @Schema(description = "数据边界开始时间:只有自定义边界才有值") + private Long boundaryStart; + @Schema(description = "数据边界结束时间:只有自定义边界才有值") + private Long boundaryEnd; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportDTO.java new file mode 100644 index 0000000..767efca --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonProductionReportDTO.java @@ -0,0 +1,61 @@ +package com.thing.carbontrack.report.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 产品碳足迹报告 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@Data +@Schema(description = "产品碳足迹报告") +public class IotCarbonProductionReportDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "产品id") + private Long productId; + @Schema(description = "产品名称") + private String productName; + @Schema(description = "产品模型") + private String productModel; + @Schema(description = "功能单位") + private String functionUnit; + @Schema(description = "碳足报告配置id") + private Long configId; + @Schema(description = "系统边界类型:1-摇篮到大门,2-摇篮到坟墓") + private String boundary; + @Schema(description = "数据边界类型:1-自然月;2-自然年;3-自定义") + private Integer boundaryType; + @Schema(description = "数据边界开始时间") + private Date boundaryStart; + @Schema(description = "数据边界结束时间") + private Date boundaryEnd; + @Schema(description = "报告有效期") + private Long validEndTime; + @Schema(description = "碳足迹值") + private BigDecimal totalCarbon; + @Schema(description = "各阶段碳足迹占比") + private String stagePercentJson; + @Schema(description = "企业名称") + private String companyName; + @Schema(description = "报告编号") + private String reportCode; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonResultReport.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonResultReport.java new file mode 100644 index 0000000..8d239be --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/dto/IotCarbonResultReport.java @@ -0,0 +1,93 @@ +package com.thing.carbontrack.report.dto; + +import com.thing.common.core.utils.AggUtil; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Map; +import java.util.Objects; + +import static com.thing.common.core.enumeration.CarbonLifecycleEnum.*; +import static java.math.RoundingMode.HALF_UP; + +/** + * @author siyang + * @date 2024/5/31 09:28 + * @description 数据库查询结果:从result表聚合出的report数据 + */ +@Data +public class IotCarbonResultReport { + + /** 系统边界类型:1-摇篮到大门,2-摇篮到坟墓 */ + private String boundary; + + /** 碳足迹总值 */ + private BigDecimal totalCarbon; + + /** 碳足迹:原料获取阶段 */ + private BigDecimal mptCarbon; + + /** 碳足迹:生产制造阶段 */ + private BigDecimal pmCarbon; + + /** 碳足迹:产品运输阶段 */ + private BigDecimal ptCarbon; + + /** 碳足迹:产品使用阶段 */ + private BigDecimal puCarbon; + + /** 碳足迹:废弃处理阶段 */ + private BigDecimal disposeCarbon; + + /** 企业名称 */ + private String companyName; + + public BigDecimal summingCarbon() { + BigDecimal totalCarbon = AggUtil.sum(mptCarbon,pmCarbon,ptCarbon,puCarbon,disposeCarbon); + setTotalCarbon(totalCarbon.setScale(4, HALF_UP)); + return this.totalCarbon; + } + + public BigDecimal calcMptPercent() { + return calcPercent(mptCarbon); + } + + public BigDecimal calcPmPercent() { + return calcPercent(pmCarbon); + } + + public BigDecimal calcPtPercent() { + return calcPercent(ptCarbon); + } + + public BigDecimal calcPuPercent() { + return calcPercent(puCarbon); + } + + public BigDecimal calcDisposePercent() { + return calcPercent(disposeCarbon); + } + + private BigDecimal calcPercent(BigDecimal carbonVal) { + return divide(carbonVal, totalCarbon); + } + + public static IotCarbonResultReport convert(Map carbonLifecycleMap) { + IotCarbonResultReport res = new IotCarbonResultReport(); + res.setMptCarbon(carbonLifecycleMap.get(MATERIAL_PURCHASE_TRANSPORT.getDesc())); + res.setPmCarbon(carbonLifecycleMap.get(PRODUCT_MANUFACTURE.getDesc())); + res.setPtCarbon(carbonLifecycleMap.get(PRODUCT_TRANSPORT.getDesc())); + res.setPuCarbon(carbonLifecycleMap.get(PRODUCT_USAGE.getDesc())); + res.setDisposeCarbon(carbonLifecycleMap.get(DISPOSE.getDesc())); + res.setTotalCarbon(res.summingCarbon()); + return res; + } + + private static BigDecimal divide(BigDecimal value, BigDecimal count) { + if (Objects.isNull(value)) { + return null; + } + return value.multiply(BigDecimal.valueOf(100)).divide(count, 1, RoundingMode.HALF_UP); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportConfigEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportConfigEntity.java new file mode 100644 index 0000000..81d76eb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportConfigEntity.java @@ -0,0 +1,51 @@ +package com.thing.carbontrack.report.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 碳足迹报告配置 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_report_config") +public class IotCarbonProductionReportConfigEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id + private Long id; + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 产品id + */ + private Long productId; + /** + * 数据边界类型:1-自然月;2-自然年;3-自定义 + */ + private Integer boundaryType; + /** + * 数据边界开始时间:只有自定义边界才有值 + */ + private Long boundaryStart; + /** + * 数据边界结束时间:只有自定义边界才有值 + */ + private Long boundaryEnd; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportEntity.java new file mode 100644 index 0000000..58e61e8 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/entity/IotCarbonProductionReportEntity.java @@ -0,0 +1,72 @@ +package com.thing.carbontrack.report.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 产品碳足迹报告 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_report") +public class IotCarbonProductionReportEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 产品id + */ + private Long productId; + /** + * 碳足报告配置id + */ + private Long configId; + /** + * 系统边界类型:1-摇篮到大门,2-摇篮到坟墓 + */ + private String boundary; + /** + * 数据边界类型:1-自然月;2-自然年;3-自定义 + */ + private Integer boundaryType; + /** + * 数据边界开始时间 + */ + private Date boundaryStart; + /** + * 数据边界结束时间 + */ + private Date boundaryEnd; + /** + * 报告有效期 + */ + private Long validEndTime; + /** + * 碳足迹值 + */ + private BigDecimal totalCarbon; + /** + * 各阶段碳足迹占比 + */ + private String stagePercentJson; + /** + * 企业名称 + */ + private String companyName; + /** + * 报告编号 + */ + private String reportCode; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportConfigMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportConfigMapper.java new file mode 100644 index 0000000..3709c94 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.report.mapper; + +import com.thing.carbontrack.report.entity.IotCarbonProductionReportConfigEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 碳足迹报告配置 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@Mapper +public interface IotCarbonProductionReportConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportMapper.java new file mode 100644 index 0000000..68723fe --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/mapper/IotCarbonProductionReportMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.report.mapper; + +import com.thing.carbontrack.report.entity.IotCarbonProductionReportEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 产品碳足迹报告 +* +* @author ssy dev@lrd.com +* @since 3.0 2024-07-03 +*/ +@Mapper +public interface IotCarbonProductionReportMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportConfigService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportConfigService.java new file mode 100644 index 0000000..1dc9951 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportConfigService.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.report.service; + +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportConfigEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 碳足迹报告配置 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +public interface IotCarbonProductionReportConfigService extends IBaseService { + + void handleSave(IotCarbonProductionReportConfigDTO dto); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportService.java new file mode 100644 index 0000000..1b8e387 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/IotCarbonProductionReportService.java @@ -0,0 +1,26 @@ +package com.thing.carbontrack.report.service; + +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportEntity; +import com.thing.common.orm.service.IBaseService; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Date; + +/** + * 产品碳足迹报告 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +public interface IotCarbonProductionReportService extends IBaseService { + + IotCarbonProductionReportDTO generateReport(IotCarbonProductionReportConfigDTO config); + + String generateReportCode(Long configId, Date boundaryStart, Date boundaryEnd); + + void handleSave(IotCarbonProductionReportDTO dto); + + Pair getBoundary(IotCarbonProductionReportConfigDTO config); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportConfigServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportConfigServiceImpl.java new file mode 100644 index 0000000..23a4db4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportConfigServiceImpl.java @@ -0,0 +1,48 @@ +package com.thing.carbontrack.report.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportConfigEntity; +import com.thing.carbontrack.report.mapper.IotCarbonProductionReportConfigMapper; +import com.thing.carbontrack.report.service.IotCarbonProductionReportConfigService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Objects; + +/** + * 碳足迹报告配置 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +@Service +public class IotCarbonProductionReportConfigServiceImpl extends BaseServiceImpl implements IotCarbonProductionReportConfigService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void handleSave(IotCarbonProductionReportConfigDTO dto) { + // 检验是否存在 + IotCarbonProductionReportConfigEntity entity = + getOne( + QueryWrapper.create() + .eq(IotCarbonProductionReportConfigEntity::getProductId, dto.getProductId()) + .eq(IotCarbonProductionReportConfigEntity::getBoundaryType, dto.getBoundaryType()) + .limit(1)); + if (Objects.nonNull(entity)) { + // 存在则更新 + dto.setId(entity.getId()); + updateDto(dto); + } else { + // 不存在则新增 + saveDto(dto); + } + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportServiceImpl.java new file mode 100644 index 0000000..c48eb75 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/report/service/impl/IotCarbonProductionReportServiceImpl.java @@ -0,0 +1,161 @@ +package com.thing.carbontrack.report.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportEntity; +import com.thing.carbontrack.report.mapper.IotCarbonProductionReportMapper; +import com.thing.carbontrack.report.service.IotCarbonProductionReportService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.*; + +import static com.thing.carbontrack.production.entity.table.IotCarbonProductionVarietyEntityTableDef.IOT_CARBON_PRODUCTION_VARIETY_ENTITY; +import static com.thing.carbontrack.report.entity.table.IotCarbonProductionReportConfigEntityTableDef.IOT_CARBON_PRODUCTION_REPORT_CONFIG_ENTITY; +import static com.thing.carbontrack.report.entity.table.IotCarbonProductionReportEntityTableDef.IOT_CARBON_PRODUCTION_REPORT_ENTITY; + +/** + * 产品碳足迹报告 + * + * @author ssy dev@lrd.com + * @since 3.0 2024-07-03 + */ +@Service +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +public class IotCarbonProductionReportServiceImpl extends BaseServiceImpl implements IotCarbonProductionReportService { + private final IotCarbonProductionResultService resultService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper queryWrapper = new QueryWrapper(); + + Long id = MapUtils.getLong(params, "id"); + String productIdStr = MapUtils.getString(params, "productIds"); + String start = MapUtils.getString(params, "start"); + String end = MapUtils.getString(params, "end"); + + List productIds = + StringUtils.isBlank(productIdStr) + ? null + : Arrays.stream(productIdStr.split(",")) + .map(String::trim) + .map(Long::valueOf) + .toList(); + Long startTs = DateTimeUtils.convertTimeToLong(start); + Long endTs = DateTimeUtils.convertTimeToLong(end); + + queryWrapper + .select( + IOT_CARBON_PRODUCTION_REPORT_ENTITY.ALL_COLUMNS, + IOT_CARBON_PRODUCTION_VARIETY_ENTITY.NAME.as(IotCarbonProductionReportDTO::getProductName), + IOT_CARBON_PRODUCTION_VARIETY_ENTITY.MODEL.as(IotCarbonProductionReportDTO::getProductModel), + IOT_CARBON_PRODUCTION_VARIETY_ENTITY.F_UNIT.as(IotCarbonProductionReportDTO::getFunctionUnit)) + .from(IOT_CARBON_PRODUCTION_REPORT_ENTITY) + .innerJoin(IOT_CARBON_PRODUCTION_VARIETY_ENTITY) + .on(IOT_CARBON_PRODUCTION_REPORT_ENTITY.PRODUCT_ID.eq(IOT_CARBON_PRODUCTION_VARIETY_ENTITY.ID)) + .leftJoin(IOT_CARBON_PRODUCTION_REPORT_CONFIG_ENTITY) + .on(IOT_CARBON_PRODUCTION_REPORT_ENTITY.CONFIG_ID.eq(IOT_CARBON_PRODUCTION_REPORT_CONFIG_ENTITY.ID)) + .eq(IotCarbonProductionReportEntity::getId, id, Objects::nonNull) + .in(IotCarbonProductionReportEntity::getProductId, productIds, CollectionUtils.isNotEmpty(productIds)) + .ge(IotCarbonProductionReportEntity::getCreateDate, startTs, Objects::nonNull) + .le(IotCarbonProductionReportEntity::getCreateDate, endTs, Objects::nonNull); + + return queryWrapper; + } + + @Override + public void handleSave(IotCarbonProductionReportDTO dto) { + // 检测是否已存在相同配置的报告,存在则更新 + IotCarbonProductionReportEntity latestOne = + getOne( + QueryWrapper.create() + .eq(IotCarbonProductionReportEntity::getBoundary, dto.getBoundary()) + .eq(IotCarbonProductionReportEntity::getBoundaryType, dto.getBoundaryType()) + .eq(IotCarbonProductionReportEntity::getBoundaryStart, dto.getBoundaryStart()) + .eq(IotCarbonProductionReportEntity::getBoundaryEnd, dto.getBoundaryEnd()) + .orderBy(IotCarbonProductionReportEntity::getCreateDate) + .desc() + .limit(1)); + if(Objects.nonNull(latestOne)){ + latestOne.setTotalCarbon(dto.getTotalCarbon()); + latestOne.setStagePercentJson(dto.getStagePercentJson()); + updateById(latestOne); + return; + } + + String reportCode = generateReportCode(dto.getConfigId(), dto.getBoundaryStart(), dto.getBoundaryEnd()); + dto.setReportCode(reportCode); + saveDto(dto); + } + + + @Override + public IotCarbonProductionReportDTO generateReport(IotCarbonProductionReportConfigDTO config) { + Pair timePair = getBoundary(config); + return resultService.generateReport( + config.getProductId(), + config.getBoundaryType(), + new Date(timePair.getLeft()), + new Date(timePair.getRight()), + config.getTenantCode()); + } + + public String generateReportCode(Long configId, Date boundaryStart, Date boundaryEnd) { + String reportCodePrefix = "LRD-ECO-%s%s"; + int reportCodeNumLength = 3; + IotCarbonProductionReportEntity latestOne = + getOne( + QueryWrapper.create() + .eq(IotCarbonProductionReportEntity::getConfigId, configId, Objects::nonNull) + .eq(IotCarbonProductionReportEntity::getBoundaryStart, boundaryStart, Objects::nonNull) + .eq(IotCarbonProductionReportEntity::getBoundaryEnd, boundaryEnd, Objects::nonNull) + .orderBy(IotCarbonProductionReportEntity::getCreateDate) + .desc() + .limit(1)); + LocalDateTime now = LocalDateTime.now(); + String timeStr = + now.getYear() + + addZeroPrefix(now.getMonthValue(), 2) + + addZeroPrefix(now.getDayOfMonth(), 2); + if(Objects.isNull(latestOne)){ + return String.format(reportCodePrefix, timeStr, addZeroPrefix(1, reportCodeNumLength)); + } else { + Integer codeNum = getReportSuffix(latestOne.getReportCode()) + 1; + String numSuffix = addZeroPrefix(codeNum, reportCodeNumLength); + return String.format(reportCodePrefix, timeStr, numSuffix); + } + } + + private Integer getReportSuffix(String reportCode) { + if (StringUtils.isBlank(reportCode)) { + return 0; + } + String suffix = reportCode.substring(16); + return Integer.parseInt(suffix); + } + + private String addZeroPrefix(Integer num, int length){ + return StringUtils.leftPad(num.toString(), length, "0"); + } + + public Pair getBoundary(IotCarbonProductionReportConfigDTO config) { + Integer boundaryType = config.getBoundaryType(); + return switch (boundaryType) { + case 1 -> Pair.of(DateTimeUtils.monthStartTs(), DateTimeUtils.monthEndTs()); + case 2 -> Pair.of(DateTimeUtils.yearStartTs(), DateTimeUtils.yearEndTs()); + case 3 -> Pair.of(config.getBoundaryStart(), config.getBoundaryEnd()); + default -> throw new IllegalArgumentException("碳足迹配置错误:boundaryType=" + boundaryType); + }; + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/controller/RoutematrixController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/controller/RoutematrixController.java new file mode 100644 index 0000000..dacb958 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/controller/RoutematrixController.java @@ -0,0 +1,40 @@ +package com.thing.carbontrack.routematrix.controller; + + +import com.thing.carbontrack.routematrix.dto.PlaceParam; +import com.thing.carbontrack.routematrix.dto.PlaceReq; +import com.thing.carbontrack.routematrix.dto.RoutematrixReq; +import com.thing.carbontrack.routematrix.service.RoutematrixService; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +@RestController +@RequestMapping("v2/routematrix") +@RequiredArgsConstructor +public class RoutematrixController { + + + private final RoutematrixService routematrixService; + + @PostMapping("placeSearch") + @Operation(summary="根据地址获取地址经纬度") + public Result placeSearch(@RequestBody PlaceParam params){ + return new Result().ok(routematrixService.placeSearch(params)); + } + + + + @PostMapping("routematrixSearch") + @Operation(summary="出发地,目的的,获取导航距离") + public Result routematrixSearch(@RequestBody PlaceParam params){ + return new Result().ok(routematrixService.routematrixSearch(params)); + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceParam.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceParam.java new file mode 100644 index 0000000..c00105a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceParam.java @@ -0,0 +1,33 @@ +package com.thing.carbontrack.routematrix.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PlaceParam { + @Schema(description = "出发地") + private String query; + + private String region; + + private String output = "json"; + + private String page_size = "1"; + + @Schema(description = "目的地") + private String destination="31.697626,120.713591"; + + + public PlaceParam(String query) { + this.query = query; + } + + public String getRegion() { + return this.query; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceReq.java new file mode 100644 index 0000000..f828765 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/PlaceReq.java @@ -0,0 +1,13 @@ +package com.thing.carbontrack.routematrix.dto; + + +import lombok.Data; + +@Data +public class PlaceReq { + + private String lat; + + private String lng; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/RoutematrixReq.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/RoutematrixReq.java new file mode 100644 index 0000000..62c1eed --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/dto/RoutematrixReq.java @@ -0,0 +1,13 @@ +package com.thing.carbontrack.routematrix.dto; + + +import lombok.Data; + +@Data +public class RoutematrixReq { + + private String km; + + private String mm; + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/RoutematrixService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/RoutematrixService.java new file mode 100644 index 0000000..5f8085f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/RoutematrixService.java @@ -0,0 +1,13 @@ +package com.thing.carbontrack.routematrix.service; + +import com.thing.carbontrack.routematrix.dto.PlaceParam; +import com.thing.carbontrack.routematrix.dto.PlaceReq; +import com.thing.carbontrack.routematrix.dto.RoutematrixReq; + +public interface RoutematrixService { + + PlaceReq placeSearch(PlaceParam param); + + + RoutematrixReq routematrixSearch(PlaceParam param); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/impl/RoutematrixServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/impl/RoutematrixServiceImpl.java new file mode 100644 index 0000000..70c57f3 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/routematrix/service/impl/RoutematrixServiceImpl.java @@ -0,0 +1,145 @@ +package com.thing.carbontrack.routematrix.service.impl; + + +import com.alibaba.fastjson.JSONObject; +import com.thing.carbontrack.routematrix.dto.PlaceParam; +import com.thing.carbontrack.routematrix.dto.PlaceReq; +import com.thing.carbontrack.routematrix.dto.RoutematrixReq; +import com.thing.carbontrack.routematrix.service.RoutematrixService; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.stereotype.Service; +import org.springframework.web.util.UriUtils; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.URL; +import java.net.URLConnection; +import java.util.LinkedHashMap; +import java.util.Map; + +@Service +public class RoutematrixServiceImpl implements RoutematrixService { + public static final String AK = "fIA24tHyrIcpUuuPn18XktyGGFD0pXNA"; + + public static final String placeSearchURL = "https://api.map.baidu.com/place/v2/search?"; + + public static String routematrixURL = "https://api.map.baidu.com/routematrix/v2/driving?"; + + + + + + + @Override + public PlaceReq placeSearch(PlaceParam param){ + PlaceReq req = new PlaceReq(); + Map params = new LinkedHashMap<>(); + params.put("query", param.getQuery()); + params.put("region", param.getRegion()); + params.put("output", param.getOutput()); + params.put("page_size", param.getPage_size()); + params.put("ak", AK); + try { + String result = requestGetAK(placeSearchURL, params); + JSONObject object = JSONObject.parseObject(result); + String result_type = object.getString("result_type"); + if(result_type.equals("poi_type")){ + if(ObjectUtils.isNotEmpty(object.getJSONArray("results"))){ + JSONObject info = (JSONObject) object.getJSONArray("results").get(0); + JSONObject location = info.getJSONObject("location"); + req.setLat(location.getString("lat")); + req.setLng(location.getString("lng")); + } + } + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + return req; + } + + + + @Override + public RoutematrixReq routematrixSearch(PlaceParam param) { + RoutematrixReq reqResult = new RoutematrixReq(); + + PlaceReq origins = null; + try { + origins = placeSearch(param); + } catch (Exception e) { + e.printStackTrace(); + reqResult.setKm("0"); + return reqResult; + } + Map params = new LinkedHashMap<>(); + params.put("origins", origins.getLat()+","+origins.getLng()); + params.put("destinations", param.getDestination()); + params.put("ak", AK); + params.put("tactics", "11"); + try { + String result = requestGetAK(routematrixURL, params); + JSONObject object = JSONObject.parseObject(result); + Integer status = object.getInteger("status"); + if(status.equals(0)){ + JSONObject info = (JSONObject) object.getJSONArray("result").get(0); + JSONObject distance = info.getJSONObject("distance"); + String km = distance.getString("text"); + if(km.contains("米")){ + reqResult.setKm("0"); + }else { + reqResult.setKm(distance.getString("text").replace("公里","")); + reqResult.setMm(distance.getString("value")); + } + }else { + reqResult.setKm("0"); + } + } catch (Exception e) { + e.printStackTrace(); + reqResult.setKm("0"); + return reqResult; + } + return reqResult; + } + + + /** + * 统一的 调用方法 + * @param strUrl + * @param param + * @return + * @throws Exception + */ + public String requestGetAK(String strUrl, Map param) throws Exception { + if (strUrl == null || strUrl.length() <= 0 || param == null || param.size() <= 0) { + return null; + } + + StringBuffer queryString = new StringBuffer(); + queryString.append(strUrl); + for (Map.Entry pair : param.entrySet()) { + queryString.append(pair.getKey() + "="); + queryString.append(UriUtils.encode((String) pair.getValue(), "UTF-8") + "&"); + } + + if (queryString.length() > 0) { + queryString.deleteCharAt(queryString.length() - 1); + } + + URL url = new URL(queryString.toString()); + URLConnection httpConnection = url.openConnection(); + httpConnection.connect(); + + InputStreamReader isr = new InputStreamReader(httpConnection.getInputStream()); + BufferedReader reader = new BufferedReader(isr); + StringBuffer buffer = new StringBuffer(); + String line; + while ((line = reader.readLine()) != null) { + buffer.append(line); + } + reader.close(); + isr.close(); + return buffer.toString(); + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/controller/IotCarbonScreenController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/controller/IotCarbonScreenController.java new file mode 100644 index 0000000..72cb350 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/controller/IotCarbonScreenController.java @@ -0,0 +1,133 @@ +package com.thing.carbontrack.screen.controller; + +import com.thing.carbontrack.certification.dto.IotCarbonCertificateDTO; +import com.thing.carbontrack.certification.service.IotCarbonCertificateService; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.screen.dto.*; +import com.thing.carbontrack.screen.service.IotCarbonScreenService; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.MapUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/6/27 09:20 + * @description 企业边缘侧碳足迹看板 + */ +@RestController +@RequestMapping("v2/iot/carbon/screen") +@Tag(name = "企业边缘侧碳足迹看板") +@RequiredArgsConstructor +public class IotCarbonScreenController { + private final IotCarbonProductionResultService resultService; + private final IotCarbonProductionVarietyService productionVarietyService; + private final IotCarbonCertificateService certificateService; + private final IotCarbonScreenService carbonScreenService; + + @GetMapping("latest/batch/rank") + @Operation(summary = "最新批次产品碳足迹") + @Parameters({ + @Parameter(name = "keywords", description = "关键字,模糊搜索产品名称、编码"), + @Parameter(name = "month", description = "月份,不填写则默认查询近两个月,若近两个月没有则查全量"), + @Parameter(name = "boundary", description = "核算边界:1-摇篮到大门,2-摇篮到坟墓"), + }) + public Result> getLatestCarbonRank(@RequestParam Map params) { + List data = resultService.getLatestCarbonRank(params); + return new Result>().ok(data); + } + + + @GetMapping("trend/{productId}") + @Operation(summary = "产品碳足迹趋势") + public Result> getCarbonTrend( + @PathVariable("productId") Long productId) { + String boundary = null; + try { + boundary = productionVarietyService.getById(productId).getBoundary(); + } catch (Exception e) { + boundary="2"; + } + + List data = resultService.getCarbonTrend(productId, boundary); + return new Result>().ok(data); + } + + @GetMapping("trend/{productId}/{boundary}") + @Operation(summary = "产品碳足迹趋势") + public Result> getCarbonTrend( + @PathVariable("productId") Long productId, @PathVariable("boundary") String boundary) { + List data = resultService.getCarbonTrend(productId, boundary); + return new Result>().ok(data); + } + + @GetMapping("lifecycle/overview") + @Operation(summary = "产品碳足迹生命周期概览(中间底部和右侧底部)") + @Parameters({ + @Parameter(name = "productId", description = "产品id", required = true), + @Parameter(name = "prCode", description = "批次号", required = true), + @Parameter(name = "boundary", description = "核算边界:1-摇篮到大门,2-摇篮到坟墓"), + }) + public Result> getCarbonLifecycleOverview(@Parameter(hidden = true) @RequestParam Map params) { + List data = resultService.getCarbonLifecycleOverview(params); + return new Result>().ok(data); + } + + @GetMapping("lifecycle/detail") + @Operation(summary = "某阶段产品碳足迹详情(中间圆圈)") + @Parameters({ + @Parameter(name = "productId", description = "产品id", required = true), + @Parameter(name = "prCode", description = "批次号", required = true), + @Parameter(name = "boundary", description = "核算边界:1-摇篮到大门,2-摇篮到坟墓"), + @Parameter( + name = "stage", + description = "生命周期阶段:1-原材料采购与运输, 2-生产制造, 3-产品运输, 4-产品使用, 5-废弃处理", + required = true), + }) + public Result> getCarbonLifecycleDetail(@Parameter(hidden = true) @RequestParam Map params) { + List data = resultService.getCarbonLifecycleDetail(params); + return new Result>().ok(data); + } + + @GetMapping("product/info") + @Operation(summary = "产品信息") + @Parameters({ + @Parameter(name = "productId", description = "产品id", required = true), + @Parameter(name = "prCode", description = "批次号", required = true) + }) + public Result getCarbonProductInfo(@Parameter(hidden = true) @RequestParam Map params) { + Long productId = MapUtils.getLong(params, "productId"); + String prCode = MapUtils.getString(params, "prCode"); + ProductInfo data = resultService.getCarbonProductInfo(productId, prCode); + return new Result().ok(data); + } + + @GetMapping("certificate/info") + @Operation(summary = "证书信息") + @Parameters({@Parameter(name = "productId", description = "产品id", required = true)}) + public Result getCertificateBaseInfo(@Parameter(hidden = true) @RequestParam Map params) { + Long productId = MapUtils.getLong(params, "productId"); + IotCarbonCertificateDTO data = certificateService.getByProductId(productId); + return new Result().ok(data); + } + + @GetMapping("summary") + @Operation(summary = "总计") + public Result getCarbonSummary() { + CarbonSummary data = carbonScreenService.getCarbonSummary(); + return new Result().ok(data); + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/CarbonSummary.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/CarbonSummary.java new file mode 100644 index 0000000..26ed817 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/CarbonSummary.java @@ -0,0 +1,37 @@ +package com.thing.carbontrack.screen.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * @author siyang + * @date 2024/7/15 15:18 + * @description 大屏碳足迹汇总数据 + */ +@Data +@Schema(description = "大屏碳足迹汇总数据") +public class CarbonSummary { + @Schema(description = "当月总能耗") + private List energyUsageList; + + @Schema(description = "当月总碳排") + private BigDecimal carbon; + + @Schema(description = "核算产品数") + private Long productNum; + + @Data + public static class EnergyInfo { + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源品种名称") + private String evName; + @Schema(description = "能源单位") + private String unit; + @Schema(description = "能耗值") + private BigDecimal value; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycle.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycle.java new file mode 100644 index 0000000..b7829ba --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycle.java @@ -0,0 +1,42 @@ +package com.thing.carbontrack.screen.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Optional; + +/** + * @author siyang + * @date 2024/6/27 09:38 + * @description 产品碳足迹生命周期 + */ +@Data +@Schema(description = "产品碳足迹生命周期各阶段概览") +@NoArgsConstructor +public class ProductCarbonLifecycle { + + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "生命周期阶段名称") + private String stage; + + @Schema(description = "当前阶段总碳足迹值") + private BigDecimal carbon; + + @Schema(description = "当前阶段总碳足迹占比") + private BigDecimal percent; + + public ProductCarbonLifecycle(Long productId, String stage, BigDecimal carbon) { + this.productId = productId; + this.stage = stage; + this.carbon = round(carbon); + } + + private BigDecimal round(BigDecimal value) { + return Optional.ofNullable(value).orElse(BigDecimal.ZERO).setScale(4, RoundingMode.HALF_UP); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycleDetail.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycleDetail.java new file mode 100644 index 0000000..9dc2267 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductCarbonLifecycleDetail.java @@ -0,0 +1,149 @@ +package com.thing.carbontrack.screen.dto; + +import com.thing.carbontrack.productionResult.dto.*; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/6/27 09:38 + * @description 产品碳足迹生命周期某阶段详情 + */ +@Data +@Schema(description = "产品碳足迹生命周期某阶段详情") +@NoArgsConstructor +public class ProductCarbonLifecycleDetail { + + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "原料名称/工序名称/运输过程名称") + private String name; + + @Schema(description = "碳足迹值/能耗值") + private BigDecimal value; + + public ProductCarbonLifecycleDetail(Long productId, String name, BigDecimal value) { + this.productId = productId; + this.name = name; + this.value = value; + } + + public static List fromMpt( + Long productId, List details) { + return details.stream() + .map( + detail -> + new ProductCarbonLifecycleDetail( + productId, + detail.getMaterialName(), + round(detail.getAcquireCarbon()))) + .collect(Collectors.toList()); + } + + public static List fromMptForTransport( + Long productId, List details) { + return details.stream() + .map( + detail -> + new ProductCarbonLifecycleDetail( + productId, + detail.getMaterialName(), + round(detail.getTransportUse()))) + .collect(Collectors.toList()); + } + + public static List fromPmProcess( + Long productId, List details, Long finalNum) { + return details.stream() + .map( + detail -> + new ProductCarbonLifecycleDetail( + productId, + detail.getProcessName(), + divide(detail.getCarbon(), finalNum))) + .collect(Collectors.toList()); + } + + public static List fromPIndirect( + Long productId, List details, Long finalNum) { + return details.stream() + .map( + detail -> + new ProductCarbonLifecycleDetail( + productId, + detail.getProcessName(), + divide(detail.getCarbon(), finalNum))) + .collect(Collectors.toList()); + } + + public static List fromPt( + Long productId, List details) { + return details.stream() + .map( + detail -> + new ProductCarbonLifecycleDetail( + productId, + detail.getTransportType(), + round(detail.getTransportCarBonAvg()))) + .collect(Collectors.toList()); + } + + public static List fromPu( + Long productId, List details, Long finalNum) { + return details.stream() + .map( + detail -> { + String evName = detail.getEvName(); + String[] evArr = evName.split(" "); + return new ProductCarbonLifecycleDetail( + productId, evArr[0], divide(detail.getUsageCarbon(), finalNum)); + }) + .collect(Collectors.toList()); + } + + public static List fromDispose( + Long productId, List details) { + return details.stream() + .map( + detail -> { + String evName = detail.getEvName(); + String[] evArr = evName.split(" "); + return new ProductCarbonLifecycleDetail( + productId, evArr[0], round(detail.getEvUsageCarbonAvg())); + }) + .collect(Collectors.toList()); + } + + public static List fromDisposeForTransport( + Long productId, List details) { + return details.stream() + .map( + detail -> { + String evName = detail.getEvName(); + String[] evArr = evName.split(" "); + return new ProductCarbonLifecycleDetail( + productId, evArr[0], round(detail.getTransportCarBonAvg())); + }) + .collect(Collectors.toList()); + } + + private static BigDecimal round(BigDecimal value) { + return Optional.ofNullable(value).orElse(BigDecimal.ZERO).setScale(1, RoundingMode.HALF_UP); + } + + private static BigDecimal divide(BigDecimal val, Long num) { + if (Objects.isNull(val)) { + return null; + } + return val.divide(BigDecimal.valueOf(num), 1, RoundingMode.HALF_UP); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductInfo.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductInfo.java new file mode 100644 index 0000000..a7a67c0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/ProductInfo.java @@ -0,0 +1,40 @@ +package com.thing.carbontrack.screen.dto; + +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author siyang + * @date 2024/6/27 09:38 + * @description 产品碳足迹简要信息 + */ +@Data +@Schema(description = "产品碳足迹简要信息") +@NoArgsConstructor +public class ProductInfo { + + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "产品图片") + private String productUrl; + + @Schema(description = "功能单位") + private String functionUnit; + + @Schema(description = "核算边界:1-摇篮到大门,2-摇篮到坟墓") + private String boundary; + + public ProductInfo(IotCarbonProductionVarietyEntity product) { + this.productId = product.getId(); + this.productName = product.getName(); + this.productUrl = product.getUrl(); + this.functionUnit = product.getFUnit(); + this.boundary = product.getBoundary(); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/SimpleProductionCarbon.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/SimpleProductionCarbon.java new file mode 100644 index 0000000..33938b0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/dto/SimpleProductionCarbon.java @@ -0,0 +1,32 @@ +package com.thing.carbontrack.screen.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * @author siyang + * @date 2024/6/27 09:38 + * @description 简单产品碳足迹 + */ +@Data +@Schema(description = "简单产品碳足迹") +public class SimpleProductionCarbon { + + @Schema(description = "产品id") + private Long productId; + + @Schema(description = "产品名称") + private String productName; + + @Schema(description = "批次号") + private String prCode; + + @Schema(description = "碳足迹值") + private BigDecimal carbon; + + @Schema(description = "工单日期") + private Date prDate; +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/IotCarbonScreenService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/IotCarbonScreenService.java new file mode 100644 index 0000000..2941f3b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/IotCarbonScreenService.java @@ -0,0 +1,12 @@ +package com.thing.carbontrack.screen.service; + +import com.thing.carbontrack.screen.dto.CarbonSummary; + +/** + * @author siyang + * @date 2024/7/17 11:12 + */ +public interface IotCarbonScreenService { + + CarbonSummary getCarbonSummary(); +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/impl/IotCarbonScreenServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/impl/IotCarbonScreenServiceImpl.java new file mode 100644 index 0000000..a02bb36 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/screen/service/impl/IotCarbonScreenServiceImpl.java @@ -0,0 +1,149 @@ +package com.thing.carbontrack.screen.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.ratio.dto.IotCarbonRatioDTO; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.carbontrack.screen.dto.CarbonSummary; +import com.thing.carbontrack.screen.dto.CarbonSummary.EnergyInfo; +import com.thing.carbontrack.screen.service.IotCarbonScreenService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.service.IotThingDictService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/7/17 11:13 + */ +@Service +@RequiredArgsConstructor +public class IotCarbonScreenServiceImpl implements IotCarbonScreenService { + private final TsKvService tsKvService; + private final IotThingDictService dictService; + private final IotCarbonRatioService carbonRatioService; + private final CarbonEnergyVarietyService energyVarietyService; + private final IotCarbonProductionResultService productionResultService; + + + @Override + public CarbonSummary getCarbonSummary() { + Long tenantCode = UserContext.getRealTenantCode(); + + // 查询当月总能耗 + List evEntities = + energyVarietyService.list( + QueryWrapper.create().eq(CarbonEnergyVarietyEntity::getType, "能源")); + Map evCodeMap = + evEntities.stream() + .collect( + Collectors.toMap( + CarbonEnergyVarietyEntity::getCode, Function.identity())); + Set attrCodes = evCodeMap.keySet(); + Map dictMap = + dictService.findDictByCodes(attrCodes).stream() + .collect( + Collectors.toMap( + e -> e.getCode() + "mm", + Function.identity(), + (v1, v2) -> v1)); + + List tskvList = findTskv(tenantCode, dictMap.keySet()); + List energyInfoList = tskv2EvInfo(tskvList, evCodeMap, dictMap); + + // 计算当月总碳排 + BigDecimal monthCarbon = calculateMonthCarbon(energyInfoList); + + // 查询产品数量 + Long productNum = productionResultService.productCount(); + + + CarbonSummary data = new CarbonSummary(); + data.setEnergyUsageList(energyInfoList); + data.setCarbon(monthCarbon); + data.setProductNum(productNum); + return data; + } + + private List findTskv(Long tenantCode, Set attrCodes){ + Long startTs = DateTimeUtils.monthStartTs(); + Long endTs = System.currentTimeMillis(); + //常熟开关厂,企业物编码,被搞乱了,临时处理 + if(tenantCode.equals(463020701385752576L)){ + return tsKvService.findTsKvByCodeAndAttrs("CO_" + 91320581142037132L+"W", attrCodes, startTs, endTs, true); + }else { + return tsKvService.findTsKvByCodeAndAttrs("CO_" + tenantCode, attrCodes, startTs, endTs, true); + } + } + + private List tskv2EvInfo( + List tskvList, + Map evCodeMap, + Map dictMap) { + return tskvList.stream() + .map( + tskv -> { + EnergyInfo energyInfo = new EnergyInfo(); + String attrKey = tskv.getAttrKey(); + CarbonEnergyVarietyEntity evEntity = evCodeMap.get(AttributeTypeEnum.removeSuffix(attrKey)); + IotThingDictDTO dict = dictMap.get(attrKey); + + energyInfo.setEvId(evEntity.getId()); + energyInfo.setEvName(evEntity.getName()); + energyInfo.setUnit("万" + dict.getUnit()); + + String val = tskv.getVal(); + if (StringUtils.isNotBlank(val)) { + BigDecimal value = + new BigDecimal(val) + .divide( + new BigDecimal(10000), + 1, + RoundingMode.HALF_UP); + energyInfo.setValue(value); + } + return energyInfo; + }) + .toList(); + } + + private BigDecimal calculateMonthCarbon(List energyInfoList) { + List evIds = energyInfoList.stream().map(EnergyInfo::getEvId).toList(); + List latestRatioList = carbonRatioService.getLatestRatioByEvIds(evIds); + Map evRatioMap = + latestRatioList.stream() + .collect( + Collectors.toMap( + IotCarbonRatioDTO::getEvId, + IotCarbonRatioDTO::getRatio, + (v1, v2) -> v1)); + return energyInfoList.stream() + .map( + e -> { + BigDecimal ratio = evRatioMap.get(e.getEvId()); + if(null!=ratio){ + return e.getValue().multiply(ratio); + }else { + return BigDecimal.ZERO; + } + }) + .reduce(BigDecimal::add) + .orElse(null); + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareController.java new file mode 100644 index 0000000..6fd612b --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareController.java @@ -0,0 +1,70 @@ +package com.thing.carbontrack.share.controller; + +import com.thing.carbontrack.share.dto.IotCarbonShareDTO; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.carbontrack.task.DataSharingService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +/** +* 协议配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@RestController +@RequestMapping("v2/share/iotcarbonshare") +@Tag(name="协议配置") +@RequiredArgsConstructor +public class IotCarbonShareController { + + private final IotCarbonShareService iotCarbonShareService; + + //当前企业协议配置信息 + private final DataSharingService dataSharingService; + + @GetMapping("get") + @Operation(summary="信息") + public Result get(){ + IotCarbonShareDTO data = iotCarbonShareService.getIotCarbonShareDTO(); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonShareDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonShareService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonShareDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonShareService.updateDto(dto); + return new Result<>(); + } + + + @GetMapping("dataSharing") + @Operation(summary="共享,点击共享弹窗[是否确认共享,确认,调用该接口]") + public Result dataSharing(){ + dataSharingService.dataSharing(); + return new Result().ok("共享成功"); + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareDetailsController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareDetailsController.java new file mode 100644 index 0000000..b556e94 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/controller/IotCarbonShareDetailsController.java @@ -0,0 +1,97 @@ +package com.thing.carbontrack.share.controller; + +import com.thing.carbontrack.share.dto.IotCarbonShareDetailsDTO; +import com.thing.carbontrack.share.service.IotCarbonShareDetailsService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 共享配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@RestController +@RequestMapping("v2/share/iotcarbonsharedetails") +@Tag(name="共享配置") +@RequiredArgsConstructor +public class IotCarbonShareDetailsController { + + private final IotCarbonShareDetailsService iotCarbonShareDetailsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonShareDetailsService.getPageData(params, IotCarbonShareDetailsDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonShareDetailsDTO data = iotCarbonShareDetailsService.getByIdAs(id, IotCarbonShareDetailsDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonShareDetailsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonShareDetailsService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonShareDetailsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonShareDetailsService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonShareDetailsService.batchDelete(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotCarbonShareDetailsService.listAs(params, IotCarbonShareDetailsDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "共享配置", list, IotCarbonShareDetailsExcel.class); + *} + */ + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDTO.java new file mode 100644 index 0000000..f0b73fd --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDTO.java @@ -0,0 +1,44 @@ +package com.thing.carbontrack.share.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 协议配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@Data +@Schema(description = "协议配置") +public class IotCarbonShareDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "协议名称") + private String name; + @Schema(description = "公共服务地址") + private String url; + @Schema(description = "企业令牌token") + private String token; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDetailsDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDetailsDTO.java new file mode 100644 index 0000000..7d8606f --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/dto/IotCarbonShareDetailsDTO.java @@ -0,0 +1,56 @@ +package com.thing.carbontrack.share.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 共享配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@Data +@Schema(description = "共享配置") +public class IotCarbonShareDetailsDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "类型,1结果数据,2过程数据") + private String type; + @Schema(description = "产品id") + private Long mId; + @Schema(description = "产品编码") + private String code; + @Schema(description = "产品名称") + private String name; + @Schema(description = "所属行业/产品所属行业") + private String industry; + @Schema(description = "产品分类/产品所属行业子类型") + private String industrySub; + @Schema(description = "系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓") + private String boundary; + @Schema(description = "功能单位计量标准数") + private String fsu; + @Schema(description = "功能单位计量单位") + private String fUnit; + @Schema(description = "所属企业/租户code") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareDetailsEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareDetailsEntity.java new file mode 100644 index 0000000..085dca5 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareDetailsEntity.java @@ -0,0 +1,74 @@ +package com.thing.carbontrack.share.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 共享配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_share_details") +public class IotCarbonShareDetailsEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类型,1结果数据,2过程数据 + */ + private String type; + /** + * 产品id + */ + private Long mId; + /** + * 产品编码 + */ + private String code; + /** + * 产品名称 + */ + private String name; + /** + * 所属行业/产品所属行业 + */ + private String industry; + /** + * 产品分类/产品所属行业子类型 + */ + private String industrySub; + /** + * 系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓 + */ + private String boundary; + /** + * 功能单位计量标准数 + */ + private String fsu; + /** + * 功能单位计量单位 + */ + private String fUnit; + /** + * 所属企业/租户code + */ + private Long tenantCode; + /** + * 企业详情id + */ + private Long companyId; + /** + * 部门id + */ + private Long deptId; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareEntity.java new file mode 100644 index 0000000..f25f2d0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/entity/IotCarbonShareEntity.java @@ -0,0 +1,50 @@ +package com.thing.carbontrack.share.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 协议配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_share") +public class IotCarbonShareEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 协议名称 + */ + private String name; + /** + * 公共服务地址 + */ + private String url; + /** + * 企业令牌token + */ + private String token; + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 企业id + */ + private Long companyId; + /** + * 部门id + */ + private Long deptId; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareDetailsMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareDetailsMapper.java new file mode 100644 index 0000000..11da6e1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareDetailsMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.share.mapper; + +import com.thing.carbontrack.share.entity.IotCarbonShareDetailsEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 共享配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@Mapper +public interface IotCarbonShareDetailsMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareMapper.java new file mode 100644 index 0000000..272baea --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/mapper/IotCarbonShareMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.share.mapper; + +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 协议配置 +* +* @author xc +* @since 3.0 2024-06-06 +*/ +@Mapper +public interface IotCarbonShareMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareDetailsService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareDetailsService.java new file mode 100644 index 0000000..0113417 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareDetailsService.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.share.service; + +import com.thing.carbontrack.share.entity.IotCarbonShareDetailsEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 共享配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +public interface IotCarbonShareDetailsService extends IBaseService { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareService.java new file mode 100644 index 0000000..c2e1e85 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/IotCarbonShareService.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.share.service; + +import com.thing.carbontrack.share.dto.IotCarbonShareDTO; +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 协议配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +public interface IotCarbonShareService extends IBaseService { + + IotCarbonShareDTO getIotCarbonShareDTO(); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareDetailsServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareDetailsServiceImpl.java new file mode 100644 index 0000000..2c338b4 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareDetailsServiceImpl.java @@ -0,0 +1,36 @@ +package com.thing.carbontrack.share.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.share.entity.IotCarbonShareDetailsEntity; +import com.thing.carbontrack.share.mapper.IotCarbonShareDetailsMapper; +import com.thing.carbontrack.share.service.IotCarbonShareDetailsService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 共享配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +@Service +public class IotCarbonShareDetailsServiceImpl extends BaseServiceImpl implements IotCarbonShareDetailsService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String tenantCode = (String) params.get("tenantCode"); + if(ObjectUtil.isNotEmpty(tenantCode)){ + wrapper.eq("tenant_code",tenantCode); + }else { + wrapper.eq("tenant_code", UserContext.getTenantCode()); + } + return wrapper; + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareServiceImpl.java new file mode 100644 index 0000000..f8f8457 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/share/service/impl/IotCarbonShareServiceImpl.java @@ -0,0 +1,36 @@ +package com.thing.carbontrack.share.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.share.dto.IotCarbonShareDTO; +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.carbontrack.share.mapper.IotCarbonShareMapper; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 协议配置 + * + * @author xc + * @since 3.0 2024-06-06 + */ +@Service +public class IotCarbonShareServiceImpl extends BaseServiceImpl implements IotCarbonShareService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public IotCarbonShareDTO getIotCarbonShareDTO() { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", UserContext.getTenantCode()); + return this.getOneAs(wrapper,IotCarbonShareDTO.class); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/controller/IotCarbonProcessStepsController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/controller/IotCarbonProcessStepsController.java new file mode 100644 index 0000000..b1e8a0a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/controller/IotCarbonProcessStepsController.java @@ -0,0 +1,125 @@ +package com.thing.carbontrack.steps.controller; + +import com.thing.carbontrack.steps.dto.IotCarbonProcessResult; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 产品工艺工序 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/steps/iotcarbonprocesssteps") +@Tag(name="AAA产品工艺工序") +@RequiredArgsConstructor +public class IotCarbonProcessStepsController { + + private final IotCarbonProcessStepsService iotCarbonProcessStepsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "key", description = "物料名称关键字") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotCarbonProcessStepsService.IotCarbonProcessResultPage(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="当前企业所有工序列表") + public Result> list(@RequestParam Map params){ + List result = iotCarbonProcessStepsService.listAsIotCarbonProcessStepsDTO(params); + return new Result>().ok(result); + } + + @GetMapping("listByMid") + @Operation(summary="根据产品id,获取产品工序列表") + public Result> listByMid(@RequestParam Long mId){ + List result = iotCarbonProcessStepsService.listByMid(mId); + return new Result>().ok(result); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonProcessStepsDTO data = iotCarbonProcessStepsService.getByIdAs(id, IotCarbonProcessStepsDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProcessStepsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setCreateTime(System.currentTimeMillis()); + iotCarbonProcessStepsService.saveDto(dto); + return new Result<>(); + } + + @PostMapping("batchSave") + @Operation(summary="批量保存") + public Result save(@RequestBody List dtos){ + if(ObjectUtils.isEmpty(dtos)){ + return new Result<>(); + } + dtos.forEach(temp->{ + if(temp.getId()==null){ + temp.setCreateTime(System.currentTimeMillis()); + iotCarbonProcessStepsService.saveDto(temp); + }else { + iotCarbonProcessStepsService.updateDto(temp); + } + }); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProcessStepsDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProcessStepsService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + for (Long id : ids) { + iotCarbonProcessStepsService.deleteByMid(id); + } + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessResult.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessResult.java new file mode 100644 index 0000000..66120ca --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessResult.java @@ -0,0 +1,31 @@ +package com.thing.carbontrack.steps.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Data +public class IotCarbonProcessResult { + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "产品编码") + private String code; + @Schema(description = "产品名称") + private String name; + @Schema(description = "产品规格型号") + private String model; + @Schema(description = "种类 1成品 2半成品 ") + private String type; + private String fUnit; + @Schema(description = "系统边界,产品碳足迹生命周期边界1.摇篮到大门,2摇篮到坟墓") + private String boundary; + @Schema(description = "工艺流程") + private String steps; + @Schema(description = "产品标准单位") + private String f_unit; + + public String getF_unit() { + return fUnit; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessStepsDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessStepsDTO.java new file mode 100644 index 0000000..99692dc --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/dto/IotCarbonProcessStepsDTO.java @@ -0,0 +1,38 @@ +package com.thing.carbontrack.steps.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 产品工艺工序 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "产品工艺工序") +public class IotCarbonProcessStepsDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "物料id") + private Long mid; + @Schema(description = "序号") + private Long orderNum; + @Schema(description = "工艺工序编码") + private String processCode; + @Schema(description = "工艺工序名称") + private String processName; + @Schema(description = "能源介质id,依赖于能源品种基本信息,多个编码以英文逗号分割") + private String evIds; + @Schema(description = "创建时间") + private Long createTime; + + @Schema(description = "节点id/前端渲染坐标使用") + private String nodeId; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/entity/IotCarbonProcessStepsEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/entity/IotCarbonProcessStepsEntity.java new file mode 100644 index 0000000..ea27518 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/entity/IotCarbonProcessStepsEntity.java @@ -0,0 +1,62 @@ +package com.thing.carbontrack.steps.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.KeyType; +import com.mybatisflex.annotation.Table; +import com.mybatisflex.core.keygen.KeyGenerators; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 产品工艺工序 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_process_steps") +public class IotCarbonProcessStepsEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据主键id + */ + @Id(keyType = KeyType.Generator,value = KeyGenerators.snowFlakeId) + private Long id; + /** + * 物料id + */ + private Long mid; + /** + * 序号 + */ + private Long orderNum; + /** + * 工艺工序编码 + */ + private String processCode; + /** + * 工艺工序名称 + */ + private String processName; + /** + * 能源介质id,依赖于能源品种基本信息,多个编码以英文逗号分割 + */ + private String evIds; + + /** + * 创建时间 + */ + private String createTime; + /** + * 节点id/前端渲染坐标使用 + */ + private String nodeId; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/mapper/IotCarbonProcessStepsMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/mapper/IotCarbonProcessStepsMapper.java new file mode 100644 index 0000000..eff41ef --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/mapper/IotCarbonProcessStepsMapper.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.steps.mapper; + +import com.thing.carbontrack.steps.dto.IotCarbonProcessResult; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.entity.IotCarbonProcessStepsEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 产品工艺工序 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonProcessStepsMapper extends PowerBaseMapper { + + List selectListByProductionIds(List idList); + + Integer IotCarbonProcessResultPageCount(Map params); + + List IotCarbonProcessResultPage(Map params); + + String evStrByMid(Long mId); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/IotCarbonProcessStepsService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/IotCarbonProcessStepsService.java new file mode 100644 index 0000000..7ec62e1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/IotCarbonProcessStepsService.java @@ -0,0 +1,38 @@ +package com.thing.carbontrack.steps.service; + +import com.thing.carbontrack.pub.dto.ProcessJsonBean; +import com.thing.carbontrack.steps.dto.IotCarbonProcessResult; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.entity.IotCarbonProcessStepsEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; + +/** + * 产品工艺工序 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonProcessStepsService extends IBaseService { + + List listAsIotCarbonProcessStepsDTO(Map params); + + List listByMid(Long mId); + + PageData IotCarbonProcessResultPage(Map params); + + Long evNumByMid(Long mId); + + List listProcessJsonBeanByMid(Long mId); + + void saveWithJson(String processJson,Long pId); + + void saveOrUpdateWithJson(String processJson, Long productId); + + void deleteByMid(Long mId); + + void saveOrUpdateInfo(List stepsEntityList); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/impl/IotCarbonProcessStepsServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/impl/IotCarbonProcessStepsServiceImpl.java new file mode 100644 index 0000000..457fa31 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/steps/service/impl/IotCarbonProcessStepsServiceImpl.java @@ -0,0 +1,183 @@ +package com.thing.carbontrack.steps.service.impl; + +import com.alibaba.fastjson.JSONArray; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.production.dto.IotCarbonProductionVarietyDTO; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.pub.dto.ProcessJsonBean; +import com.thing.carbontrack.steps.dto.IotCarbonProcessResult; +import com.thing.carbontrack.steps.dto.IotCarbonProcessStepsDTO; +import com.thing.carbontrack.steps.entity.IotCarbonProcessStepsEntity; +import com.thing.carbontrack.steps.mapper.IotCarbonProcessStepsMapper; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 产品工艺工序 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonProcessStepsServiceImpl extends BaseServiceImpl implements IotCarbonProcessStepsService { + + @Autowired + private IotCarbonProductionVarietyService iotCarbonProductionVarietyService; + + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public List listAsIotCarbonProcessStepsDTO(Map params) { + //所有产品所有工序列表 + List productionVarietyDTO = iotCarbonProductionVarietyService.listAsIotCarbonProductionVarietyDTO(Map.of("type","1")); + //所有产品id + List idList = productionVarietyDTO.stream().map(IotCarbonProductionVarietyDTO::getId).toList(); + return this.mapper.selectListByProductionIds(idList); + } + + @Override + public List listByMid(Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("mid",mId); + wrapper.orderBy("order_num",Boolean.TRUE); + return this.listAs(wrapper,IotCarbonProcessStepsDTO.class); + } + + @Override + public PageData IotCarbonProcessResultPage(Map params) { + Long page = MapUtils.getLong(params,"page"); + String key = MapUtils.getString(params,"key"); + Long limit = MapUtils.getLong(params,"limit"); + params.put("page",page-1); + if(ObjectUtils.isNotEmpty(key)){ + params.put("key","%"+key+"%"); + } + params.put("limit",limit); + params.put("tenantCode", UserContext.getTenantCode()); + Integer count = this.mapper.IotCarbonProcessResultPageCount(params); + if (count == 0) { + return new PageData<>(Collections.emptyList(), 0); + } + List pageList = this.mapper.IotCarbonProcessResultPage(params); + return new PageData<>(pageList,count); + } + + @Override + public Long evNumByMid(Long mId) { + String evIds = this.mapper.evStrByMid(mId); + return (long) evIds.split(",").length; + } + + @Override + public List listProcessJsonBeanByMid(Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("mid",mId); + wrapper.orderBy("order_num",Boolean.TRUE); + return this.listAs(wrapper,ProcessJsonBean.class); + } + + @Override + public void saveWithJson(String processJson,Long pId) { + List processJsonBeans = JSONArray.parseArray(processJson, ProcessJsonBean.class); + List entities = ConvertUtils.sourceToTarget(processJsonBeans, IotCarbonProcessStepsEntity.class); + entities.forEach(temp->{ + temp.setId(null); + temp.setMid(pId); + }); + + saveBatch(entities); + } + + @Override + @SuppressWarnings("Duplicates") + public void saveOrUpdateWithJson(String processJson, Long productId) { + // 查询出当前产品现有的工艺步骤信息, 与新数据对比,进行增删改 + List existList = list(QueryWrapper.create().eq(IotCarbonProcessStepsEntity::getMid, productId)); + List processJsonBeans = JSONArray.parseArray(processJson, ProcessJsonBean.class); + List newList = ConvertUtils.sourceToTarget(processJsonBeans, IotCarbonProcessStepsEntity.class); + + Map> existUniqueMap = + existList.stream() + .collect(Collectors.groupingBy(e -> e.getProcessCode() + "_" + e.getProcessName())); + + Map> newUniqueMap = + newList.stream() + .collect(Collectors.groupingBy(e -> e.getProcessCode() + "_" + e.getProcessName())); + + List saveList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + for (IotCarbonProcessStepsEntity newProcess : newList) { + String uniqueKey = newProcess.getProcessCode() + "_" + newProcess.getProcessName(); + if (existUniqueMap.containsKey(uniqueKey)) { + IotCarbonProcessStepsEntity existProcess = existUniqueMap.get(uniqueKey).get(0); + newProcess.setId(existProcess.getId()); + newProcess.setMid(existProcess.getMid()); + updateList.add(newProcess); + } else { + newProcess.setId(null); + newProcess.setMid(productId); + saveList.add(newProcess); + } + } + + Set deleteIds = + existList.stream() + .filter(e -> !newUniqueMap.containsKey(e.getProcessCode() + "_" + e.getProcessName())) + .map(IotCarbonProcessStepsEntity::getId) + .collect(Collectors.toSet()); + + if (CollectionUtils.isNotEmpty(deleteIds)) { + removeByIds(deleteIds); + } + if (CollectionUtils.isNotEmpty(saveList)) { + saveBatch(saveList); + } + if (CollectionUtils.isNotEmpty(updateList)) { + updateBatch(updateList); + } + } + + @Override + public void deleteByMid(Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("mid",mId); + this.remove(wrapper); + } + + @Override + public void saveOrUpdateInfo(List stepsEntityList) { + stepsEntityList.forEach(temp->{ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("mid",temp.getMid()); + wrapper.eq("process_code",temp.getProcessCode()); + wrapper.eq("process_name",temp.getProcessName()); + wrapper.limit(1); + IotCarbonProcessStepsEntity stepsEntity = this.mapper.selectOneByQuery(wrapper); + if(ObjectUtils.isNotEmpty(stepsEntity)){ + temp.setId(stepsEntity.getId()); + this.mapper.update(temp); + }else { + this.mapper.insert(temp); + } + }); + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/controller/IotCarbonProductionSyncController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/controller/IotCarbonProductionSyncController.java new file mode 100644 index 0000000..aba12fe --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/controller/IotCarbonProductionSyncController.java @@ -0,0 +1,97 @@ +package com.thing.carbontrack.sync.controller; + +import com.thing.carbontrack.sync.dto.IotCarbonProductionSyncDTO; +import com.thing.carbontrack.sync.service.IotCarbonProductionSyncService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 生产记录同步记录配置 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@RestController +@RequestMapping("v2/sync/iotcarbonproductionsync") +@Tag(name="AAA生产记录同步记录配置") +@RequiredArgsConstructor +public class IotCarbonProductionSyncController { + + private final IotCarbonProductionSyncService iotCarbonProductionSyncService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonProductionSyncService.getPageData(params, IotCarbonProductionSyncDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonProductionSyncDTO data = iotCarbonProductionSyncService.getByIdAs(id, IotCarbonProductionSyncDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotCarbonProductionSyncDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotCarbonProductionSyncService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotCarbonProductionSyncDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotCarbonProductionSyncService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonProductionSyncService.batchDelete(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotCarbonProductionSyncService.listAs(params, IotCarbonProductionSyncDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "生产记录同步记录配置", list, IotCarbonProductionSyncExcel.class); + *} + */ + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/dto/IotCarbonProductionSyncDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/dto/IotCarbonProductionSyncDTO.java new file mode 100644 index 0000000..ef0cbe2 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/dto/IotCarbonProductionSyncDTO.java @@ -0,0 +1,42 @@ +package com.thing.carbontrack.sync.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 生产记录同步记录配置 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Data +@Schema(description = "生产记录同步记录配置") +public class IotCarbonProductionSyncDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "物料id") + private Long mId; + @Schema(description = "同步类型 1结果数据 2过程数据") + private String type; + @Schema(description = "所属企业/租户code") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/entity/IotCarbonProductionSyncEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/entity/IotCarbonProductionSyncEntity.java new file mode 100644 index 0000000..adda9c5 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/entity/IotCarbonProductionSyncEntity.java @@ -0,0 +1,35 @@ +package com.thing.carbontrack.sync.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 生产记录同步记录配置 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_production_sync") +public class IotCarbonProductionSyncEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 物料id + */ + private Long mId; + /** + * 同步类型 1结果数据 2过程数据 + */ + private String type; + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/mapper/IotCarbonProductionSyncMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/mapper/IotCarbonProductionSyncMapper.java new file mode 100644 index 0000000..38bfdf7 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/mapper/IotCarbonProductionSyncMapper.java @@ -0,0 +1,16 @@ +package com.thing.carbontrack.sync.mapper; + +import com.thing.carbontrack.sync.entity.IotCarbonProductionSyncEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 生产记录同步记录配置 +* +* @author xc +* @since 3.0 2024-05-14 +*/ +@Mapper +public interface IotCarbonProductionSyncMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/IotCarbonProductionSyncService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/IotCarbonProductionSyncService.java new file mode 100644 index 0000000..0e99963 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/IotCarbonProductionSyncService.java @@ -0,0 +1,14 @@ +package com.thing.carbontrack.sync.service; + +import com.thing.carbontrack.sync.entity.IotCarbonProductionSyncEntity; +import com.thing.common.orm.service.IBaseService; + +/** + * 生产记录同步记录配置 + * + * @author xc + * @since 3.0 2024-05-14 + */ +public interface IotCarbonProductionSyncService extends IBaseService { + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/impl/IotCarbonProductionSyncServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/impl/IotCarbonProductionSyncServiceImpl.java new file mode 100644 index 0000000..abd707a --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/sync/service/impl/IotCarbonProductionSyncServiceImpl.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.sync.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.sync.entity.IotCarbonProductionSyncEntity; +import com.thing.carbontrack.sync.mapper.IotCarbonProductionSyncMapper; +import com.thing.carbontrack.sync.service.IotCarbonProductionSyncService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 生产记录同步记录配置 + * + * @author xc + * @since 3.0 2024-05-14 + */ +@Service +public class IotCarbonProductionSyncServiceImpl extends BaseServiceImpl implements IotCarbonProductionSyncService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/CarbonTrackReportGenerateTask.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/CarbonTrackReportGenerateTask.java new file mode 100644 index 0000000..e0e279e --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/CarbonTrackReportGenerateTask.java @@ -0,0 +1,134 @@ +package com.thing.carbontrack.task; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportConfigDTO; +import com.thing.carbontrack.report.dto.IotCarbonProductionReportDTO; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportConfigEntity; +import com.thing.carbontrack.report.entity.IotCarbonProductionReportEntity; +import com.thing.carbontrack.report.service.IotCarbonProductionReportConfigService; +import com.thing.carbontrack.report.service.IotCarbonProductionReportService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.quartz.timetask.task.ITask; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Component; + +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import static com.thing.common.core.utils.DateTimeUtils.DATE_PATTERN; + +/** + * @author siyang + * @date 2024/6/13 16:13 + * @description 碳足迹报表生成任务 + */ +@Slf4j +@RequiredArgsConstructor +@SuppressWarnings("Duplicates") +@Component("CarbonTrackReportGenerateTask") +public class CarbonTrackReportGenerateTask implements ITask { + + private final IotCarbonProductionReportService reportService; + private final IotCarbonProductionReportConfigService reportConfigService; + + @Override + public void run(String params) { + List configEntities = + reportConfigService.list( + QueryWrapper.create() + .isNotNull(IotCarbonProductionReportConfigEntity::getBoundaryStart) + .isNotNull(IotCarbonProductionReportConfigEntity::getBoundaryEnd)); + if (CollectionUtils.isEmpty(configEntities)) { + return; + } + + // 查询出这些配置对应的报告,并按配置id分组 + Map> reportGroup = + getReportsByConfigs(configEntities); + + List configs = + ConvertUtils.sourceToTarget( + configEntities, IotCarbonProductionReportConfigDTO.class); + + List saveList = new ArrayList<>(); + List updateList = new ArrayList<>(); + configs.forEach( + config -> { + // 生成新的报告 + IotCarbonProductionReportDTO dto = reportService.generateReport(config); + IotCarbonProductionReportEntity entity = + ConvertUtils.sourceToTarget(dto, IotCarbonProductionReportEntity.class); + entity.setConfigId(config.getId()) + .setTenantCode(config.getTenantCode()) + .setDeptId(config.getTenantCode()) + .setCompanyId(config.getTenantCode()); + + // 从以往报告中找出需要更新的一条,若存在则更新,否则新增 + List existReports = + reportGroup.get(config.getId()); + IotCarbonProductionReportEntity toUpdateReport = + Objects.isNull(existReports) + ? null + : existReports.stream() + .filter(e -> needUpdate(config, e)) + .findFirst() + .orElse(null); + + if (Objects.nonNull(toUpdateReport)) { + toUpdateReport + .setTotalCarbon( + entity.getTotalCarbon().setScale(4, RoundingMode.HALF_UP)) + .setStagePercentJson(entity.getStagePercentJson()); + updateList.add(toUpdateReport); + } else { + String reportCode = + reportService.generateReportCode(config.getId(), null, null); + entity.setReportCode(reportCode); + saveList.add(entity); + } + }); + if (CollectionUtils.isNotEmpty(saveList)) { + reportService.saveBatch(saveList); + } + if (CollectionUtils.isNotEmpty(updateList)) { + reportService.updateBatch(updateList); + } + } + + private Map> getReportsByConfigs( + List configs) { + List configIds = + configs.stream().map(IotCarbonProductionReportConfigEntity::getId).toList(); + List reportEntities = + reportService.list( + QueryWrapper.create() + .in(IotCarbonProductionReportEntity::getConfigId, configIds)); + return reportEntities.stream() + .collect(Collectors.groupingBy(IotCarbonProductionReportEntity::getConfigId)); + } + + private Boolean needUpdate( + IotCarbonProductionReportConfigDTO config, IotCarbonProductionReportEntity report) { + Pair boundaryTimePair = reportService.getBoundary(config); + boolean sameType = Objects.equals(report.getBoundaryType(), config.getBoundaryType()); + boolean sameStart = + Objects.equals( + DateTimeUtils.timestamp2Str( + report.getBoundaryStart().getTime(), DATE_PATTERN), + DateTimeUtils.timestamp2Str(boundaryTimePair.getLeft(), DATE_PATTERN)); + boolean sameEnd = + Objects.equals( + DateTimeUtils.timestamp2Str( + report.getBoundaryEnd().getTime(), DATE_PATTERN), + DateTimeUtils.timestamp2Str(boundaryTimePair.getRight(), DATE_PATTERN)); + return sameType && sameStart && sameEnd; + } +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/DataSharingService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/DataSharingService.java new file mode 100644 index 0000000..4410402 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/DataSharingService.java @@ -0,0 +1,106 @@ +package com.thing.carbontrack.task; + + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.production.service.IotCarbonProductionVarietyService; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.pub.dto.*; +import com.thing.carbontrack.pub.service.ProductionModelService; +import com.thing.carbontrack.pub.service.ProductionResultService; +import com.thing.carbontrack.share.entity.IotCarbonShareDetailsEntity; +import com.thing.carbontrack.share.entity.IotCarbonShareEntity; +import com.thing.carbontrack.share.service.IotCarbonShareDetailsService; +import com.thing.carbontrack.share.service.IotCarbonShareService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class DataSharingService { + + private final IotCarbonShareService carbonShareService; + + private final IotCarbonShareDetailsService carbonShareDetailsService; + + private final IotCarbonProductionResultService carbonProductionResultService; + + private final ProductionResultService productionResultService; + + private final IotCarbonProductionVarietyService carbonProductionVarietyService; + + private final IotCarbonBomService carbonBomService; + + private final IotCarbonProcessStepsService carbonProcessStepsService; + + private final ProductionModelService productionModelService; + + private final IotCarbonProductionRecordService productionRecordService; + + + public void dataSharing(){ + //所有企业令牌配置信息 + List shareEntities = carbonShareService.list(); + shareEntities.forEach(temp->{ + //所有需要上传的物的共享配置信息 + List shareDetailsEntities = carbonShareDetailsService.listByMap(Map.of("tenant_code",temp.getTenantCode())); + shareDetailsEntities.forEach(info->{ + List resultDTOS = getListResultDtoByMIdAndStatus(info.getMId(),"1",info.getType()); + if(ObjectUtil.isNotEmpty(resultDTOS)){ + try { + //向国网侧同步上传数据 + productionResultService.uploadProductionResult(resultDTOS, temp.getUrl(), temp.getToken()); + //产品模型重新上传 + //获取产品基本信息 + ProductJsonBean productJsonBean = carbonProductionVarietyService.getByIdAs(info.getMId(),ProductJsonBean.class); + //产品bom 基本信息 + List materialJsonBeanList = carbonBomService.queryMaterialJsonBeanListByMid(info.getMId()); + //产品工艺工序信息 + List processJsonBeanList = carbonProcessStepsService.listProcessJsonBeanByMid(info.getMId()); + ProductionModelUploadDTO modelUploadDTO = ProductionModelUploadDTO.init(productJsonBean,materialJsonBeanList,processJsonBeanList); + productionModelService.uploadProductionModel(modelUploadDTO, temp.getUrl(), temp.getToken()); + updateResultStatus(resultDTOS,"2"); + } catch (Exception e) { + updateResultStatus(resultDTOS,"3"); + } + } + }); + }); + } + + private void updateResultStatus(List resultDTOS,String status) { + List resultEntities = new ArrayList<>(); + resultDTOS.forEach(resultSyncDTO->{ + IotCarbonProductionResultEntity entity = new IotCarbonProductionResultEntity(); + entity.setId(resultSyncDTO.getOriId()); + entity.setSync_status(status); + resultEntities.add(entity); + }); + carbonProductionResultService.updateBatch(resultEntities); + } + + + private List getListResultDtoByMIdAndStatus(Long mId,String status,String type){ + List resultSyncDTOS = new ArrayList<>(); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId); + wrapper.eq("sync_status",status); + List results = carbonProductionResultService.list(wrapper); + results.forEach(temp->{ + temp.setResultType(type); + //根据产品id+工单编码,获取该工单是实采数据,还是填报数据 + String prType = productionRecordService.getPrTypeByMidAndPrCode(temp.getM_id(),temp.getPrCode()); + resultSyncDTOS.add(ProductionResultSyncDTO.init(temp,prType )); + }); + return resultSyncDTOS; + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/IndirectEnergyTask.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/IndirectEnergyTask.java new file mode 100644 index 0000000..d9e36d0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/IndirectEnergyTask.java @@ -0,0 +1,180 @@ +package com.thing.carbontrack.task; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.Indirect.entity.IotCarbonIndirectEnergyEntity; +import com.thing.carbontrack.Indirect.service.IotCarbonIndirectEnergyService; +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +/** + * 间接用能配置 + */ +@RestController +@RequestMapping("v2/test2") +@RequiredArgsConstructor +public class IndirectEnergyTask { + + private final TsKvService tskvService; + + private final IotCarbonIndirectEnergyService iotCarbonIndirectEnergyService; + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonRatioService carbonRatioService; + + private final IotCarbonProductionProcessVarietyService processVarietyService; + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + + + + @GetMapping("test") + public void test(){ + //获取所有未计算得 间接用能记录 + List indirectEntitys = iotCarbonIndirectEnergyService.getListByStatus("1"); + + indirectEntitys.forEach(temp->{ + //当前 公摊对应得工单记录列表 由产品id+工单编码+报工组id查询而来 + List recordEntities = iotCarbonProductionRecordService.getListByParam(temp.getM_id(),temp.getPrCode(),temp.getGroupId()); + //判断数据是设备接入,还是能源数据录入 + try { + if(temp.getPrType().equals("1")){ + recordEntities.forEach(recordEntity->{ + lotCalculation(temp,recordEntity); + }); + }else { + recordEntities.forEach(recordEntity->{ + timePeriodCalculation(temp,recordEntity); + }); + } + temp.setPrStatus(2L); + } catch (Exception e) { + temp.setPrStatus(3L); + } + }); + iotCarbonIndirectEnergyService.updateBatch(indirectEntitys); + } + + + /** + * 批次计算 + * @param temp + * @param recordEntity + */ + private void lotCalculation(IotCarbonIndirectEnergyEntity temp, IotCarbonProductionRecordEntity recordEntity) { + BigDecimal ratio = this.getRatioByEvId(temp.getEvId()); + //批次计算,先获取设备总能耗 + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(temp,recordEntity); + //获取对应能源品种得编码 + CarbonEnergyVarietyEntity energy = carbonEnergyVarietyService.getById(temp.getEvId()); + String attrCode = energy.getCode()+"hh"; + //查询tskv + TsKvDTO tsKvDTO = tskvService.findTsKvAggByCodeAndAttr(temp.getThingCode(),attrCode,recordEntity.getStartTime().getTime(),recordEntity.getEndTime().getTime(), AggType.SUM); + BigDecimal use = new BigDecimal(tsKvDTO.getVal()==null?"0":tsKvDTO.getVal()); + BigDecimal newRatio = temp.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(newRatio).divide(new BigDecimal(temp.getStepsNum()), RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + this.saveOrUpdate(varietyEntity); + } + + + + + + /** + * 时段计算 + * @param temp + * @param recordEntity + */ + private void timePeriodCalculation(IotCarbonIndirectEnergyEntity temp, IotCarbonProductionRecordEntity recordEntity) { + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(temp,recordEntity); + //折标媒系数 + BigDecimal ratio = this.getRatioByEvId(temp.getEvId()); + //计算总能耗 == 先计算 pNum 除以 finalNum,并设置保留小数位数为 4 + getTimePeriodResult(temp, ratio,varietyEntity,recordEntity); + //判断当前entity 是否存在,存在则更新,不存在则新增 拿prId 去结果中查询 + this.saveOrUpdate(varietyEntity); + + } + + private BigDecimal getRatioByEvId(Long evId){ + return carbonRatioService.queryOnByEvIdAndType(evId,"1").getRatio(); + } + + /** + * 保存,还是修改数据 + * @param varietyEntity + */ + private void saveOrUpdate(IotCarbonProductionProcessVarietyEntity varietyEntity){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("pr_id",varietyEntity.getPrId()); + wrapper.eq("ps_id",varietyEntity.getPsId()); + wrapper.eq("m_id",varietyEntity.getMId()); + wrapper.eq("ev_id",varietyEntity.getEvId()); + IotCarbonProductionProcessVarietyEntity entity = processVarietyService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + varietyEntity.setId(entity.getId()); + processVarietyService.updateById(varietyEntity); + }else { + processVarietyService.save(varietyEntity); + } + } + + /** + * 区间数据 最终碳排计算 + * @param entity + * @param ratio + * @return + */ + @NotNull + private static void getTimePeriodResult(IotCarbonIndirectEnergyEntity entity, BigDecimal ratio,IotCarbonProductionProcessVarietyEntity varietyEntity,IotCarbonProductionRecordEntity recordEntity) { + BigDecimal pNumBigDecimal = new BigDecimal(recordEntity.getPNum()); + BigDecimal finalNumBigDecimal = new BigDecimal(recordEntity.getFinalNum()); + BigDecimal oneRatio = finalNumBigDecimal.divide(pNumBigDecimal, 4, RoundingMode.HALF_UP); + //计算总能耗 == 先计算 能耗分摊比率 + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal use = entity.getDosage().multiply(newRatio).setScale(4, RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(oneRatio).divide(new BigDecimal(entity.getStepsNum()), RoundingMode.HALF_UP).setScale(4, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + } + + /** + * 初始化计算结果对象 + * @return + */ + private IotCarbonProductionProcessVarietyEntity initVarietyEntity(IotCarbonIndirectEnergyEntity temp,IotCarbonProductionRecordEntity entity ){ + IotCarbonProductionProcessVarietyEntity varietyEntity = new IotCarbonProductionProcessVarietyEntity(); + varietyEntity.setPsId(entity.getPsId()); + varietyEntity.setPrId(temp.getId()); + varietyEntity.setPrCode(entity.getPrCode()); + varietyEntity.setEvId(temp.getEvId()); + varietyEntity.setFinal_num(entity.getFinalNum()); + varietyEntity.setMId(temp.getM_id()); + varietyEntity.setType("3"); + return varietyEntity; + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessStatusTask.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessStatusTask.java new file mode 100644 index 0000000..2986c71 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessStatusTask.java @@ -0,0 +1,40 @@ +package com.thing.carbontrack.task; + + +import com.thing.carbontrack.productionRecord.dto.RecordReq; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.steps.service.IotCarbonProcessStepsService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Objects; + + +@RestController +@RequestMapping("v2/test3") +@RequiredArgsConstructor +public class ProductionProcessStatusTask { + + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonProcessStepsService iotCarbonProcessStepsService; + + @GetMapping("test") + public void test(){ + //查询已计算状态工单,工序数量,工序数量与当前bom数量一致,则翻转工单状态为 全部计算完成 + List recordReqList = iotCarbonProductionRecordService.getRecordReqList(); + recordReqList.forEach(temp->{ + Long num = iotCarbonProcessStepsService.evNumByMid(temp.getMId()); + if(Objects.equals(num, temp.getNum())){ + //已全量计算 翻转记录表中工单状态 + iotCarbonProductionRecordService.updateByMidAndPrCode(temp.getMId() ,temp.getPrCode()); + } + }); + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessTask.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessTask.java new file mode 100644 index 0000000..af02261 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionProcessTask.java @@ -0,0 +1,159 @@ +package com.thing.carbontrack.task; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbontrack.processVariety.entity.IotCarbonProductionProcessVarietyEntity; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.entity.IotCarbonProductionRecordEntity; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.jetbrains.annotations.NotNull; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + + +@RestController +@RequestMapping("v2/test") +@RequiredArgsConstructor +public class ProductionProcessTask { + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final TsKvService tskvService; + + private final IotCarbonRatioService carbonRatioService; + + private final IotCarbonProductionProcessVarietyService processVarietyService; + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + + + @GetMapping("test") + public void test(){ + //获取所有未计算得 报工记录 + List productionRecordList = iotCarbonProductionRecordService.getListByStatus("1"); + productionRecordList.forEach(temp->{ + try { + if(temp.getPrType().equals("1")){ + lotCalculation(temp); + }else { + timePeriodCalculation(temp); + } + temp.setPrStatus(2L); + } catch (Exception e) { + temp.setPrStatus(3L); + } + }); + iotCarbonProductionRecordService.updateBatch(productionRecordList); + } + + /** + * 时段计算 + * @param entity + */ + private void timePeriodCalculation(IotCarbonProductionRecordEntity entity){ + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(entity); + //折标媒系数 + BigDecimal ratio = this.getRatioByEvId(entity.getEvId()); + //计算总能耗 == 先计算 pNum 除以 finalNum,并设置保留小数位数为 4 + getTimePeriodResult(entity, ratio,varietyEntity); + //判断当前entity 是否存在,存在则更新,不存在则新增 拿prId 去结果中查询 + this.saveOrUpdate(varietyEntity); + } + + /** + * 批次计算 + * @param entity + */ + private void lotCalculation(IotCarbonProductionRecordEntity entity){ + BigDecimal ratio = this.getRatioByEvId(entity.getEvId()); + //批次计算,先获取设备总能耗 + IotCarbonProductionProcessVarietyEntity varietyEntity = initVarietyEntity(entity); + //获取对应能源品种得编码 + CarbonEnergyVarietyEntity energy = carbonEnergyVarietyService.getById(entity.getEvId()); + String attrCode = energy.getCode()+"hh"; + //查询tskv + TsKvDTO tsKvDTO = tskvService.findTsKvAggByCodeAndAttr(entity.getThingCode(),attrCode,entity.getStartTime().getTime(),entity.getEndTime().getTime(), AggType.SUM); + BigDecimal use = new BigDecimal(tsKvDTO.getVal()==null?"0":tsKvDTO.getVal()); + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal finalUse=use.multiply(newRatio).setScale(4, RoundingMode.HALF_UP); + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setCarbon(carbon); + varietyEntity.setUsage(finalUse); + this.saveOrUpdate(varietyEntity); + } + + private BigDecimal getRatioByEvId(Long evId){ + return carbonRatioService.queryOnByEvIdAndType(evId,"1").getRatio(); + } + + /** + * 保存,还是修改数据 + * @param varietyEntity + */ + private void saveOrUpdate(IotCarbonProductionProcessVarietyEntity varietyEntity){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("pr_id",varietyEntity.getPrId()); + IotCarbonProductionProcessVarietyEntity entity = processVarietyService.getOne(wrapper); + if(ObjectUtils.isNotEmpty(entity)){ + varietyEntity.setId(entity.getId()); + processVarietyService.updateById(varietyEntity); + }else { + processVarietyService.save(varietyEntity); + } + } + + /** + * 区间数据 最终碳排计算 + * @param entity + * @param ratio + * @return + */ + @NotNull + private static void getTimePeriodResult(IotCarbonProductionRecordEntity entity, BigDecimal ratio,IotCarbonProductionProcessVarietyEntity varietyEntity) { + BigDecimal pNumBigDecimal = new BigDecimal(entity.getPNum()); + BigDecimal finalNumBigDecimal = new BigDecimal(entity.getFinalNum()); + BigDecimal oneRatio = finalNumBigDecimal.divide(pNumBigDecimal, 4, RoundingMode.HALF_UP); + + //计算总能耗 == 先计算 能耗分摊比率 + BigDecimal newRatio = entity.getRatio().divide(new BigDecimal("100"), RoundingMode.HALF_UP); + BigDecimal use = entity.getDosage().multiply(newRatio).setScale(4, RoundingMode.HALF_UP); + + BigDecimal finalUse=use.multiply(oneRatio).setScale(4, RoundingMode.HALF_UP); + + BigDecimal carbon = finalUse.multiply(ratio).setScale(4, RoundingMode.HALF_UP); + varietyEntity.setUsage(finalUse); + varietyEntity.setCarbon(carbon); + } + + /** + * 初始化计算结果对象 + * @param entity + * @return + */ + private IotCarbonProductionProcessVarietyEntity initVarietyEntity(IotCarbonProductionRecordEntity entity ){ + IotCarbonProductionProcessVarietyEntity varietyEntity = new IotCarbonProductionProcessVarietyEntity(); + varietyEntity.setPsId(entity.getPsId()); + varietyEntity.setPrId(entity.getId()); + varietyEntity.setPrCode(entity.getPrCode()); + varietyEntity.setEvId(entity.getEvId()); + varietyEntity.setFinal_num(entity.getFinalNum()); + varietyEntity.setMId(entity.getM_id()); + varietyEntity.setType("2"); + return varietyEntity; + } + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionResultTask.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionResultTask.java new file mode 100644 index 0000000..57171cb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/task/ProductionResultTask.java @@ -0,0 +1,333 @@ +package com.thing.carbontrack.task; + + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.bom.dto.BomCarbonReq; +import com.thing.carbontrack.bom.service.IotCarbonBomService; +import com.thing.carbontrack.event.standardcal.ProductionResultEvent; +import com.thing.carbontrack.processVariety.service.IotCarbonProductionProcessVarietyService; +import com.thing.carbontrack.productionRecord.dto.ProductionPage; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.productionResult.dto.*; +import com.thing.carbontrack.productionResult.entity.IotCarbonProductionResultEntity; +import com.thing.carbontrack.productionResult.service.IotCarbonProductionResultService; +import com.thing.carbontrack.ratio.dto.CarBonRatioResult; +import com.thing.carbontrack.ratio.service.IotCarbonRatioService; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.context.event.EventListener; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 生产记录用能核算结果 + */ +@RestController +@RequestMapping("v2/test4") +@RequiredArgsConstructor +public class ProductionResultTask { + + + private final IotCarbonProductionResultService carbonProductionResultService; + + private final IotCarbonProductionRecordService iotCarbonProductionRecordService; + + private final IotCarbonBomService carbonBomService; + + private final IotCarbonProductionProcessVarietyService carbonProductionProcessVarietyService; + + private final IotCarbonUseConfigService carbonUseConfigService; + + private final IotCarbonRatioService carbonRatioService; + + private final String APPTYPE = "CK"; + + private final String CHANGK = "CK"; + + private final String BASE = "BASE"; + + @GetMapping("test") + public void start(){ + //获取所有生产报工已经计算完毕的报工记录 + List reqs =iotCarbonProductionRecordService.getRecordReqListByStatus(); + reqs.forEach(this::calculate); + + } + + /** + * 计算产品批次碳足迹 + * @param production + */ + @Transactional + public synchronized void calculate(ProductionPage production){ + try { + //获取填充 生产数量 + + //bom 原料计算 + calculateBom(production); + //产品生产计算 + calculateProduce(production,"2"); + //产品公摊计算 + calculateProduce(production,"3"); + + if(production.getBoundary().equals("2")){ + //产品运输计算 + calculateTransport(production); + //产品使用计算 + calculateUse(production); + //产品废弃处理计算 + calculateDispose(production); + } + //翻转为5 汇总成功 + iotCarbonProductionRecordService.updatePrStatus(5L,production.getMId(),production.getPrCode()); + } catch (Exception e) { + //翻转为6 汇总异常 + iotCarbonProductionRecordService.updatePrStatus(6L,production.getMId(),production.getPrCode()); + } + + + } + + + /** + * 废弃处理,碳排计算 + * @param production + */ + private void calculateDispose(ProductionPage production) { + List disposeDetails = carbonUseConfigService.getDisposeDetailListByMIdAndPrCode(production.getMId(),null); + + disposeDetails.forEach(temp->{ + CarBonRatioResult result = carbonRatioService.getRatioByEvIdAndTime(temp.getEvId(),production.getEndTime()); + //先计算废弃物能源 + temp.setEvName(result.getName()); + Long salesNum = production.getFinalNum(); + temp.setSalesNum(salesNum); + //废弃能耗单耗 + BigDecimal evUsageAvg = temp.getEvUsage(); + //用量单耗 + BigDecimal evUsage = temp.getEvUsage().multiply(new BigDecimal(salesNum)); + //碳排总量 + BigDecimal evUsageCarbon = evUsage.multiply(result.getRatio()).setScale(4, RoundingMode.UP); + //用能总量 + BigDecimal evUsageCarbonAvg = evUsageAvg.multiply(result.getRatio()).setScale(4,RoundingMode.UP); + temp.setEvUsageAvg(evUsageAvg); + temp.setEvUsage(evUsage); + temp.setEvUsageCarbon(evUsageCarbon); + temp.setEvUsageCarbonAvg(evUsageCarbonAvg); + //初始化结果对象 + production.setEvId(temp.getEvId()); + production.setEvName(temp.getEvName()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"6", + evUsageCarbon,evUsage,evUsageAvg,evUsageCarbonAvg); + //计算废弃运输能源消耗情况 + //单件运输碳排 + BigDecimal transportCarBonAvg = temp.getTransportDistance().multiply(temp.getTransportRatio()).multiply(temp.getProductWight()).setScale(4,RoundingMode.UP); + //总运输碳排 + BigDecimal transportCarBon = transportCarBonAvg.multiply(new BigDecimal(salesNum)).setScale(4,RoundingMode.UP); + temp.setTransportCarBon(transportCarBon); + temp.setTransportCarBonAvg(transportCarBonAvg); + entity.setF_usage(temp.getTransportDistance()); + entity.setF_usage_avg(temp.getTransportDistance()); + entity.setF_carbon(transportCarBon); + entity.setF_carbon_avg(transportCarBonAvg); + entity.setF_ev_id(temp.getTransportId()); + entity.setF_ev_name(temp.getTransportType()); + entity.setJson(JSONObject.toJSONString(List.of(temp))); + //do update 还是 insert 废弃处理来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"6", temp.getEvId(), entity.getGroup_id(),entity.getF_ev_id()); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + + }); + + + + + } + + + /** + * 产品使用,碳排计算 + * @param production + */ + private void calculateUse(ProductionPage production) { + List puDetails = carbonUseConfigService.getPuDetailListByMIdAndPrCode(production.getMId(),null); + + puDetails.forEach(temp->{ + //evId获取 能源碳排因子值 + CarBonRatioResult result = carbonRatioService.getRatioByEvIdAndTime(temp.getEvId(),production.getEndTime()); + temp.setEvName(result.getName()); + temp.setFinishTime(production.getEndTime()); + temp.setSalesNum(production.getFinalNum()); + //碳排单耗 + BigDecimal carbonAvg = result.getRatio().multiply(new BigDecimal(temp.getServiceLife())).multiply(temp.getConsumption()).setScale(4,RoundingMode.UP); + //用量单耗 + BigDecimal consumptionAvg = temp.getConsumption().multiply(new BigDecimal(temp.getServiceLife())).setScale(4,RoundingMode.UP); + //碳排总量 + BigDecimal usageCarbon = carbonAvg.multiply(new BigDecimal(temp.getSalesNum())).setScale(4,RoundingMode.UP); + //用能总量 + BigDecimal consumption = consumptionAvg.multiply(new BigDecimal(temp.getSalesNum())).setScale(4,RoundingMode.UP); + temp.setCarbonAvg(carbonAvg); + temp.setConsumptionAvg(consumptionAvg); + temp.setUsageCarbon(usageCarbon); + temp.setConsumption(consumption); + production.setEvId(temp.getEvId()); + production.setEvName(temp.getEvName()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"5", + usageCarbon,consumption,consumptionAvg,carbonAvg); + entity.setJson(JSONObject.toJSONString(List.of(temp))); + entity.setLife(temp.getServiceLife()); + //do update 还是 insert 产品使用来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"5", temp.getEvId(), null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + }); + } + + /** + * 产品运输碳排计算 + * @param production + */ + private void calculateTransport(ProductionPage production) { + List ptDetails = carbonUseConfigService.getListByMIdAndPrCode(production.getMId(),production.getPrCode()); + ptDetails.forEach(temp->{ + production.setEvId(temp.getTransportId()); + production.setEvName(temp.getTransportType()); + production.setGroupId(temp.getGroupId()); + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,"4", + temp.getTransportCarBon(),temp.getTransportDistance(),temp.getTransportDistance(),temp.getTransportCarBonAvg()); + + entity.setJson(JSONObject.toJSONString(List.of(temp))); + + //do update 还是 insert 产品运输来说,根据产品id,工单编码,类型,能源品种id,groupId 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode() + ,"4", temp.getTransportId(), temp.getGroupId(),null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + }); + } + + + /** + * 生产/碳排计算 + * @param production + */ + private void calculateProduce(ProductionPage production,String type) { + List pmProcessDetails = carbonProductionProcessVarietyService.getPIndirectDetailListByMidAndPrCodes(production.getMId(),List.of(production.getPrCode()),type); + //按能源品种进行分组 + Map> mapPm = pmProcessDetails.stream().collect(Collectors.groupingBy(PIndirectDetail::getEvId)); + for (Long evId : mapPm.keySet()) { + production.setEvId(evId); + List group = mapPm.get(evId); + production.setEvName(group.stream().findFirst().orElse(new PIndirectDetail()).getEvName()); + //分别计算每个能源品种的 总能耗,平均值 + BigDecimal totalEvUsage = group.stream() + .map(PIndirectDetail::getEvUsage) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + BigDecimal totalCarbon = group.stream() + .map(PIndirectDetail::getCarbon) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal avgEvUsage= totalEvUsage.divide(new BigDecimal(production.getFinalNum()),RoundingMode.UP).setScale(4,RoundingMode.UP); + BigDecimal avgCarbon= totalCarbon.divide(new BigDecimal(production.getFinalNum()),RoundingMode.UP).setScale(4,RoundingMode.UP); + //初始化当前能源品种的碳排结果 + IotCarbonProductionResultEntity entity = IotCarbonProductionResultEntity.initDto(production,type, + totalCarbon,totalEvUsage,avgEvUsage,avgCarbon); + entity.setJson(JSONObject.toJSONString(group)); + //do update 还是 insert 生产间接来说,根据产品id,工单编码,类型,能源品种id 来判断 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode(),type,evId,null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + } + } + + + /** + * bom 碳排计算 + * @param production + */ + private void calculateBom(ProductionPage production) { + //单件产品 运输,物料,碳排放值 + BomCarbonReq carbonReq = carbonBomService.queryBomCarbonByMid(production.getMId()); + IotCarbonProductionResultEntity entity = new IotCarbonProductionResultEntity(); + //获取 BOM详情json + List entityList = carbonBomService.listByProductId(production.getMId()); + if(APPTYPE.equals(CHANGK)){ + //常开来说,除以 数量,等于平均单件用量 + BigDecimal uintCarbon = carbonReq.getMuFinalUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP); + BigDecimal uintTransportTotalCarbon = carbonReq.getTransportUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP); + //初始化运输结果 + entity = IotCarbonProductionResultEntity.initDto(production,"1", + carbonReq.getMuFinalUse(),carbonReq.getTransportUse(),uintTransportTotalCarbon,uintCarbon); + entityList.forEach(temp->{ + temp.setAcquireCarbon(temp.getAcquireCarbon().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + temp.setDosage(temp.getDosage().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + temp.setTransportUse(temp.getTransportUse().divide(new BigDecimal(production.getFinalNum()),4,RoundingMode.UP)); + }); + }else if (APPTYPE.equals(BASE)){ + BigDecimal totalCarbon = carbonReq.getMuFinalUse().multiply(new BigDecimal(production.getFinalNum())).setScale(4, RoundingMode.UP); + BigDecimal transportTotalCarbon = carbonReq.getTransportUse().multiply(new BigDecimal(production.getFinalNum())).setScale(4, RoundingMode.UP); + //初始化运输结果 + entity = IotCarbonProductionResultEntity.initDto(production,"1", + totalCarbon,transportTotalCarbon,carbonReq.getTransportUse(),carbonReq.getMuFinalUse()); + } + entity.setJson(JSONObject.toJSONString(entityList)); + //do update 还是 insert 产品bom来说,根据 产品id,工单编码,类型,来判断是否已存在 + IotCarbonProductionResultEntity entityInfo = this.getResultEntityByParam(production.getMId(),production.getPrCode(),"1",null,null,null); + if(ObjectUtils.isNotEmpty(entityInfo)){ + entity.setId(entityInfo.getId()); + carbonProductionResultService.updateById(entity); + }else { + carbonProductionResultService.save(entity); + } + } + + + /** + * 根据入参,查询汇算结果 + * @param mId + * @param prCode + * @param type + * @param evId + * @return + */ + private IotCarbonProductionResultEntity getResultEntityByParam(Long mId,String prCode,String type,Long evId,Long groupId,Long fevid){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId,ObjectUtils.isNotEmpty(mId)); + wrapper.eq("pr_code",prCode,ObjectUtils.isNotEmpty(prCode)); + wrapper.eq("carbon_type",type,ObjectUtils.isNotEmpty(type)); + wrapper.eq("ev_id",evId,ObjectUtils.isNotEmpty(evId)); + wrapper.eq("ev_id",evId,ObjectUtils.isNotEmpty(evId)); + wrapper.eq("f_ev_id",fevid,ObjectUtils.isNotEmpty(fevid)); + return carbonProductionResultService.getOne(wrapper); + } + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/controller/IotCarbonUseConfigController.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/controller/IotCarbonUseConfigController.java new file mode 100644 index 0000000..8cde919 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/controller/IotCarbonUseConfigController.java @@ -0,0 +1,123 @@ +package com.thing.carbontrack.useConfig.controller; + +import com.thing.carbontrack.event.standardcal.ProductionProcessEvent; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.useConfig.dto.IotCarbonUseConfigDTO; +import com.thing.carbontrack.useConfig.dto.RecalculateByTimeDTO; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 产品运输,使用,废弃处理,配置信息 +* +* @author xc +* @since 3.0 2024-05-28 +*/ +@RestController +@RequestMapping("v2/useConfig/iotcarbonuseconfig") +@Tag(name="产品运输,使用,废弃处理,配置信息") +@RequiredArgsConstructor +public class IotCarbonUseConfigController { + + private final IotCarbonUseConfigService iotCarbonUseConfigService; + + private final IotCarbonProductionRecordService carbonProductionRecordService; + + private final ApplicationEventPublisher applicationEventPublisher; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "mId", description = "产品id"), + @Parameter(name = "type", description = "配置类型1工单运输配置,2.产品使用配置,3废弃配置"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotCarbonUseConfigService.getPageData(params, IotCarbonUseConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotCarbonUseConfigDTO data = iotCarbonUseConfigService.getByIdAs(id, IotCarbonUseConfigDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody List dtos){ + //效验数据 + Long uuid = System.currentTimeMillis(); + dtos.forEach(temp->{ + temp.setGroupId(uuid); + iotCarbonUseConfigService.saveDto(temp); + }); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody List dtos){ + dtos.forEach(iotCarbonUseConfigService::updateDto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotCarbonUseConfigService.batchDelete(ids); + return new Result<>(); + } + + @GetMapping("recalculate") + @Operation(summary="按工单重新计算") + @Parameters({ + @Parameter(name = "mId", description = "产品id"), + @Parameter(name = "prCode", description = "工单编码"), + }) + public Result recalculate(@Parameter(hidden = true) @RequestParam Map params){ + Long mid = MapUtils.getLong(params,"mid"); + String prCode = MapUtils.getString(params,"prCode"); + List prCodes = List.of(prCode.split(",")); + prCodes.forEach(temp->{ + carbonProductionRecordService.updatePrStatus(1L, mid, temp); + }); + applicationEventPublisher.publishEvent(new ProductionProcessEvent(this,"2")); + return new Result().ok("重新计算成功!"); + } + + + + @PostMapping("recalculateByTime") + @Operation(summary="按产品id+时间 重新计算") + public Result recalculateByTime( @RequestBody RecalculateByTimeDTO dto){ + carbonProductionRecordService.updatePrStatusByTimeAndMid(dto); + applicationEventPublisher.publishEvent(new ProductionProcessEvent(this,"2")); + return new Result().ok("重新计算成功!"); + } + +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/IotCarbonUseConfigDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/IotCarbonUseConfigDTO.java new file mode 100644 index 0000000..b513bb0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/IotCarbonUseConfigDTO.java @@ -0,0 +1,64 @@ +package com.thing.carbontrack.useConfig.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 产品运输,使用,废弃处理,配置信息 +* +* @author xc +* @since 3.0 2024-05-28 +*/ +@Data +@Schema(description = "产品运输,使用,废弃处理,配置信息") +public class IotCarbonUseConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "使用年限") + private BigDecimal life; + @Schema(description = "产品id") + private Long m_id; + @Schema(description = "产品使用能源介质id/工单运输碳排因子id 废弃能源介质id") + private Long evId; + @Schema(description = "使用能源用量/工单运输里程/废弃能源用量") + private BigDecimal use; + @Schema(description = "使用运输功能单位") + private String unit; + @Schema(description = "配置类型1运输配置,2.使用配置,3废弃") + private String type; + @Schema(description = "工单运输销售数量 工单运输才有") + private Integer num; + @Schema(description = "客户名称 工单运输才有") + private String customerName; + @Schema(description = "客户地址 工单运输才有") + private String customerAddress; + @Schema(description = "生产记录编码") + private String prCode; + @Schema(description = "废弃运输碳排因子id") + private Long f_ev_id; + @Schema(description = "废弃运输历程") + private BigDecimal f_km; + @Schema(description = "废弃运输功能单位") + private String f_unit; + @Schema(description = "产品编码") + private String m_code; + @Schema(description = "产品名称") + private String m_name; + @Schema(description = "产品功能单位") + private String m_unit; + @Schema(description = "产品重量单位") + private String w_unit; + @Schema(description = "产品重量") + private BigDecimal p_weight; + @Schema(description = "提交数据组id") + private Long groupId; + @Schema(description = "运输方式名称") + private String transportName; +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/RecalculateByTimeDTO.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/RecalculateByTimeDTO.java new file mode 100644 index 0000000..c336905 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/dto/RecalculateByTimeDTO.java @@ -0,0 +1,19 @@ +package com.thing.carbontrack.useConfig.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +@Data +public class RecalculateByTimeDTO { + @Schema(description = "产品id 集合") + private List mId; + @Schema(description = "开始时间") + private Date startTime; + @Schema(description = "结束时间") + private Date endTime; + + +} diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/entity/IotCarbonUseConfigEntity.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/entity/IotCarbonUseConfigEntity.java new file mode 100644 index 0000000..1f357bc --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/entity/IotCarbonUseConfigEntity.java @@ -0,0 +1,73 @@ +package com.thing.carbontrack.useConfig.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.carbontrack.ck.datacleaning.dto.CkOrderDto; +import com.thing.carbontrack.production.entity.IotCarbonProductionVarietyEntity; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 产品运输,使用,废弃处理,配置信息 + * + * @author xc + * @since 3.0 2024-05-28 + */ +@Getter +@Setter +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_carbon_use_config") +public class IotCarbonUseConfigEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @Id + private Long id; + private BigDecimal life; + private Long m_id; + private Long evId; + private BigDecimal use; + private String unit; + private String type; + private Integer num; + private String customerName; + private String customerAddress; + private String prCode; + private Long f_ev_id; + private BigDecimal f_km; + private String f_unit; + private String m_code; + private String m_name; + private String m_unit; + private String w_unit; + private BigDecimal p_weight; + private Long groupId; + + + + public static void init(IotCarbonUseConfigEntity entity, IotCarbonProductionVarietyEntity varietyEntity, CkOrderDto dto){ + entity.setM_id(varietyEntity.getId()); + entity.setM_name(varietyEntity.getName()); + entity.setM_code(varietyEntity.getCode()); + entity.setCustomerName(dto.getCustomerName()); + entity.setCustomerAddress(dto.getCustomerName()); + entity.setM_unit(varietyEntity.getFUnit()); + entity.setW_unit(varietyEntity.getWUnit()); + entity.setP_weight(varietyEntity.getPWeight()); + } + + public static void useInit(IotCarbonUseConfigEntity entity, IotCarbonProductionVarietyEntity varietyEntity){ + entity.setM_id(varietyEntity.getId()); + entity.setM_name(varietyEntity.getName()); + entity.setM_code(varietyEntity.getCode()); + entity.setM_unit(varietyEntity.getFUnit()); + entity.setW_unit(varietyEntity.getWUnit()); + entity.setP_weight(varietyEntity.getPWeight()); + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/mapper/IotCarbonUseConfigMapper.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/mapper/IotCarbonUseConfigMapper.java new file mode 100644 index 0000000..a5acfd0 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/mapper/IotCarbonUseConfigMapper.java @@ -0,0 +1,28 @@ +package com.thing.carbontrack.useConfig.mapper; + +import com.thing.carbontrack.productionResult.dto.DisposeDetail; +import com.thing.carbontrack.productionResult.dto.PtDetail; +import com.thing.carbontrack.productionResult.dto.PuDetail; +import com.thing.carbontrack.useConfig.entity.IotCarbonUseConfigEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; + +/** +* 产品运输,使用,废弃处理,配置信息 +* +* @author xc +* @since 3.0 2024-05-28 +*/ +@Mapper +public interface IotCarbonUseConfigMapper extends PowerBaseMapper { + + List getListByMIdAndPrCodes(@Param("mId") Long mId, @Param("prCodes") Collection prCodes); + + List getPuDetailListByMIdAndPrCodes(@Param("mId") Long mId, @Param("prCodes") Collection prCodes); + + List getDisposeDetailListByMIdAndPrCodes(@Param("mId") Long mId, @Param("prCodes") Collection prCodes); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/IotCarbonUseConfigService.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/IotCarbonUseConfigService.java new file mode 100644 index 0000000..69787fb --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/IotCarbonUseConfigService.java @@ -0,0 +1,36 @@ +package com.thing.carbontrack.useConfig.service; + +import com.thing.carbontrack.productionRecord.dto.ProductionOverviewReq; +import com.thing.carbontrack.productionResult.dto.DisposeDetail; +import com.thing.carbontrack.productionResult.dto.PtDetail; +import com.thing.carbontrack.productionResult.dto.PuDetail; +import com.thing.carbontrack.useConfig.dto.IotCarbonUseConfigDTO; +import com.thing.carbontrack.useConfig.entity.IotCarbonUseConfigEntity; +import com.thing.common.orm.service.IBaseService; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 产品运输,使用,废弃处理,配置信息 + * + * @author xc + * @since 3.0 2024-05-28 + */ +public interface IotCarbonUseConfigService extends IBaseService { + + List getListByMIdAndPrCode(Long mId, String prCode); + + List getPuDetailListByMIdAndPrCode(Long mId, String prCode); + + List getDisposeDetailListByMIdAndPrCode(Long mId, String prCode); + + Set customerNameList(Long mId); + + Set customerAddressList(Map params); + + List customerInfoList(Map params); + + List productionListByMid(Long mId); +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/impl/IotCarbonUseConfigServiceImpl.java b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/impl/IotCarbonUseConfigServiceImpl.java new file mode 100644 index 0000000..fed9ea1 --- /dev/null +++ b/modules/carbon-track/src/main/java/com/thing/carbontrack/useConfig/service/impl/IotCarbonUseConfigServiceImpl.java @@ -0,0 +1,114 @@ +package com.thing.carbontrack.useConfig.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbontrack.productionRecord.dto.ProductionOverviewReq; +import com.thing.carbontrack.productionRecord.service.IotCarbonProductionRecordService; +import com.thing.carbontrack.productionResult.dto.DisposeDetail; +import com.thing.carbontrack.productionResult.dto.PtDetail; +import com.thing.carbontrack.productionResult.dto.PuDetail; +import com.thing.carbontrack.useConfig.dto.IotCarbonUseConfigDTO; +import com.thing.carbontrack.useConfig.entity.IotCarbonUseConfigEntity; +import com.thing.carbontrack.useConfig.mapper.IotCarbonUseConfigMapper; +import com.thing.carbontrack.useConfig.service.IotCarbonUseConfigService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 产品运输,使用,废弃处理,配置信息 + * + * @author xc + * @since 3.0 2024-05-28 + */ +@Service +public class IotCarbonUseConfigServiceImpl extends BaseServiceImpl implements IotCarbonUseConfigService { + + @Autowired + private IotCarbonProductionRecordService iotCarbonProductionRecordService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + Long mId = MapUtils.getLong(params,"m_id"); + String prCode = MapUtils.getString(params,"prCode"); + String type = MapUtils.getString(params,"type"); + wrapper.eq("m_id",mId, ObjectUtils.isNotEmpty(mId)); + wrapper.eq("pr_code",prCode,ObjectUtils.isNotEmpty(prCode)); + wrapper.eq("type",type,ObjectUtils.isNotEmpty(type)); + return wrapper; + } + + @Override + public List getListByMIdAndPrCode(Long mId, String prCode) { + return this.mapper.getListByMIdAndPrCodes(mId,List.of(prCode)); + } + + @Override + public List getPuDetailListByMIdAndPrCode(Long mId, String prCode) { + if(ObjectUtils.isEmpty(prCode)){ + return this.mapper.getPuDetailListByMIdAndPrCodes(mId,List.of()); + }else { + return this.mapper.getPuDetailListByMIdAndPrCodes(mId,List.of(prCode)); + } + } + + @Override + public List getDisposeDetailListByMIdAndPrCode(Long mId, String prCode) { + if(ObjectUtils.isEmpty(prCode)){ + return this.mapper.getDisposeDetailListByMIdAndPrCodes(mId,List.of()); + }else { + return this.mapper.getDisposeDetailListByMIdAndPrCodes(mId,List.of(prCode)); + } + } + + @Override + public Set customerNameList(Long mId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("m_id",mId, ObjectUtils.isNotEmpty(mId)); + wrapper.eq("type","1"); + List entities = this.list(wrapper); + return entities.stream() + .map(IotCarbonUseConfigEntity::getCustomerName) + .collect(Collectors.toSet()); + } + + @Override + public Set customerAddressList(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + Long mId = MapUtils.getLong(params,"m_id"); + String customerName = MapUtils.getString(params,"customerName"); + wrapper.eq("m_id",mId, ObjectUtils.isNotEmpty(mId)); + wrapper.eq("customer_name",customerName,ObjectUtils.isNotEmpty(customerName)); + wrapper.eq("type","1"); + List entities = this.list(wrapper); + return entities.stream() + .map(IotCarbonUseConfigEntity::getCustomerAddress) + .collect(Collectors.toSet()); + } + + @Override + public List customerInfoList(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + Long mId = MapUtils.getLong(params,"m_id"); + String customerName = MapUtils.getString(params,"customerName"); + String customerAddress = MapUtils.getString(params,"customerAddress"); + wrapper.eq("m_id",mId, ObjectUtils.isNotEmpty(mId)); + wrapper.eq("customer_name",customerName,ObjectUtils.isNotEmpty(customerName)); + wrapper.eq("customer_address",customerAddress,ObjectUtils.isNotEmpty(customerAddress)); + wrapper.eq("type","1"); + return this.listAs(wrapper,IotCarbonUseConfigDTO.class); + } + + @Override + public List productionListByMid(Long mId) { + List reqs = iotCarbonProductionRecordService.productionListByMid(mId); + return reqs; + } +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/application-track.yml b/modules/carbon-track/src/main/resources/application-track.yml new file mode 100644 index 0000000..e9473c8 --- /dev/null +++ b/modules/carbon-track/src/main/resources/application-track.yml @@ -0,0 +1,2 @@ +ck: + tenantCode: 457910890981752832 \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/discard.json b/modules/carbon-track/src/main/resources/discard.json new file mode 100644 index 0000000..23bf032 --- /dev/null +++ b/modules/carbon-track/src/main/resources/discard.json @@ -0,0 +1,9 @@ +{ + "evId": "159136210321707008", + "use": 25.0000000000, + "unit": "kWh", + "type": "3", + "f_ev_id": "166755918688608257", + "f_km": 100.0000000000, + "f_unit": "km" +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/CkOrderBomMapper.xml b/modules/carbon-track/src/main/resources/mapper/CkOrderBomMapper.xml new file mode 100644 index 0000000..da49c59 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/CkOrderBomMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/CkOrderMapper.xml b/modules/carbon-track/src/main/resources/mapper/CkOrderMapper.xml new file mode 100644 index 0000000..d6fdd3c --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/CkOrderMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonBomMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonBomMapper.xml new file mode 100644 index 0000000..06989e1 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonBomMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonIndirectEnergyMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonIndirectEnergyMapper.xml new file mode 100644 index 0000000..b364d43 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonIndirectEnergyMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonOutboundConfigMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonOutboundConfigMapper.xml new file mode 100644 index 0000000..c1bec9b --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonOutboundConfigMapper.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProcessStepsMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProcessStepsMapper.xml new file mode 100644 index 0000000..83f8c36 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProcessStepsMapper.xml @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionProcessVarietyMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionProcessVarietyMapper.xml new file mode 100644 index 0000000..918a04f --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionProcessVarietyMapper.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionRecordMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionRecordMapper.xml new file mode 100644 index 0000000..d12c7b3 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionRecordMapper.xml @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + UPDATE iot_carbon_production_record + SET pr_status = '4' + WHERE + m_id = #{mId} + AND pr_code = #{prCode} + + + + UPDATE iot_carbon_production_record + SET pr_status = #{prStatus} + WHERE + m_id = #{mId} + AND pr_code = #{prCode} + + + + + UPDATE iot_carbon_production_record + SET pr_status ='1' + WHERE + m_id in + + #{id} + + AND end_time >= #{startTime}::DATE and end_time <= #{endTime}::DATE + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionResultMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionResultMapper.xml new file mode 100644 index 0000000..85ed96c --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionResultMapper.xml @@ -0,0 +1,140 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionSyncMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionSyncMapper.xml new file mode 100644 index 0000000..5043114 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionSyncMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionVarietyMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionVarietyMapper.xml new file mode 100644 index 0000000..6436839 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonProductionVarietyMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonRatioMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonRatioMapper.xml new file mode 100644 index 0000000..e4a3a7a --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonRatioMapper.xml @@ -0,0 +1,45 @@ + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/mapper/IotCarbonUseConfigMapper.xml b/modules/carbon-track/src/main/resources/mapper/IotCarbonUseConfigMapper.xml new file mode 100644 index 0000000..6d6acc4 --- /dev/null +++ b/modules/carbon-track/src/main/resources/mapper/IotCarbonUseConfigMapper.xml @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/production.json b/modules/carbon-track/src/main/resources/production.json new file mode 100644 index 0000000..2568ec7 --- /dev/null +++ b/modules/carbon-track/src/main/resources/production.json @@ -0,0 +1,17 @@ +{ + "code": "KGG", + "name": "开关柜", + "model": "KGG", + "url": "http://minio.lrdiot.com/thingmanager/20240705/81129561fe71.png", + "type": "1", + "industry": "专用设备制造业", + "industrySub": "开关柜", + "fsu": "1", + "fUnit": "套", + "pSize": "80*60*200", + "pWeight": 60.000000, + "wUnit": "kg", + "boundary": "2", + "pSizeUnit": "cm", + "stepsJson": "{\"cells\":[{\"position\":{\"x\":100,\"y\":160},\"size\":{\"width\":100,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"rgba(60,58,58,0.8)\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"冲孔\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"rgba(60,58,58,0.8)\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"b4c55893df6b4350b2a1c31454284117\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"data\":{\"a\":1},\"zIndex\":1},{\"position\":{\"x\":300,\"y\":160},\"size\":{\"width\":100,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"rgba(60,58,58,0.8)\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"折弯\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"rgba(60,58,58,0.8)\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"fcee9a7f97d6498e8734954be9ae607a\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"data\":{\"a\":1},\"zIndex\":2},{\"shape\":\"edge\",\"attrs\":{\"line\":{\"stroke\":\"#747474\"}},\"id\":\"91d7316a-ecbf-4a07-a628-b43a9bee82a8\",\"router\":{\"name\":\"manhattan\"},\"zIndex\":3,\"source\":{\"cell\":\"b4c55893df6b4350b2a1c31454284117\"},\"target\":{\"cell\":\"fcee9a7f97d6498e8734954be9ae607a\"}},{\"position\":{\"x\":500,\"y\":160},\"size\":{\"width\":100,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"rgba(60,58,58,0.8)\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"烘干\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"rgba(60,58,58,0.8)\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"1889019b8df04ec8ad8a6172b50ba2f6\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"data\":{\"a\":1},\"zIndex\":4},{\"shape\":\"edge\",\"attrs\":{\"line\":{\"stroke\":\"#747474\"}},\"id\":\"eff369b0-6888-47bc-8e1a-8061340fd6b2\",\"router\":{\"name\":\"manhattan\"},\"zIndex\":5,\"source\":{\"cell\":\"fcee9a7f97d6498e8734954be9ae607a\"},\"target\":{\"cell\":\"1889019b8df04ec8ad8a6172b50ba2f6\"}},{\"position\":{\"x\":500,\"y\":340},\"size\":{\"width\":100,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"rgba(60,58,58,0.8)\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"装配\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"rgba(60,58,58,0.8)\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"cac5e8fbe0924c09a0f48fc0aa6d9647\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"data\":{\"a\":1},\"zIndex\":6},{\"shape\":\"edge\",\"attrs\":{\"line\":{\"stroke\":\"#747474\"}},\"id\":\"e33f74a7-956b-4b65-bb96-44c63351c8fd\",\"router\":{\"name\":\"manhattan\"},\"zIndex\":7,\"source\":{\"cell\":\"1889019b8df04ec8ad8a6172b50ba2f6\"},\"target\":{\"cell\":\"cac5e8fbe0924c09a0f48fc0aa6d9647\"}},{\"position\":{\"x\":100,\"y\":340},\"size\":{\"width\":100,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"rgba(60,58,58,0.8)\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"检验入库\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"rgba(60,58,58,0.8)\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"341b8e2a21394ae1b87b4b49007587a9\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"data\":{\"a\":1},\"zIndex\":8},{\"shape\":\"edge\",\"attrs\":{\"line\":{\"stroke\":\"#747474\"}},\"id\":\"2edc5d7d-67c5-4202-8cbd-62fa78ff6978\",\"router\":{\"name\":\"manhattan\"},\"zIndex\":9,\"source\":{\"cell\":\"cac5e8fbe0924c09a0f48fc0aa6d9647\"},\"target\":{\"cell\":\"341b8e2a21394ae1b87b4b49007587a9\"}},{\"position\":{\"x\":100,\"y\":100},\"size\":{\"width\":500,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"#3C3A3A\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"铜排加工\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"#3C3A3A\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"880c54581a6846898ddb1f1b6260b8fe\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"zIndex\":10},{\"position\":{\"x\":100,\"y\":400},\"size\":{\"width\":500,\"height\":40},\"attrs\":{\"text\":{\"fill\":\"#3C3A3A\",\"refX\":\"50%\",\"refY\":\"50%\",\"xAlign\":\"middle\",\"yAlign\":\"middle\",\"textWrap\":{\"text\":\"元器件装配\",\"height\":\"50%\",\"ellipsis\":true,\"breakWord\":true}},\"body\":{\"refWidth\":1,\"refHeight\":1,\"stroke\":\"#3C3A3A\",\"strokeWidth\":1,\"fill\":\"#fff\",\"rx\":8,\"ry\":8}},\"visible\":true,\"shape\":\"rect\",\"id\":\"cc0b33be8e134f58b04f10e55581a721\",\"markup\":[{\"tagName\":\"rect\",\"selector\":\"body\"},{\"tagName\":\"text\",\"selector\":\"text\"}],\"zIndex\":11}]}" +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/setps.json b/modules/carbon-track/src/main/resources/setps.json new file mode 100644 index 0000000..06518b0 --- /dev/null +++ b/modules/carbon-track/src/main/resources/setps.json @@ -0,0 +1,37 @@ +[ + { + "orderNum": "1", + "processCode": "A001", + "processName": "冲孔", + "evIds": "159136210321707008", + "nodeId": "b4c55893df6b4350b2a1c31454284117" + }, + { + "orderNum": "2", + "processCode": "A002", + "processName": "折弯", + "evIds": "159136210321707008", + "nodeId": "fcee9a7f97d6498e8734954be9ae607a" + }, + { + "orderNum": "3", + "processCode": "A003", + "processName": "烘干", + "evIds": "159136210321707008", + "nodeId": "1889019b8df04ec8ad8a6172b50ba2f6" + }, + { + "orderNum": "4", + "processCode": "A004", + "processName": "装配", + "evIds": "159136210321707008", + "nodeId": "cac5e8fbe0924c09a0f48fc0aa6d9647" + }, + { + "orderNum": "5", + "processCode": "A005", + "processName": "检验入库", + "evIds": "159136210321707008", + "nodeId": "341b8e2a21394ae1b87b4b49007587a9" + } +] \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/transport.json b/modules/carbon-track/src/main/resources/transport.json new file mode 100644 index 0000000..0193a60 --- /dev/null +++ b/modules/carbon-track/src/main/resources/transport.json @@ -0,0 +1,5 @@ +{ + "evId": "166755918688608257", + "unit": "km", + "type": "1" +} \ No newline at end of file diff --git a/modules/carbon-track/src/main/resources/use.json b/modules/carbon-track/src/main/resources/use.json new file mode 100644 index 0000000..70bdb46 --- /dev/null +++ b/modules/carbon-track/src/main/resources/use.json @@ -0,0 +1,7 @@ +{ + "life": 25.0000000000, + "evId": "159136210321707008", + "use": 10.0000000000, + "unit": "kWh", + "type": "2" +} \ No newline at end of file diff --git a/modules/configuration/pom.xml b/modules/configuration/pom.xml new file mode 100644 index 0000000..e5627da --- /dev/null +++ b/modules/configuration/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + com.thing.modules + configuration + jar + ThingBI Server Modules configuration + + UTF-8 + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + + + com.mybatis-flex + mybatis-flex-spring-boot-starter + + + org.mybatis + mybatis-spring + + + + + com.mybatis-flex + mybatis-flex-processor + provided + + + org.projectlombok + lombok + + + com.thing.common + core + + + + + + + com.thing.modules + thing + + + + + \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/controller/IotBoardManageController.java b/modules/configuration/src/main/java/com/thing/configuration/board/controller/IotBoardManageController.java new file mode 100644 index 0000000..b7731b3 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/controller/IotBoardManageController.java @@ -0,0 +1,161 @@ +package com.thing.configuration.board.controller; + + +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.IsDefaultEnum; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.configuration.board.dto.IotBoardManageDTO; +import com.thing.configuration.board.service.IotBoardManageService; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; +import java.util.Set; + + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +@RestController +@RequestMapping("board/iotboardmanage") +@Tag(name="组态大屏管理") +public class IotBoardManageController { + @Autowired + private IotBoardManageService iotBoardManageService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name ="name",description ="看板名称"), + @Parameter(name ="type",description ="看板类型") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotBoardManageService.pageIotBoardManageDTO(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotBoardManageDTO data = iotBoardManageService.getIotBoardManageDTO(id); + checkParam(data); + return new Result().ok(data); + } + + public static void checkParam(IotBoardManageDTO data) { + if(ObjectUtil.equals(data.getIsTemplate(), IsDefaultEnum.Y.getValue())) { + if(!"1001".equals(String.valueOf(UserContext.getTenantCode()))){ + data.setIsOperate("1"); + } + } + if (!String.valueOf(data.getTenantCode()).equals(String.valueOf(UserContext.getTenantCode()))){ + if(!UserContext.isAdmin()){ + data.setIsOperate("1"); + } + } + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("board:iotboardmanage:save") + public Result save(@RequestBody IotBoardManageDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + return iotBoardManageService.saveIotBoardManageDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("board:iotboardmanage:update") + public Result update(@RequestBody IotBoardManageDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + iotBoardManageService.updateIotBoardManageDTO(dto); + + return new Result().ok("修改成功!"); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("board:iotboardmanage:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + return iotBoardManageService.deleteIotBoardManageDTO(ids); + } + + @GetMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name ="name",description ="看板名称"), + @Parameter(name ="type",description ="看板类型") + }) + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = iotBoardManageService.listIotBoardManageDTO(params); + JsonProcessingUtils.exportJson(response, list, "group.json"); + } + + @PostMapping("importJson") + @Operation(summary="json导入,返回失败的数据信息") + @RequiresPermissions("board:iotboardmanage:importJson") + public Result> importJson(MultipartFile file, HttpServletRequest request) { + return new Result>().ok(iotBoardManageService.importJson(file, request)); + } + + + + @GetMapping("getAllTemplate") + @Operation(summary="获取所有为模板的大屏数据") + public Result> getAllTemplate(){ + return new Result>().ok(iotBoardManageService.getAllTemplate()); + } + + + @GetMapping("getAllType") + @Operation(summary="获取所有看板类型") + public Result> getAllType(){ + return new Result>().ok(iotBoardManageService.getAllType()); + } + + + @GetMapping("/copy/{id}") + @Operation(summary="复制") + @RequiresPermissions("board:iotboardmanage:copy") + public Result copy(@PathVariable("id") Long id){ + return iotBoardManageService.copy(id); + } + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/dto/IotBoardManageDTO.java b/modules/configuration/src/main/java/com/thing/configuration/board/dto/IotBoardManageDTO.java new file mode 100644 index 0000000..a5da096 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/dto/IotBoardManageDTO.java @@ -0,0 +1,67 @@ +package com.thing.configuration.board.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.configuration.configuration.dto.IotConfigurationDesigDTO; +import com.thing.sys.biz.dto.SysMenuDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +@Data +@Schema( name= "组态大屏管理") +public class IotBoardManageDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "看板id主键") + private Long id; + @Schema(description = "看板名称") + private String name; + @Schema(description = "看板类型") + private String type; + @Schema(description = "缩略图url") + private String thumbnailUrl; + @Schema(description = "看板详情的背景图片") + private String backgroundImage; + @Schema(description = "看板详情的颜色") + private String backgroundColor; + @Schema(description = "看板详情的对比度") + private String backgroundRepeat; + @Schema(description = "看板详情的尺寸") + private String backgroundSize; + @Schema(description = "是否默认背景") + private Boolean isDefaultBackImg; + @Schema(description = "是否为模板") + private Object isTemplate; + @Schema(description = "备注") + private String remarks; + @Schema(description = "配置菜单信息") + private SysMenuDTO sysMenuDTO; + @Schema(description = "看板预览url") + private String previewUrl; + @Schema(description = "模板id") + private String templateId; + private IotConfigurationDesigDTO iotConfigurationDesigDTO; + @Schema(description = "是否可操作编辑,0可编辑,1不可编辑") + private String isOperate; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/entity/IotBoardManageEntity.java b/modules/configuration/src/main/java/com/thing/configuration/board/entity/IotBoardManageEntity.java new file mode 100644 index 0000000..d49dd04 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/entity/IotBoardManageEntity.java @@ -0,0 +1,56 @@ +package com.thing.configuration.board.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_board_manage") +public class IotBoardManageEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 看板名称 + */ + private String name; + /** + * 看板类型 + */ + private String type; + /** + * 缩略图url + */ + private String thumbnailUrl; + /** + * 看板详情的背景图片 + */ + private String backgroundImage; + /** + * 是否为模板 + */ + private Object isTemplate; + /** + * 备注 + */ + private String remarks; + + /** + * 看板预览url + */ + private String previewUrl; + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/excel/IotBoardManageExcel.java b/modules/configuration/src/main/java/com/thing/configuration/board/excel/IotBoardManageExcel.java new file mode 100644 index 0000000..e7542b7 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/excel/IotBoardManageExcel.java @@ -0,0 +1,28 @@ +package com.thing.configuration.board.excel; + +import lombok.Data; + +import java.util.Date; + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +@Data +public class IotBoardManageExcel { + private Long id; + private String name; + private String type; + private String thumbnailUrl; + private Object isTemplate; + private String remarks; + private Long tenantCode; + private Long companyId; + private Long deptId; + private Long creator; + private Date createDate; + private Long updater; + private Date updateDate; +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/mapper/IotBoardManageMapper.java b/modules/configuration/src/main/java/com/thing/configuration/board/mapper/IotBoardManageMapper.java new file mode 100644 index 0000000..c1d9293 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/mapper/IotBoardManageMapper.java @@ -0,0 +1,17 @@ +package com.thing.configuration.board.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.configuration.board.entity.IotBoardManageEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 组态大屏管理 +* +* @author xc +* @since 3.0 2023-05-10 +*/ +@Mapper +public interface IotBoardManageMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/service/IotBoardManageService.java b/modules/configuration/src/main/java/com/thing/configuration/board/service/IotBoardManageService.java new file mode 100644 index 0000000..58610df --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/service/IotBoardManageService.java @@ -0,0 +1,42 @@ +package com.thing.configuration.board.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.configuration.board.dto.IotBoardManageDTO; +import com.thing.configuration.board.entity.IotBoardManageEntity; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +public interface IotBoardManageService extends IBaseService { + + Result saveIotBoardManageDTO(IotBoardManageDTO dto); + + PageData pageIotBoardManageDTO(Map params); + + void updateIotBoardManageDTO(IotBoardManageDTO dto); + + Result deleteIotBoardManageDTO(Long[] ids); + + IotBoardManageDTO getIotBoardManageDTO(Long id); + + List importJson(MultipartFile file, HttpServletRequest request); + + List getAllTemplate(); + + Set getAllType(); + + Result copy(Long id); + + List listIotBoardManageDTO(Map params); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/board/service/impl/IotBoardManageServiceImpl.java b/modules/configuration/src/main/java/com/thing/configuration/board/service/impl/IotBoardManageServiceImpl.java new file mode 100644 index 0000000..1f1d186 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/board/service/impl/IotBoardManageServiceImpl.java @@ -0,0 +1,438 @@ +package com.thing.configuration.board.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.configuration.board.controller.IotBoardManageController; +import com.thing.configuration.board.dto.IotBoardManageDTO; +import com.thing.configuration.board.entity.IotBoardManageEntity; +import com.thing.configuration.board.mapper.IotBoardManageMapper; +import com.thing.configuration.board.service.IotBoardManageService; +import com.thing.configuration.configuration.dto.IotConfigurationDesigDTO; +import com.thing.configuration.configuration.entity.IotConfigurationDesigEntity; +import com.thing.configuration.configuration.mapper.IotConfigurationDesigMapper; +import com.thing.configuration.configuration.service.IotConfigurationDesigService; +import com.thing.sys.biz.dto.SysMenuDTO; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysLanguageEntity; +import com.thing.sys.biz.entity.SysMenuEntity; +import com.thing.sys.biz.entity.SysRoleMenuEntity; +import com.thing.sys.biz.mapper.SysLanguageMapper; +import com.thing.sys.biz.mapper.SysRoleMenuMapper; +import com.thing.sys.biz.service.SysLanguageService; +import com.thing.sys.biz.service.SysMenuService; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysRoleService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.util.TenantSubsetUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 组态大屏管理 + * + * @author xc + * @since 3.0 2023-05-10 + */ +@Service +public class IotBoardManageServiceImpl extends BaseServiceImpl implements IotBoardManageService { + + @Resource private SysMenuService sysMenuService; + @Resource private SysLanguageService sysLanguageService; + @Resource private SysRoleService sysRoleService; + @Resource private SysRoleMenuService sysRoleMenuService; + @Resource private SysLanguageMapper sysLanguageDao; + @Resource private SysRoleMenuMapper sysRoleMenuDao; + @Resource private TenantSubsetUtil tenantSubsetUtil; + @Resource private IotConfigurationDesigMapper iotConfigurationDesigDao; + @Resource @Lazy private IotConfigurationDesigService iotConfigurationDesigService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryColumn nameColumn = column("name"); + QueryColumn idColumn = column("id::varchar"); + + QueryWrapper wrapper = new QueryWrapper(); + String type = (String) params.get("type"); + wrapper.like( "type", type,StringUtils.isNotBlank(type)); + String name = (String) params.get("name"); + if(StringUtils.isNotEmpty(name)){ + try { + Long key = Long.parseLong(name); + wrapper.and(nameColumn.like(name).or(idColumn.like(key))); + } catch (NumberFormatException e) { + wrapper.and(nameColumn.like(name)); + } + } + + String ids = (String) params.get("ids"); + if(StringUtils.isNotBlank(ids)&&!"null".equals(params.get("ids"))){ + List idList = Arrays.asList(ids.split(",")); + wrapper.in( "id", idList,CollectionUtils.isNotEmpty(idList)); + } + //切换租户/租户/企业登陆 + /*wrapper.and(w->w.eq("is_template",true).or(). + in("tenant_code",tenantSubsetUtil.paramsAddTenantCodeList(true)));*/ + wrapper.in("tenant_code",tenantSubsetUtil.paramsAddTenantCodeList(true)); + return wrapper; + } + + + @Override + @Transactional + public Result saveIotBoardManageDTO(IotBoardManageDTO dto) { + QueryColumn is_template = column("is_template"); + QueryColumn tenant_code = column("tenant_code"); + +// dto.setCreateDate(System.currentTimeMillis()); +// dto.setUpdateDate(null); +// dto.setTenantCode(null); +// dto.setCompanyId(null); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( "type", dto.getType(),ObjectUtil.isNotEmpty(dto.getType())); + wrapper.eq( "name", dto.getName(),ObjectUtil.isNotEmpty(dto.getName())); + wrapper.and(is_template.eq(true).or(tenant_code.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + + if(this.mapper.selectCountByQuery(wrapper)>0){ + return new Result().error("大屏看板已存在,请勿重复添加!"); + } + IotBoardManageEntity entity =ConvertUtils.convertWithTypeAdapt(dto, IotBoardManageEntity.class); + entity.setId(null); + this.mapper.insert(entity); + if(ObjectUtil.isNotNull(dto.getSysMenuDTO())&&ObjectUtil.isNotEmpty(dto.getSysMenuDTO().getName())){ + //新增菜单信息 + SysMenuDTO menuDTO = dto.getSysMenuDTO(); + menuDTO.setType(0); + menuDTO.setOpenStyle(0); + menuDTO.setSaView(1); + menuDTO.setUrl("scada/preview?id=" + entity.getId()); + SysMenuEntity sysMenuEntity = ConvertUtils.sourceToTarget(menuDTO, SysMenuEntity.class); + sysMenuService.save(sysMenuEntity); + sysLanguageService.saveOrUpdate("sys_menu", sysMenuEntity.getId(), "name", sysMenuEntity.getName(), HttpContextUtils.getLanguage()); + //将菜单分配给对应的租户/企业下的默认角色 + SysRoleDTO sysRoleDTO = sysRoleService.getDefaultRole(new HashMap<>(1)); + if (ObjectUtil.isNotNull(sysRoleDTO)){ + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setRoleId(sysRoleDTO.getId()); + sysRoleMenuEntity.setMenuId(sysMenuEntity.getId()); + sysRoleMenuService.save(sysRoleMenuEntity); + } + } + if(StringUtils.isNotEmpty(dto.getTemplateId())){ + IotConfigurationDesigDTO configurationDesigDTO=null; + if(ObjectUtil.isNotNull(dto.getIotConfigurationDesigDTO())){ + configurationDesigDTO=dto.getIotConfigurationDesigDTO(); + }else { + configurationDesigDTO = iotConfigurationDesigService.getDetailByBoardManageId(String.valueOf(dto.getTemplateId())); + } + if(ObjectUtil.isNotNull(configurationDesigDTO)){ + configurationDesigDTO.setId(null); + configurationDesigDTO.setBoardManageId(entity.getId()); + configurationDesigDTO.setPageName(dto.getName()); + configurationDesigDTO.setApiSetIds(null); + iotConfigurationDesigService.saveDto(configurationDesigDTO); + } + } + return new Result().ok("添加成功!"); + } + + @Override + public PageData pageIotBoardManageDTO(Map params) { + PageData pageData = this.getPageData(params,IotBoardManageDTO.class); + pageData.getList().forEach(temp->{ + SysMenuDTO sysMenuDTO = sysMenuService.getByUrl("scada/preview?id=" + temp.getId()); + if (ObjectUtil.isNotNull(sysMenuDTO)) { + temp.setSysMenuDTO(sysMenuDTO); + } + IotConfigurationDesigDTO dto = iotConfigurationDesigService.getDetailByBoardManageId(String.valueOf(temp.getId())); + if(null!=dto){ + temp.setBackgroundColor(dto.getBackgroundColor()); + temp.setBackgroundRepeat(dto.getBackgroundRepeat()); + temp.setBackgroundSize(dto.getBackgroundSize()); + temp.setIsDefaultBackImg(dto.getIsDefaultBackImg()); + } + + IotBoardManageController.checkParam(temp); + + }); + + return pageData; + } + + @Override + public void updateIotBoardManageDTO(IotBoardManageDTO dto) { + //更新看板管理信息 + IotBoardManageEntity entity = ConvertUtils.sourceToTarget(dto, IotBoardManageEntity.class); + updateById(entity); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("board_manage_id",entity.getId()); + IotConfigurationDesigEntity desigEntity = iotConfigurationDesigDao.selectOneByQuery(wrapper); + desigEntity.setPageName(entity.getName()); + + if(ObjectUtil.isNotNull(dto.getSysMenuDTO())&&StringUtils.isNotEmpty(dto.getSysMenuDTO().getName())){ + //更新菜单信息 + SysMenuDTO menuDTO = dto.getSysMenuDTO(); + if (ObjectUtil.isNotNull(menuDTO.getId())) { + sysMenuService.update(menuDTO); + } else { + //新增菜单信息 + menuDTO.setType(0); + menuDTO.setOpenStyle(0); + menuDTO.setSaView(1); + menuDTO.setUrl("scada/preview?id=" + entity.getId()); + SysMenuEntity sysMenuEntity = ConvertUtils.sourceToTarget(menuDTO, SysMenuEntity.class); + sysMenuService.save(sysMenuEntity); + sysLanguageService.saveOrUpdate("sys_menu", sysMenuEntity.getId(), "name", sysMenuEntity.getName(), HttpContextUtils.getLanguage()); + + //将菜单分配给对应的租户/企业下的默认角色 + SysRoleDTO sysRoleDTO = sysRoleService.getDefaultRole(new HashMap<>(1)); + if (ObjectUtil.isNull(sysRoleDTO)) return; + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setRoleId(sysRoleDTO.getId()); + sysRoleMenuEntity.setMenuId(sysMenuEntity.getId()); + sysRoleMenuService.save(sysRoleMenuEntity); + } + } else { + List entityList = mapper.selectListByQuery(QueryWrapper.create().in(IotBoardManageEntity::getId, entity.getId())); + List urlList = entityList.stream().map(item -> "scada/preview?id=" + item.getId()).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(urlList)){ + List sysMenuDTOList = sysMenuService.getByUrlList(urlList); + if (CollectionUtil.isNotEmpty(sysMenuDTOList)) { + List menuIdList = sysMenuDTOList.stream().map(SysMenuDTO::getId).collect(Collectors.toList()); + sysRoleMenuDao.deleteByMenuIds(menuIdList); + //删除菜单表 + sysMenuService.batchDelete((Long[]) menuIdList.toArray()); + sysLanguageDao.deleteByQuery(QueryWrapper.create().in(SysLanguageEntity::getTableId, menuIdList)); + } + } + } + } + + + @Override + @Transactional + public Result deleteIotBoardManageDTO(Long[] ids) { + List entityList = mapper.selectListByQuery(QueryWrapper.create().in(IotBoardManageEntity::getId, ids)); + //删除角色与菜单的关联表 + List urlList = entityList.stream().map(item -> "scada/preview?id=" + item.getId()).collect(Collectors.toList()); + List sysMenuDTOList = sysMenuService.getByUrlList(urlList); + if (CollectionUtil.isNotEmpty(sysMenuDTOList)) { +// List menuIdList = sysMenuDTOList.stream().map(SysMenuDTO::getId).collect(Collectors.toList()); +// sysRoleMenuDao.deleteByMenuIds(menuIdList); +// //删除菜单表 +// sysMenuService.removeByIds(menuIdList); +// sysLanguageDao.deleteByQuery(QueryWrapper.create().in(SysLanguageEntity::getTableId, menuIdList)); + } + //删除大屏管理表 + batchDelete(ids); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("board_manage_id",ids); + iotConfigurationDesigDao.deleteByQuery(wrapper); + return new Result().ok("删除成功"); + } + + @Override + public IotBoardManageDTO getIotBoardManageDTO(Long id) { + IotBoardManageDTO data =this.getByIdAs(id,IotBoardManageDTO.class); + //id 页面id为。组态设计详情id + SysMenuDTO sysMenuDTO = sysMenuService.getByUrl("scada/preview?id=" + data.getId()); + + if (ObjectUtil.isNotNull(sysMenuDTO)) { + data.setSysMenuDTO(sysMenuDTO); + } + IotConfigurationDesigDTO dto = iotConfigurationDesigService.getDetailByBoardManageId(String.valueOf(data.getId())); + if(null!=dto){ + data.setBackgroundColor(dto.getBackgroundColor()); + data.setBackgroundRepeat(dto.getBackgroundRepeat()); + data.setBackgroundSize(dto.getBackgroundSize()); + data.setIsDefaultBackImg(dto.getIsDefaultBackImg()); + + } + + return data; + } + + + + @Override + public List importJson(MultipartFile file, HttpServletRequest request) { + List resultErrorList = new ArrayList<>(); + + String jsonString = JsonProcessingUtils.readJson(file); + List iotGroupList = JSON.parseObject(jsonString, new TypeReference<>() { + }); + if (CollectionUtil.isEmpty(iotGroupList)) { + throw new SysException("导入json为空,请检查json后再进行导入"); + } + for (IotBoardManageDTO temp : iotGroupList) { +// temp.setTenantCode(null); +// temp.setCompanyId(null); +// temp.setDeptId(null); +// temp.setCreateDate(null); +// temp.setCreator(null); + temp.setTemplateId(Long.toString(temp.getId())); + if(ErrorCode.INTERNAL_SERVER_ERROR==this.saveIotBoardManageDTO(temp).getCode()){ + resultErrorList.add(temp); + }; + } + return resultErrorList; + } + + @Override + public List getAllTemplate() { + QueryColumn is_default = column("is_default"); + QueryColumn tenant_code = column("tenant_code"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("is_template", true); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq(0).or(tenant_code.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + List iotBoardManageDTOS = ConvertUtils.sourceToTarget(mapper.selectListByQuery(wrapper), IotBoardManageDTO.class); + for (IotBoardManageDTO iotBoardManageDTO : iotBoardManageDTOS) { + IotConfigurationDesigDTO dto = iotConfigurationDesigService.getDetailByBoardManageId(String.valueOf(iotBoardManageDTO.getId())); + if(null!=dto){ + iotBoardManageDTO.setBackgroundColor(dto.getBackgroundColor()); + iotBoardManageDTO.setBackgroundRepeat(dto.getBackgroundRepeat()); + iotBoardManageDTO.setBackgroundSize(dto.getBackgroundSize()); + iotBoardManageDTO.setIsDefaultBackImg(dto.getIsDefaultBackImg()); + } + } + return iotBoardManageDTOS.stream().sorted(Comparator.comparing(IotBoardManageDTO::getCreateDate).reversed()).collect(Collectors.toList()); + } + + @Override + public Set getAllType() { + Set result = new HashSet<>(); + QueryWrapper wrapper = new QueryWrapper(); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + wrapper.eq("tenant_code",currentTenantCode); + wrapper.select("type"); + this.mapper.selectListByQuery(wrapper).forEach(temp->{ + result.add(temp.getType()); + }); + return result; + } + + @Override + @Transactional + public Result copy(Long id) { + IotBoardManageDTO data =this.getByIdAs(id,IotBoardManageDTO.class); + String name = data.getName()+"_COPY"+UUID.randomUUID().toString().substring(1,5); + data.setId(null); +// data.setCreator(SecurityUser.getUser().getId()); +// data.setCreateDate(System.currentTimeMillis()); +// data.setUpdater(null); +// data.setUpdateDate(null); + data.setName(name); + this.saveDto(data); + IotConfigurationDesigDTO desigDTO = iotConfigurationDesigService.getDetailByBoardManageId(String.valueOf(id)); + if(ObjectUtil.isNotNull(desigDTO)){ + desigDTO.setBoardManageId(data.getId()); + desigDTO.setId(null); + desigDTO.setPageName(desigDTO.getPageName()+"_COPY"+UUID.randomUUID().toString().substring(1,5)); + iotConfigurationDesigService.saveDto(desigDTO); + } + return new Result().ok("复制成功"); + } + + @Override + public List listIotBoardManageDTO(Map params) { + String val ="{\n" + + "\"normalData\": {\n" + + "\"dataPoint\": \"\",\n" + + "\"compareType\": \"\",\n" + + "\"conditionVariables\": [\n" + + "{\n" + + "\"type\": \"rangeImage\",\n" + + "\"label\": \"范围/图片\",\n" + + "\"name\": \"\"\n" + + "}\n" + + "],\n" + + "\"defaultValue\": \"[]\",\n" + + "\"unit\": \"\"\n" + + "},\n" + + "\"uiData\": {\n" + + "\"dataPoint\": \"\",\n" + + "\"compareType\": \"\",\n" + + "\"conditionVariables\": [\n" + + "],\n" + + "\"defaultValue\": \"[]\"\n" + + "},\n" + + "\"animationData\": {\n" + + "\"animationCombo\": [\n" + + "{\n" + + "\"min\": \"\",\n" + + "\"max\": \"\",\n" + + "\"animationName\": \"旋转\",\n" + + "\"defaultValue\": \"[]\"\n" + + "}\n" + + "]\n" + + "},\n" + + "\"hiddenData\": {\n" + + "\"hiddenCombo\": [\n" + + "{\n" + + "\"dataPoint\": \"\",\n" + + "\"min\": \"\",\n" + + "\"max\": \"\",\n" + + "\"showOrHiddenName\": \"隐藏\",\n" + + "\"defaultValue\": \"[]\"\n" + + "}\n" + + "]\n" + + "}\n" + + "}"; + List list = this.listAs(params,IotBoardManageDTO.class); + list.forEach(temp->{ + IotConfigurationDesigDTO desigDTO = iotConfigurationDesigService.getDetailByBoardManageId(temp.getId().toString()); + if(ObjectUtil.isNotNull(desigDTO)){ + JSONObject object = JSONObject.parseObject(desigDTO.getPictureData()); + JSONArray object1 = JSONArray.parseArray(JSONObject.toJSONString(object.get("nodes"))); + JSONArray object2 = new JSONArray(); + object1.parallelStream().forEach(info->{ + JSONObject o1 = JSON.parseObject(info.toString()); + JSONObject o2 = JSONObject.parseObject(o1.getString("properties")); + o2.put("dynamic",JSONObject.parseObject(val)); + o1.put("properties",o2); + object2.add(o1); + }); + object.put("nodes",object2); + desigDTO.setPictureData(JSONObject.toJSONString(object)); + desigDTO.setApiSetIds(""); + desigDTO.setIotThingApiDTOList(Lists.newArrayList()); + temp.setIotConfigurationDesigDTO(desigDTO); + } + }); + return list; + } +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/controller/IotConfigurationDesigController.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/controller/IotConfigurationDesigController.java new file mode 100644 index 0000000..92ca678 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/controller/IotConfigurationDesigController.java @@ -0,0 +1,74 @@ +package com.thing.configuration.configuration.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.configuration.configuration.dto.IotConfigurationDesigDTO; +import com.thing.configuration.configuration.service.IotConfigurationDesigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +/** +* 组态设计管理 +* +* @author xc +* @since 3.0 2023-05-29 +*/ +@RestController +@RequestMapping("iotconfigurationdesig") +@Tag(name="组态设计管理") +public class IotConfigurationDesigController { + @Autowired + private IotConfigurationDesigService iotConfigurationDesigService; + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotConfigurationDesigDTO data = iotConfigurationDesigService.getIotConfigurationDesigDTOById(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存 boardManageId必传") + public Result save(@RequestBody IotConfigurationDesigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + iotConfigurationDesigService.saveIotConfigurationDesigDTO(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotConfigurationDesigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + iotConfigurationDesigService.updateIotConfigurationDesigDTO(dto); + + return new Result(); + } + + @GetMapping("getDetailByBoardManageId") + @Operation(summary="获取组态设计信息,根据组态看板id") + @Parameters({ + @Parameter(name ="boardManageId", description = "组态看板id"), + }) + public Result getDetailByBoardManageId(String boardManageId){ + IotConfigurationDesigDTO data = iotConfigurationDesigService.getDetailByBoardManageId(boardManageId); + + return new Result().ok(data); + } +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/dto/IotConfigurationDesigDTO.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/dto/IotConfigurationDesigDTO.java new file mode 100644 index 0000000..900cf6b --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/dto/IotConfigurationDesigDTO.java @@ -0,0 +1,56 @@ +package com.thing.configuration.configuration.dto; + +import com.thing.thing.api.dto.IotThingApiDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 组态设计管理 +* +* @author xc +* @since 3.0 2023-05-29 +*/ +@Data +@Schema( name= "组态设计管理") +public class IotConfigurationDesigDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + @Schema(description = "主键id") + private Long id; + @Schema(description = "组态看板id") + private Long boardManageId; + @Schema(description = "看板宽度") + private Integer width; + @Schema(description = "看板高度") + private Integer height; + @Schema(description = "背景图片") + private String backgroundImage; + @Schema(description = "背景颜色") + private String backgroundColor; + @Schema(description = "backgroundRepeat") + private String backgroundRepeat; + @Schema(description = "页面名称") + private String pageName; + @Schema(description = "device_ratio") + private String deviceRatio; + @Schema(description = "background_size") + private String backgroundSize; + @Schema(description = "图数据") + private String pictureData; + @Schema(description = "缩略图") + private String thumbnailUrl; + @Schema(description = "超级apiIds,多个id以英文逗号,分割") + private String apiSetIds; + @Schema(description = "超级api详细信息集合,用于组态设计编辑/查看,回显使用") + private List iotThingApiDTOList; + public String previewUrl; + @Schema(description = "是否默认背景") + private Boolean isDefaultBackImg; + @Schema(description = "boardType") + private String boardType; +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/entity/IotConfigurationDesigEntity.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/entity/IotConfigurationDesigEntity.java new file mode 100644 index 0000000..b133cfa --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/entity/IotConfigurationDesigEntity.java @@ -0,0 +1,82 @@ +package com.thing.configuration.configuration.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 组态设计管理 + * + * @author xc + * @since 3.0 2023-05-29 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_configuration_design") +public class IotConfigurationDesigEntity { + private static final long serialVersionUID = 1L; + + /** + * 主键id + */ + @Id + private Long id; + /** + * 组态看板id + */ + private Long boardManageId; + /** + * 看板宽度 + */ + private Integer width; + /** + * 看板高度 + */ + private Integer height; + /** + * 背景图片 + */ + private String backgroundImage; + /** + * 背景颜色 + */ + private String backgroundColor; + /** + * backgroundRepeat + */ + private String backgroundRepeat; + /** + * 页面名称 + */ + private String pageName; + /** + * device_ratio + */ + private String deviceRatio; + /** + * background_size + */ + private String backgroundSize; + /** + * 图数据 + */ + private String pictureData; + + /** + *超级apiid,多个id以英文逗号,分割 + */ + private String apiSetIds; + + /** + * 是否默认背景 + */ + private Boolean isDefaultBackImg; + + /** + * boardType + */ + private String boardType; +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/mapper/IotConfigurationDesigMapper.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/mapper/IotConfigurationDesigMapper.java new file mode 100644 index 0000000..ea29715 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/mapper/IotConfigurationDesigMapper.java @@ -0,0 +1,16 @@ +package com.thing.configuration.configuration.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.configuration.configuration.entity.IotConfigurationDesigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 组态设计管理 +* +* @author xc +* @since 3.0 2023-05-29 +*/ +@Mapper +public interface IotConfigurationDesigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/service/IotConfigurationDesigService.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/service/IotConfigurationDesigService.java new file mode 100644 index 0000000..8fb4c1e --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/service/IotConfigurationDesigService.java @@ -0,0 +1,28 @@ +package com.thing.configuration.configuration.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.configuration.configuration.dto.IotConfigurationDesigDTO; +import com.thing.configuration.configuration.entity.IotConfigurationDesigEntity; + +import java.util.List; + +/** + * 组态设计管理 + * + * @author xc + * @since 3.0 2023-05-29 + */ +public interface IotConfigurationDesigService extends IBaseService { + + IotConfigurationDesigDTO getDetailByBoardManageId(String boardManageId); + + + List getDetailsByBoardManageIds(List ids); + + + void saveIotConfigurationDesigDTO(IotConfigurationDesigDTO dto); + + void updateIotConfigurationDesigDTO(IotConfigurationDesigDTO dto); + + IotConfigurationDesigDTO getIotConfigurationDesigDTOById(Long id); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/configuration/service/impl/IotConfigurationDesigServiceImpl.java b/modules/configuration/src/main/java/com/thing/configuration/configuration/service/impl/IotConfigurationDesigServiceImpl.java new file mode 100644 index 0000000..f377098 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/configuration/service/impl/IotConfigurationDesigServiceImpl.java @@ -0,0 +1,135 @@ +package com.thing.configuration.configuration.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.configuration.board.dto.IotBoardManageDTO; +import com.thing.configuration.board.entity.IotBoardManageEntity; +import com.thing.configuration.board.service.IotBoardManageService; +import com.thing.configuration.configuration.dto.IotConfigurationDesigDTO; +import com.thing.configuration.configuration.entity.IotConfigurationDesigEntity; +import com.thing.configuration.configuration.mapper.IotConfigurationDesigMapper; +import com.thing.configuration.configuration.service.IotConfigurationDesigService; +import com.thing.thing.api.dto.IotThingApiDTO; +import com.thing.thing.api.mapper.IotThingApiMapper; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 组态设计管理 + * + * @author xc + * @since 3.0 2023-05-29 + */ +@Service +public class IotConfigurationDesigServiceImpl extends BaseServiceImpl implements IotConfigurationDesigService { + + @Autowired + private IotBoardManageService iotBoardManageService; + @Autowired + IotThingApiMapper iotThingApiDao; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + @Override + public IotConfigurationDesigDTO getDetailByBoardManageId(String boardManageId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("board_manage_id",Long.parseLong(boardManageId)); + IotConfigurationDesigEntity entity = this.mapper.selectOneByQuery(wrapper); + if(ObjectUtil.isNotNull(entity)){ + IotConfigurationDesigDTO dto = ConvertUtils.convertWithTypeAdapt(entity,IotConfigurationDesigDTO.class); + if(StringUtils.isNotBlank(entity.getApiSetIds())){ + List apiIdList = Arrays.stream(entity.getApiSetIds().split(",")) + .map(Long::valueOf) + .collect(Collectors.toList()); + if(!apiIdList.isEmpty()){ + dto.setIotThingApiDTOList( ConvertUtils.sourceToTarget(iotThingApiDao.selectListByIds(apiIdList), IotThingApiDTO.class)); + } + } + IotBoardManageEntity manageDTO = iotBoardManageService.getById(Long.parseLong(boardManageId)); + dto.setThumbnailUrl(manageDTO.getThumbnailUrl()); + dto.setPreviewUrl(manageDTO.getPreviewUrl()); + return dto; + } + return null; + } + + @Override + public List getDetailsByBoardManageIds(List ids) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("board_manage_id",ids); + List entitys = mapper.selectListByQuery(wrapper); + if(ObjectUtil.isNotNull(entitys)){ + return ConvertUtils.sourceToTarget(entitys,IotConfigurationDesigDTO.class); + } + return null; + } + + @Override + @Transactional + public void saveIotConfigurationDesigDTO(IotConfigurationDesigDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("board_manage_id",dto.getBoardManageId()); + IotConfigurationDesigEntity entity = mapper.selectOneByQuery(wrapper); + if(ObjectUtil.isNotNull(entity)){ + dto.setId(entity.getId()); + this.updateDto(dto); + }else { + this.saveDto(dto); + } + this.updateIotBoardManageDTO(dto); + } + + @Override + @Transactional + public void updateIotConfigurationDesigDTO(IotConfigurationDesigDTO dto) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("board_manage_id",dto.getBoardManageId()); + IotConfigurationDesigEntity entity = this.mapper.selectOneByQuery(wrapper); + if(ObjectUtil.isNotNull(entity)){ + dto.setId(entity.getId()); + this.updateDto(dto); + }else { + this.saveDto(dto); + } + this.updateIotBoardManageDTO(dto); + } + + @Override + public IotConfigurationDesigDTO getIotConfigurationDesigDTOById(Long id) { + IotConfigurationDesigDTO data = this.getByIdAs(id,IotConfigurationDesigDTO.class); + if(StringUtils.isNotBlank(data.getApiSetIds())){ + List apiIds = Arrays.asList(data.getApiSetIds().split(",")); + if(!apiIds.isEmpty()){ + data.setIotThingApiDTOList( ConvertUtils.sourceToTarget(iotThingApiDao.selectListByIds(apiIds), IotThingApiDTO.class)); + } + } + return data; + } + + + /** + * 修改组态看板缩略图字段 + * @param dto + */ + public void updateIotBoardManageDTO(IotConfigurationDesigDTO dto){ + IotBoardManageDTO iotBoardManageDTO = new IotBoardManageDTO(); + iotBoardManageDTO.setId(dto.getBoardManageId()); + iotBoardManageDTO.setThumbnailUrl(dto.getThumbnailUrl()); + iotBoardManageDTO.setBackgroundImage(dto.getBackgroundImage()); + iotBoardManageDTO.setPreviewUrl(dto.getPreviewUrl()); + iotBoardManageService.updateDto(iotBoardManageDTO); + } +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/controller/IotNewSourceMaterialController.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/controller/IotNewSourceMaterialController.java new file mode 100644 index 0000000..73d3aa9 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/controller/IotNewSourceMaterialController.java @@ -0,0 +1,149 @@ +package com.thing.configuration.newmaterial.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.configuration.newmaterial.dto.IotNewSourceMaterialDTO; +import com.thing.configuration.newmaterial.service.IotNewSourceMaterialService; +import com.thing.thing.group.entity.IotGroupInfoEntity; +import com.thing.thing.group.mapper.IotGroupInfoMapper; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.List; +import java.util.Map; + + +/** +* 素材管理 +* +* @author xc +* @since 3.0 2023-05-09 +*/ +@RestController +@RequestMapping("v2/material") +@Tag(name="新素材管理") +public class IotNewSourceMaterialController { + @Autowired + private IotNewSourceMaterialService iotNewSourceMaterialService; + + @Autowired + private IotGroupInfoMapper iotGroupInfoDao; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name ="ids",description ="素材id 多个以英文逗号分割"), + @Parameter(name ="name",description ="素材名称"), + @Parameter(name ="type",description ="文件类型"), + @Parameter(name ="groupIds",description ="素材组id 多个以英文逗号分割"), + + }) + public Result> page( @RequestParam Map params){ + PageData page = iotNewSourceMaterialService.pageIotSourceMaterialDTO(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotNewSourceMaterialDTO data = iotNewSourceMaterialService.getIotSourceMaterialDTO(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody IotNewSourceMaterialDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if("0".equals(String.valueOf(dto.getIsDefault()))){ + IotGroupInfoEntity entity = new IotGroupInfoEntity(); + entity.setId(dto.getGroupId()); + entity.setIsDefault(0); + iotGroupInfoDao.update(entity); + } + return iotNewSourceMaterialService.saveIotSourceMaterialDTO(dto); + } + + + @PostMapping("saveBatch") + @Operation(summary="批量保存") + public Result saveBatch(@RequestBody IotNewSourceMaterialDTO[] dtos){ + return iotNewSourceMaterialService.saveBatch(dtos); + + } + + + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotNewSourceMaterialDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + iotNewSourceMaterialService.updateDto(dto); + if("0".equals(String.valueOf(dto.getIsDefault()))){ + IotGroupInfoEntity entity = new IotGroupInfoEntity(); + entity.setId(dto.getGroupId()); + entity.setIsDefault(0); + iotGroupInfoDao.update(entity); + } + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + iotNewSourceMaterialService.deleteIotNewSourceMaterialDTO(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name ="groupIds",description ="部件组id 多个以英文逗号分割"), + @Parameter(name ="name",description ="素材名称"), + @Parameter(name ="type",description ="文件类型"), + @Parameter(name ="ids",description ="id数组"), + }) + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + params.put("ids", ids); + List list = iotNewSourceMaterialService.listAs(params,IotNewSourceMaterialDTO.class); + JsonProcessingUtils.exportJson(response, list, "detail.json"); + + } + + + @PostMapping("importJson") + @Operation(summary="json导入,返回失败的数据信息") + public Result> importJson(MultipartFile file, HttpServletRequest request) { + return new Result>().ok(iotNewSourceMaterialService.importJson(file, request)); + } + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/dto/IotNewSourceMaterialDTO.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/dto/IotNewSourceMaterialDTO.java new file mode 100644 index 0000000..3fa8792 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/dto/IotNewSourceMaterialDTO.java @@ -0,0 +1,72 @@ +package com.thing.configuration.newmaterial.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 素材管理 + * + * @author xc + * @since 3.0 2023-05-09 + */ +@Data +@Schema( name= "素材管理") +public class IotNewSourceMaterialDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "素材id") + private Long id; + @Schema(description = "素材名称") + private String name; + @Schema(description = "素材文件类型") + private String type; + @Schema(description = "是否默认 0是,1否") + private Integer isDefault; + @Schema(description = "所属素材组id 新增/修改必传") + private Long groupId; + + @Schema(description = "所属素材组名称/不必传,回显使用") + private String groupName; + @Schema(description = "所属素材组类型/不必传,回显使用") + private String groupType; + @Schema(description = "所属素材组缩略图URL/不必传,回显使用") + private String thumbnailUrl; + + + + @Schema(description = "文件url") + private String image; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建者中文名") + private String creatorName; + + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新者中文名") + private String updaterName; + + @Schema(description = "更新时间") + private Date updateDate; + + @Schema(description = "是否可以编辑 0可编辑 1不可编辑") + private String isOperate ="0"; + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/entity/IotNewSourceMaterialEntity.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/entity/IotNewSourceMaterialEntity.java new file mode 100644 index 0000000..de5fc51 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/entity/IotNewSourceMaterialEntity.java @@ -0,0 +1,55 @@ +package com.thing.configuration.newmaterial.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 素材管理 + * + * @author xc + * @since 3.0 2023-05-09 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_source_material") +public class IotNewSourceMaterialEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 素材id + */ + @Id + private Long id; + /** + * 素材名称 + */ + private String name; + /** + * 素材类型 + */ + private String type; + /** + * 是否默认 0是,1否 + */ + private Integer isDefault; + /** + * 所属素材组id + */ + private Long groupId; + /** + * 文件url + */ + private String image; + /** + * 备注说明 + */ + private String remark; + + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/mapper/IotNewSourceMaterialMapper.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/mapper/IotNewSourceMaterialMapper.java new file mode 100644 index 0000000..15b8c85 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/mapper/IotNewSourceMaterialMapper.java @@ -0,0 +1,22 @@ +package com.thing.configuration.newmaterial.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.configuration.newmaterial.dto.IotNewSourceMaterialDTO; +import com.thing.configuration.newmaterial.entity.IotNewSourceMaterialEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 素材管理 +* +* @author xc +* @since 3.0 2023-05-09 +*/ +@Mapper +public interface IotNewSourceMaterialMapper extends PowerBaseMapper { + + List getList(Map params); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/IotNewSourceMaterialService.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/IotNewSourceMaterialService.java new file mode 100644 index 0000000..c8a8a74 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/IotNewSourceMaterialService.java @@ -0,0 +1,35 @@ +package com.thing.configuration.newmaterial.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.configuration.newmaterial.dto.IotNewSourceMaterialDTO; +import com.thing.configuration.newmaterial.entity.IotNewSourceMaterialEntity; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.List; +import java.util.Map; + +/** + * 素材管理 + * + * @author xc + * @since 3.0 2023-05-09 + */ +public interface IotNewSourceMaterialService extends IBaseService { + + PageData pageIotSourceMaterialDTO(Map params); + + IotNewSourceMaterialDTO getIotSourceMaterialDTO(Long id); + + Result saveIotSourceMaterialDTO(IotNewSourceMaterialDTO dto); + + List importJson(MultipartFile file, HttpServletRequest request); + + void deleteIotNewSourceMaterialDTO(Long[] ids); + + Result saveBatch(IotNewSourceMaterialDTO[] dtos); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/impl/IotNewSourceMaterialServiceImpl.java b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/impl/IotNewSourceMaterialServiceImpl.java new file mode 100644 index 0000000..40e94ae --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/newmaterial/service/impl/IotNewSourceMaterialServiceImpl.java @@ -0,0 +1,243 @@ +package com.thing.configuration.newmaterial.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.configuration.newmaterial.dto.IotNewSourceMaterialDTO; +import com.thing.configuration.newmaterial.entity.IotNewSourceMaterialEntity; +import com.thing.configuration.newmaterial.mapper.IotNewSourceMaterialMapper; +import com.thing.configuration.newmaterial.service.IotNewSourceMaterialService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.oss.cloud.AbstractCloudStorageService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.group.dto.IotGroupInfoDTO; +import com.thing.thing.group.entity.IotGroupInfoEntity; +import com.thing.thing.group.mapper.IotGroupInfoMapper; +import com.thing.thing.group.service.IotGroupInfoService; +import com.thing.util.TenantSubsetUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 素材管理 + * + * @author xc + * @since 3.0 2023-05-09 + */ +@Service +public class IotNewSourceMaterialServiceImpl extends BaseServiceImpl implements IotNewSourceMaterialService { + + + @Autowired + TenantSubsetUtil tenantSubsetUtil; + + @Autowired + IotGroupInfoService iotGroupInfoService; + + @Autowired + SysUserService userService; + + @Autowired + private IotGroupInfoMapper iotGroupInfoDao; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryColumn is_default = column("is_default"); + QueryColumn tenant_code = column("tenant_code"); + + QueryWrapper wrapper = new QueryWrapper(); + List group_ids = null; + String strs = (String)params.get("groupIds"); + if(StringUtils.isNotBlank(strs)&&!"null".equals(params.get("strs"))){ + group_ids = Arrays.stream(strs.split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()); + wrapper.in("group_id", group_ids,CollectionUtils.isNotEmpty(group_ids)); + } + + String type = (String) params.get("type"); + wrapper.like("type", type,StringUtils.isNotBlank(type)); + + String name = (String) params.get("name"); + wrapper.like( "name", name,StringUtils.isNotBlank(name)); + ArrayList ids = MapUtil.get(params, "ids",ArrayList.class); + if (ObjectUtil.isNotNull(ids)&&ids.size()!=0) { + wrapper.in("id", ids); + } + //切换租户/租户/企业登陆 + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq(0).or(tenant_code.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + return wrapper; + } + + + @Override + public PageData pageIotSourceMaterialDTO(Map params) { + + PageData data = getPageData(params,IotNewSourceMaterialDTO.class); + + data.getList().forEach(temp->{ + IotGroupInfoDTO groupInfoDTO = iotGroupInfoService.getByIdAs(temp.getGroupId(),IotGroupInfoDTO.class); + if(ObjectUtil.isNotNull(groupInfoDTO)){ + temp.setGroupName(groupInfoDTO.getName()); + temp.setGroupType(groupInfoDTO.getBusinessType()); + } + temp.setImage(OSSFactory.splice(temp.getImage())); + + //数据创建者,以及超管,拥有编辑删除权限, + //默认数据不得编辑删除 + checkIsOperate(temp); + if(ObjectUtil.isNotNull(temp.getUpdater())){ + SysUserDTO sysUserDTO = userService.get(temp.getUpdater()); + if(ObjectUtil.isNotNull(sysUserDTO)){ + temp.setUpdaterName(sysUserDTO.getUsername()); + } + } + if(ObjectUtil.isNotNull(temp.getCreator())){ + SysUserDTO sysUserDTO = userService.get(temp.getCreator()); + if(ObjectUtil.isNotNull(sysUserDTO)){ + temp.setCreatorName(sysUserDTO.getUsername()); + } + } + + }); + + return data; + } + + @Override + public IotNewSourceMaterialDTO getIotSourceMaterialDTO(Long id) { + IotNewSourceMaterialDTO data = getByIdAs(id,IotNewSourceMaterialDTO.class); + IotGroupInfoDTO groupInfoDTO = iotGroupInfoService.getByIdAs(data.getGroupId(),IotGroupInfoDTO.class); + data.setGroupName(groupInfoDTO.getName()); + data.setGroupType(groupInfoDTO.getBusinessType()); + data.setImage(OSSFactory.splice(data.getImage())); + //数据创建者,以及超管,拥有编辑删除权限, + //默认数据不得编辑删除 + checkIsOperate(data); + return data; + } + + private void checkIsOperate(IotNewSourceMaterialDTO data) { + if(0==data.getIsDefault()){ + if(!"1001".equals(String.valueOf(UserContext.getTenantCode()))){ + data.setIsOperate("1"); + } + } + if (!String.valueOf(data.getTenantCode()).equals(String.valueOf(UserContext.getTenantCode()))){ + if(!UserContext.isAdmin()){ + data.setIsOperate("1"); + } + } + } + + + @Override + public Result saveIotSourceMaterialDTO(IotNewSourceMaterialDTO dto) { + IotNewSourceMaterialEntity entity = ConvertUtils.convertWithTypeAdapt(dto, IotNewSourceMaterialEntity.class); + if(null!=entity.getId()){ + updateById(entity); + return new Result().ok("修改成功!"); + } + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( "group_id", dto.getGroupId(),ObjectUtil.isNotEmpty(dto.getGroupId())); + wrapper.eq("name", dto.getName(),ObjectUtil.isNotEmpty(dto.getName())); + wrapper.eq("type", dto.getType(),ObjectUtil.isNotEmpty(dto.getType())); + if(this.mapper.selectCountByQuery(wrapper)>0){ + entity.setName(entity.getName()+"_COPY"+UUID.randomUUID().toString().substring(1,5)); + } + entity.setImage(OSSFactory.cutOut(entity.getImage())); + save(entity); + return new Result().ok("添加成功!"); + } + + + @Override + public List importJson(MultipartFile file, HttpServletRequest request) { + List resultErrorList = new ArrayList<>(); + String jsonString = JsonProcessingUtils.readJson(file); + List iotGroupList = JSON.parseObject(jsonString, new TypeReference<>() { + }); + if (CollectionUtil.isEmpty(iotGroupList)) { + throw new SysException("导入json为空,请检查json后再进行导入"); + } + for (IotNewSourceMaterialDTO temp : iotGroupList) { + if(ErrorCode.INTERNAL_SERVER_ERROR==this.saveIotSourceMaterialDTO(temp).getCode()){ + resultErrorList.add(temp); + }; + } + return resultErrorList; + } + + @Override + @Transactional + public void deleteIotNewSourceMaterialDTO(Long[] ids) { + // AbstractCloudStorageService OSSService = OSSFactory.build(); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in( "id", ids); + List entityList= mapper.selectListByQuery(wrapper); +// entityList.parallelStream().forEach(temp->{ +// String str = temp.getImage(); +// if(StringUtils.isNotBlank(str)){ +// OSSService.delFile(OSSFactory.cutOut(str)); +// } +// }); + + this.batchDelete(ids); + } + + @Override + @Transactional + public Result saveBatch(IotNewSourceMaterialDTO[] dtos) { + try { + for (IotNewSourceMaterialDTO dto : dtos) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if("0".equals(String.valueOf(dto.getIsDefault()))){ + IotGroupInfoEntity entity = new IotGroupInfoEntity(); + entity.setId(dto.getGroupId()); + entity.setIsDefault(0); + iotGroupInfoDao.update(entity); + } + this.saveIotSourceMaterialDTO(dto); + } + } catch (SysException e) { + return new Result().error("批量新增失败"); + } + return new Result().ok("批量新增成功"); + } +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/controller/IotSectionDetailController.java b/modules/configuration/src/main/java/com/thing/configuration/section/controller/IotSectionDetailController.java new file mode 100644 index 0000000..f1b157a --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/controller/IotSectionDetailController.java @@ -0,0 +1,163 @@ +package com.thing.configuration.section.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.configuration.section.dto.GroupIofoDTO; +import com.thing.configuration.section.dto.IotSectionDetailDTO; +import com.thing.configuration.section.dto.SectionGroupInfoDTO; +import com.thing.configuration.section.service.IotSectionDetailService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@RestController +@RequestMapping("section/iotsectiondetail") +@Tag(name="部件管理") +public class IotSectionDetailController { + @Autowired + private IotSectionDetailService iotSectionDetailService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name ="group_ids", description = "部件组id 多个以英文逗号分割"), + @Parameter(name ="name", description = "部件名称"), + @Parameter(name ="sectionType", description = "数据类型"), + @Parameter(name ="isLocal", description = "是否连本地数据都查询出来true查出来,false不查出来(开发使用)") + + }) + public Result> page( @RequestParam Map params){ + PageData page = iotSectionDetailService.pageIotSectionDetailDTO(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + + return iotSectionDetailService.getIotSectionDetailDTO(id); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotSectionDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + return iotSectionDetailService.saveIotSectionDetailDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotSectionDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + return iotSectionDetailService.updateIotSectionDetailDTO(dto); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + iotSectionDetailService.batchDelete(ids); + return new Result(); + } + + + @PostMapping("queryListByName") + @Operation(summary="根据部件名称数组,获取部件详细信息列表") + public Result> queryListByName(@RequestBody String[] names){ + List result = iotSectionDetailService.queryListByName(names); + return new Result().ok(result); + } + + + + @GetMapping("getAllGroupName") + @Operation(summary="获取所有组名称,并获取第一个组类型缩略图作为默认图片,组态设计页面使用") + public Result> getAllGroupName(){ + + return iotSectionDetailService.getAllGroupName(); + } + + + @GetMapping("list") + @Operation(summary="列表查询,组态设计页面使用") + @Parameters({ + @Parameter(name ="groupName", description = "部件组名称"), + @Parameter(name ="name", description = "部件名称") + }) + public Result> listIotSectionDetailDTO( @RequestParam Map params){ + return iotSectionDetailService.listIotSectionDetailDTO(params); + } + + + + @GetMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name ="group_ids", description = "部件组id 多个以英文逗号分割"), + @Parameter(name ="name", description = "部件名称"), + @Parameter(name ="sectionType", description = "数据类型") + }) + public void export( @RequestParam Map params, HttpServletResponse response) { + List list = iotSectionDetailService.listAs(params,IotSectionDetailDTO.class); + fomartFiles(list); + JsonProcessingUtils.exportJson(response, list, "detail.json"); + } + + public static void fomartFiles(List list) { + list.forEach(data->{ + Map files = new HashMap<>(); + files.put("css",data.getCss()); + files.put("jsPlugin",data.getJsPlugin()); + files.put("json",data.getJson()); + files.put("javascript",data.getJavascript()); + files.put("fakeData",data.getFakeData()); + data.setFiles(files); + }); + } + + @PostMapping("importJson") + @Operation(summary="json导入,返回失败的数据信息") + public Result> importJson(MultipartFile file, HttpServletRequest request) { + return new Result>().ok(iotSectionDetailService.importJson(file, request)); + } + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/dto/GroupIofoDTO.java b/modules/configuration/src/main/java/com/thing/configuration/section/dto/GroupIofoDTO.java new file mode 100644 index 0000000..22aa3d0 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/dto/GroupIofoDTO.java @@ -0,0 +1,18 @@ +package com.thing.configuration.section.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Data +public class GroupIofoDTO { + + @Schema(description = "组名称") + private String groupName; + + @Schema(description = "缩略图url") + private String thumbnailUrl; + + +} diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionDetailDTO.java b/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionDetailDTO.java new file mode 100644 index 0000000..5b845cc --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionDetailDTO.java @@ -0,0 +1,97 @@ +package com.thing.configuration.section.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; +import java.util.Map; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@Data +@Schema( name= "部件管理") +public class IotSectionDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "部件id") + private Long id; + @Schema(description = "部件名称") + private String name; + @Schema(description = "部件别名") + private String aliasName; + @Schema(description = "是否默认") + private Boolean isDefault; + @Schema(description = "数据类型") + private String sectionType; + @Schema(description = "部件组id") + private Long groupId; + + + @Schema(description = "部件组名称(部件所属部件组)") + private String groupName; + @Schema(description = "部件组类型(部件所属部件组)") + private String groupType; + @Schema(description = "部件组类型缩略图(部件所属部件组)") + private String thumbnailUrl; + + + @Schema(description = "部件文件url") + private String image; + @Schema(description = "部件文件类型") + private String imageType; + @Schema(description = "部件设置信息") + private String config; + @Schema(description = "部件插件jsPlugin") + private String jsPlugin; + @Schema(description = "json") + private String json; + @Schema(description = "css") + private String css; + @Schema(description = "javascript") + private String javascript; + @Schema(description = "备注说明") + private String remarks; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + //@JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + //@JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long updateDate; + + @Schema(description = "是否本地") + private Boolean isLocal=false; + + @Schema(description = "json+css+jsPlugin") + private Map files; + + @Schema(description = "是否可以编辑 0可编辑,1不可编辑") + private String isOperate ="0"; + + @Schema(description = "是否远程 true远程,false本地") + private Boolean isRemote; + + + @Schema(description = "静态数据") + private String fakeData; + + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionGroupDTO.java b/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionGroupDTO.java new file mode 100644 index 0000000..c353ac2 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/dto/IotSectionGroupDTO.java @@ -0,0 +1,55 @@ +package com.thing.configuration.section.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 部件组管理 + * + * @author xc + * @since 3.0 2023-05-06 + */ +@Data +@Schema( name= "部件组管理") +public class IotSectionGroupDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "组名称") + private String groupName; + @Schema(description = "组类型") + private String groupType; + @Schema(description = "是否默认") + private Object isDefault; + @Schema(description = "缩略图url") + private String thumbnailUrl; + @Schema(description = "备注说明") + private String remarks; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + + @Schema(description = "是否可以编辑") + private Boolean isOperate =false; + +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/dto/SectionGroupInfoDTO.java b/modules/configuration/src/main/java/com/thing/configuration/section/dto/SectionGroupInfoDTO.java new file mode 100644 index 0000000..c3c04ac --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/dto/SectionGroupInfoDTO.java @@ -0,0 +1,27 @@ +package com.thing.configuration.section.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class SectionGroupInfoDTO { + + + @Schema(description = "组名称") + private String groupName; + + @Schema(description = "组类型名称") + private String groupType; + + @Schema(description = "缩略图url") + private String thumbnailUrl; + + @Schema(description = "当前组类型下,所有部件列表") + private List detailDTOList; + + + +} diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/entity/IotSectionDetailEntity.java b/modules/configuration/src/main/java/com/thing/configuration/section/entity/IotSectionDetailEntity.java new file mode 100644 index 0000000..5eac458 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/entity/IotSectionDetailEntity.java @@ -0,0 +1,87 @@ +package com.thing.configuration.section.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_section_detail") +public class IotSectionDetailEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 部件id + */ + @Id + private Long id; + /** + * 部件名称 + */ + private String name; + /** + * 部件别名 + */ + private String aliasName; + /** + * 部件组id + */ + private Long groupId; + /** + * 是否默认 + */ + private Boolean isDefault; + /** + * 部件类型 + */ + private String sectionType; + /** + * 文件url + */ + private String image; + /** + * 文件类型 + */ + private String imageType; + /** + * 部件设置信息 + */ + private String config; + /** + * 部件插件:数组结构 + */ + private String jsPlugin; + /** + * json + */ + private String json; + /** + * css + */ + private String css; + /** + * javascript + */ + private String javascript; + /** + * 备注说明 + */ + private String remarks; + + private Boolean isLocal; + private Boolean isRemote; + + private String fakeData; +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/excel/IotSectionDetailExcel.java b/modules/configuration/src/main/java/com/thing/configuration/section/excel/IotSectionDetailExcel.java new file mode 100644 index 0000000..d156fb4 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/excel/IotSectionDetailExcel.java @@ -0,0 +1,36 @@ +package com.thing.configuration.section.excel; + +import lombok.Data; + +import java.util.Date; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@Data +public class IotSectionDetailExcel { + private Long id; + private String name; + private String aliasName; + private Long groupId; + private String image; + private String imageType; + private String config; + private String jsPlugin; + private String json; + private String css; + private String javascript; + private String remarks; + private Long tenantCode; + private Long companyId; + private Long deptId; + private Long creator; + private Date createDate; + private Long updater; + private Date updateDate; + private Object isDefault; + private String sectionType; +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/mapper/IotSectionDetailMapper.java b/modules/configuration/src/main/java/com/thing/configuration/section/mapper/IotSectionDetailMapper.java new file mode 100644 index 0000000..241450f --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/mapper/IotSectionDetailMapper.java @@ -0,0 +1,27 @@ +package com.thing.configuration.section.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.configuration.section.dto.IotSectionDetailDTO; +import com.thing.configuration.section.entity.IotSectionDetailEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@Mapper +public interface IotSectionDetailMapper extends PowerBaseMapper { + + + /** + * 根据部件组名称,部件名称(模糊),当前企业创建的+默认的数据,查询附和条件的所有部件,并以部件组类型分组返回 + * @param params 查询 + * @return list + */ + List getList(Map params); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/service/IotSectionDetailService.java b/modules/configuration/src/main/java/com/thing/configuration/section/service/IotSectionDetailService.java new file mode 100644 index 0000000..5a93c2a --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/service/IotSectionDetailService.java @@ -0,0 +1,40 @@ +package com.thing.configuration.section.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.configuration.section.dto.GroupIofoDTO; +import com.thing.configuration.section.dto.IotSectionDetailDTO; +import com.thing.configuration.section.dto.SectionGroupInfoDTO; +import com.thing.configuration.section.entity.IotSectionDetailEntity; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +public interface IotSectionDetailService extends IBaseService { + + Result saveIotSectionDetailDTO(IotSectionDetailDTO dto); + + Result updateIotSectionDetailDTO(IotSectionDetailDTO dto); + + PageData pageIotSectionDetailDTO(Map params); + + Result getIotSectionDetailDTO(Long id); + + Result> listIotSectionDetailDTO(Map params); + + Result> getAllGroupName(); + + List importJson(MultipartFile file, HttpServletRequest request); + + List queryListByName(String[] names); +} \ No newline at end of file diff --git a/modules/configuration/src/main/java/com/thing/configuration/section/service/impl/IotSectionDetailServiceImpl.java b/modules/configuration/src/main/java/com/thing/configuration/section/service/impl/IotSectionDetailServiceImpl.java new file mode 100644 index 0000000..3e260d9 --- /dev/null +++ b/modules/configuration/src/main/java/com/thing/configuration/section/service/impl/IotSectionDetailServiceImpl.java @@ -0,0 +1,298 @@ +package com.thing.configuration.section.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.configuration.section.controller.IotSectionDetailController; +import com.thing.configuration.section.dto.GroupIofoDTO; +import com.thing.configuration.section.dto.IotSectionDetailDTO; +import com.thing.configuration.section.dto.SectionGroupInfoDTO; +import com.thing.configuration.section.entity.IotSectionDetailEntity; +import com.thing.configuration.section.mapper.IotSectionDetailMapper; +import com.thing.configuration.section.service.IotSectionDetailService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.group.dto.IotGroupInfoDTO; +import com.thing.thing.group.mapper.IotGroupInfoMapper; +import com.thing.thing.group.service.IotGroupInfoService; +import com.thing.util.TenantSubsetUtil; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 部件管理 + * + * @author xc + * @since 3.0 2023-05-08 + */ +@Service +public class IotSectionDetailServiceImpl extends BaseServiceImpl implements IotSectionDetailService { + + @Autowired + IotGroupInfoService iotGroupInfoService; + + @Autowired + IotGroupInfoMapper iotGroupInfoDao; + + @Autowired + TenantSubsetUtil tenantSubsetUtil; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + List group_ids = null; + String strs = (String)params.get("group_ids"); + if(StringUtils.isNotBlank(strs)&&!"null".equals(params.get("strs"))){ + group_ids = Arrays.stream(strs.split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()); + wrapper.in("group_id", group_ids,CollectionUtils.isNotEmpty(group_ids)); + } + String isLocal = (String) params.get("isLocal"); + if(StringUtils.isNotBlank(isLocal)){ + if("false".equals(isLocal)){ + wrapper.eq("is_local", false); + } + } + String ids = (String) params.get("ids"); + if(StringUtils.isNotBlank(ids)&&!"null".equals(params.get("ids"))){ + List idList = Arrays.stream(ids.split(",")) + .map(Long::parseLong) + .collect(Collectors.toList()); + wrapper.in("id", idList,CollectionUtils.isNotEmpty(idList)); + } + + String sectionType = (String) params.get("sectionType"); + wrapper.like("section_type", sectionType,StringUtils.isNotBlank(sectionType)); + + String name = (String) params.get("name"); + wrapper.like("name", name,StringUtils.isNotBlank(name)); + //拼接权限过滤 + this.appendWrapper(wrapper); + return wrapper; + } + + @Override + public Result saveIotSectionDetailDTO(IotSectionDetailDTO dto) { + + if(MapUtils.isNotEmpty(dto.getFiles())){ + dto.setJsPlugin((String) dto.getFiles().get("jsPlugin")); + dto.setCss((String) dto.getFiles().get("css")); + dto.setJson((String) dto.getFiles().get("json")); + dto.setJavascript((String) dto.getFiles().get("javascript")); + dto.setFakeData((String)dto.getFiles().get("fakeData")); + } + if(ObjectUtil.isNull(dto.getIsDefault())){ + dto.setIsDefault(false); + } + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( "group_id", dto.getGroupId(),ObjectUtil.isNotEmpty(dto.getGroupId())); + wrapper.eq( "name", dto.getName(),ObjectUtil.isNotEmpty(dto.getName())); + wrapper.eq("section_type", dto.getSectionType(),ObjectUtil.isNotEmpty(dto.getSectionType())); + IotSectionDetailEntity entityInfo = this.mapper.selectOneByQuery(wrapper); + if(ObjectUtil.isNotNull(entityInfo)){ + dto.setId(entityInfo.getId()); + updateById(ConvertUtils.sourceToTarget(dto,IotSectionDetailEntity.class)); + }else { + IotSectionDetailEntity entity =ConvertUtils.convertWithTypeAdapt(dto,IotSectionDetailEntity.class); + entity.setId(new SnowFlakeIDKeyGenerator().nextId()); + save(entity); + } + return new Result().ok("操作成功!"); + } + + @Override + public Result updateIotSectionDetailDTO(IotSectionDetailDTO dto) { + if(MapUtils.isNotEmpty(dto.getFiles())){ + dto.setJsPlugin((String) dto.getFiles().get("jsPlugin")); + dto.setCss((String) dto.getFiles().get("css")); + dto.setJson((String) dto.getFiles().get("json")); + dto.setJavascript((String) dto.getFiles().get("javascript")); + dto.setFakeData((String)dto.getFiles().get("fakeData")); + + } + if(ObjectUtil.isNull(dto.getIsDefault())){ + dto.setIsDefault(false); + } + IotSectionDetailEntity entity =ConvertUtils.convertWithTypeAdapt(dto,IotSectionDetailEntity.class); + this.updateById(entity); + return new Result().ok("修改成功!"); + } + + @Override + public PageData pageIotSectionDetailDTO(Map params) { + PageData data = getPageData(params,IotSectionDetailDTO.class); + data.getList().forEach(temp->{ + IotGroupInfoDTO groupInfoDTO = iotGroupInfoService.getByIdAs(temp.getGroupId(),IotGroupInfoDTO.class); + if(null!=groupInfoDTO){ + temp.setGroupName(groupInfoDTO.getName()); + temp.setGroupType(groupInfoDTO.getBusinessType()); + temp.setThumbnailUrl(groupInfoDTO.getThumbnailUrl()); + } + Map files = new HashMap<>(); + files.put("css",temp.getCss()); + files.put("jsPlugin",temp.getJsPlugin()); + files.put("json",temp.getJson()); + files.put("javascript",temp.getJavascript()); + files.put("fakeData",temp.getFakeData()); + + temp.setFiles(files); + checkIsOperate(temp); + }); + return data; + } + + @Override + public Result getIotSectionDetailDTO(Long id) { + IotSectionDetailDTO data = getByIdAs(id,IotSectionDetailDTO.class); + Map files = new HashMap<>(); + files.put("css",data.getCss()); + files.put("jsPlugin",data.getJsPlugin()); + files.put("json",data.getJson()); + files.put("javascript",data.getJavascript()); + files.put("fakeData",data.getFakeData()); + + data.setFiles(files); + + IotGroupInfoDTO group = iotGroupInfoService.getByIdAs(data.getGroupId(),IotGroupInfoDTO.class); + data.setGroupName(group.getName()); + data.setGroupType(group.getBusinessType()); + checkIsOperate(data); + return new Result().ok(data); + } + + private void checkIsOperate(IotSectionDetailDTO data) { + if(data.getIsDefault()){ + if(!"1001".equals(String.valueOf(UserContext.getTenantCode()))){ + data.setIsOperate("1"); + } + } + if (!String.valueOf(data.getTenantCode()).equals(String.valueOf(UserContext.getTenantCode()))){ + if(!UserContext.isAdmin()){ + data.setIsOperate("1"); + } + } + } + + @Override + public Result> listIotSectionDetailDTO(Map params) { + List resultList = new ArrayList<>(); + Long currentTenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + Long currentCompanyId = TenantContext.getCompanyId(SecurityUser.getUser()); + params.put("currentTenantCode",currentTenantCode); + params.put("currentCompanyId",currentCompanyId); + List tempList = this.mapper.getList(params); + + Map> map = tempList.stream().collect(Collectors.groupingBy(IotSectionDetailDTO::getGroupType)); + + map.keySet().forEach(temp->{ + SectionGroupInfoDTO dto = new SectionGroupInfoDTO(); + dto.setGroupName(map.get(temp).get(0).getGroupName()); + dto.setGroupType(map.get(temp).get(0).getGroupType()); + dto.setThumbnailUrl(map.get(temp).get(0).getThumbnailUrl()); + dto.setDetailDTOList(ConvertUtils.sourceToTarget(map.get(temp),IotSectionDetailDTO.class)); + resultList.add(dto); + }); + + + return new Result().ok(resultList); + } + + @Override + public Result> getAllGroupName() { + QueryColumn is_default = column("is_default"); + QueryColumn tenant_code = column("tenant_code"); + List resultList = new ArrayList<>(); + QueryWrapper wrapper = new QueryWrapper(); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq(true).or(tenant_code.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + wrapper.eq("type","section"); + List data =ConvertUtils.sourceToTarget(iotGroupInfoDao.selectListByQuery(wrapper),IotGroupInfoDTO.class); + + Map> map = data.stream().collect(Collectors.groupingBy(IotGroupInfoDTO::getName)); + + map.keySet().forEach(temp->{ + GroupIofoDTO dto = new GroupIofoDTO(); + dto.setGroupName(map.get(temp).get(0).getName()); + dto.setThumbnailUrl(map.get(temp).get(0).getThumbnailUrl()); + resultList.add(dto); + }); + return new Result().ok(resultList); + } + + @Override + public List importJson(MultipartFile file, HttpServletRequest request) { + List resultErrorList = new ArrayList<>(); + + String jsonString = JsonProcessingUtils.readJson(file); + List iotGroupList = JSON.parseObject(jsonString, new TypeReference<>() { + }); + if (CollectionUtil.isEmpty(iotGroupList)) { + throw new SysException("导入json为空,请检查json后再进行导入"); + } + for (IotSectionDetailDTO temp : iotGroupList) { + temp.setCreateDate(null); + temp.setCreator(null); + temp.setUpdater(null); + temp.setUpdateDate(null); + temp.setTenantCode(null); + temp.setCompanyId(null); + if(ErrorCode.INTERNAL_SERVER_ERROR==this.saveIotSectionDetailDTO(temp).getCode()){ + resultErrorList.add(temp); + }; + } + return resultErrorList; + } + + @Override + public List queryListByName(String[] names) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("name",Arrays.asList(names)); + List data =ConvertUtils.sourceToTarget(mapper.selectListByQuery(wrapper),IotSectionDetailDTO.class); + IotSectionDetailController.fomartFiles(data); + return data; + } + + + private void appendWrapper(QueryWrapper wrapper){ + QueryColumn is_default = column("is_default"); + QueryColumn tenant_code = column("tenant_code"); + //切换租户,租户/企业登陆 + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq(true).or(tenant_code.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + } + + +} \ No newline at end of file diff --git a/modules/configuration/src/main/resources/mapper/material/IotNewSourceMaterialMapper.xml b/modules/configuration/src/main/resources/mapper/material/IotNewSourceMaterialMapper.xml new file mode 100644 index 0000000..29d1941 --- /dev/null +++ b/modules/configuration/src/main/resources/mapper/material/IotNewSourceMaterialMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + \ No newline at end of file diff --git a/modules/configuration/src/main/resources/mapper/section/IotSectionDetailMapper.xml b/modules/configuration/src/main/resources/mapper/section/IotSectionDetailMapper.xml new file mode 100644 index 0000000..a20b381 --- /dev/null +++ b/modules/configuration/src/main/resources/mapper/section/IotSectionDetailMapper.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/dequeue/pom.xml b/modules/dequeue/pom.xml new file mode 100644 index 0000000..9ab45c0 --- /dev/null +++ b/modules/dequeue/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + com.thing.modules + dequeue + jar + ThingBI Server Modules dequeue + + UTF-8 + + + + com.thing.common + transport + + + com.thing.modules + thing + + + \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/AbstractConsumerService.java b/modules/dequeue/src/main/java/com/thing/queue/AbstractConsumerService.java new file mode 100644 index 0000000..e02c2d3 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/AbstractConsumerService.java @@ -0,0 +1,104 @@ +package com.thing.queue; + +import cn.hutool.core.collection.CollectionUtil; + +import com.thing.common.core.event.*; +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.common.util.thread.ThingThreadFactory; +import com.thing.queue.message.ServiceType; + +import lombok.extern.slf4j.Slf4j; + +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.BinaryOperator; +import java.util.function.Function; +import java.util.stream.Collectors; + +import javax.annotation.PreDestroy; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Slf4j +public abstract class AbstractConsumerService { + protected volatile ScheduledExecutorService schedulerExecutor; + protected volatile ExecutorService consumersExecutor; + protected volatile boolean stopped = false; + + + protected final ApplicationEventPublisher applicationEventPublisher; + + public AbstractConsumerService(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + public void init(String mainConsumerThreadName) { + this.schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingThreadFactory.forName(mainConsumerThreadName + "-log")); + this.consumersExecutor = Executors.newCachedThreadPool(QueueThreadFactory.forName(mainConsumerThreadName)); + } + + @EventListener(ApplicationReadyEvent.class) + public void onApplicationEvent(ApplicationReadyEvent event) { + launchMainConsumers(); + } + + protected abstract ServiceType getServiceType(); + + /** + * 处理队列消息入口 + */ + protected abstract void launchMainConsumers(); + + /** + * 停止 + */ + protected abstract void stopMainConsumers(); + + /** + * 数据处理 + * @param list 数据集合 + */ + protected void process(List list) { + if(CollectionUtil.isEmpty(list)) { + return; + } + // 物计算 + //applicationEventPublisher.publishEvent(new QueueCalculationEvent(this, list)); + + applicationEventPublisher.publishEvent(new QueueCacheEvent(this, list)); + + Map lastValMap = list.parallelStream() + .collect(Collectors.toMap(item -> item.getThingCode() + item.getAttrKey(), Function.identity(), BinaryOperator.maxBy(Comparator.comparing(QueueMsgDTO::getTs)))); + List lastList = new ArrayList<>(lastValMap.values()); + + if(CollectionUtil.isNotEmpty(lastList)){ + // 告警 + applicationEventPublisher.publishEvent(new QueueAlarmEvent(this, lastList)); + + // socket推送 + applicationEventPublisher.publishEvent(new QueueSocketEvent(this, lastList)); + + // 同步物模型物实体 + applicationEventPublisher.publishEvent(new QueueDeviceEvent(this, lastList)); + } + } + + @PreDestroy + public void destroy() { + stopped = true; + stopMainConsumers(); + if (consumersExecutor != null) { + consumersExecutor.shutdownNow(); + } + } +} diff --git a/modules/dequeue/src/main/java/com/thing/queue/DefaultCoreConsumerService.java b/modules/dequeue/src/main/java/com/thing/queue/DefaultCoreConsumerService.java new file mode 100644 index 0000000..8f11eec --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/DefaultCoreConsumerService.java @@ -0,0 +1,392 @@ +package com.thing.queue; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Maps; +import com.google.gson.JsonParser; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.ScriptMsg; +import com.thing.api.ScriptEngine; +import com.thing.common.core.event.ExtensionSocketEvent; +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.extend.entity.TransportExtendMsgEntity; +import com.thing.extend.service.TransportExtendMsgService; +import com.thing.gen.queue.QueueProto.ControlMsg; +import com.thing.gen.queue.QueueProto.ExtensionMsg; +import com.thing.gen.queue.QueueProto.TelemetryMsg; +import com.thing.gen.queue.QueueProto.TransportMsg; +import com.thing.modules.cache.bean.ScriptCache; +import com.thing.modules.cache.service.ScriptCacheService; +import com.thing.queue.cluster.ClusterService; +import com.thing.queue.message.ProtoQueueMsg; +import com.thing.queue.message.ServiceType; +import com.thing.queue.modules.dto.QueueLogDTO; +import com.thing.queue.modules.entity.QueueMsgLogEntity; +import com.thing.queue.modules.service.QueueLogService; +import com.thing.queue.modules.service.QueueMsgLogService; +import com.thing.queue.partition.PartitionChangeEvent; +import com.thing.queue.partition.PartitionResetEvent; +import com.thing.queue.provider.CoreQueueFactory; +import com.thing.transport.api.adaptor.JsonConverter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-08-24 + **/ +@Slf4j +@Service +public class DefaultCoreConsumerService extends AbstractConsumerService { + + @Value("${queue.core.poll-interval:3000}") + private long pollDuration; + @Value("${queue.core.log-interval:300}") + private long logDuration; + @Value("${queue.core.debug:false}") + private boolean debug; + + private final QueueConsumer> mainConsumer; + private final QueueConfiguration queueConfiguration = new QueueConfiguration(debug); + private final QueueLogService queueLogService; + private final QueueMsgLogService queueMsgLogService; + private final ClusterService clusterService; + private final ScriptCacheService scriptCacheService; + private final ScriptCreateService scriptCreateService; + private final TransportExtendMsgService msgService; + protected volatile ExecutorService consumersExecutor; + protected volatile ExecutorService notificationsConsumerExecutor; + public DefaultCoreConsumerService(CoreQueueFactory tbCoreQueueFactory, ApplicationEventPublisher applicationEventPublisher, + QueueLogService queueLogService, QueueMsgLogService queueMsgLogService, + ClusterService clusterService, ScriptCacheService scriptCacheService, ScriptCreateService scriptCreateService, TransportExtendMsgService msgService) { + super(applicationEventPublisher); + this.queueLogService = queueLogService; + this.queueMsgLogService = queueMsgLogService; + this.clusterService = clusterService; + this.scriptCacheService = scriptCacheService; + this.scriptCreateService = scriptCreateService; + this.msgService = msgService; + this.mainConsumer = tbCoreQueueFactory.createCoreMsgConsumer(); + this.mainConsumer.subscribe(); + } + + @PostConstruct + public void init() { + super.init("queue-core-consumer"); + setScheduledFuture(logDuration); + this.consumersExecutor = Executors.newCachedThreadPool(); + this.notificationsConsumerExecutor = Executors.newSingleThreadExecutor(); + } + + @EventListener(PartitionChangeEvent.class) + public void onPartitionChangeEvent(PartitionChangeEvent partitionChangeEvent) { + mainConsumer.subscribe(partitionChangeEvent.getPartition()); + } + + @EventListener(PartitionResetEvent.class) + public void onPartitionResetEvent(PartitionResetEvent partitionChangeEvent) { + mainConsumer.subscribe(partitionChangeEvent.getPartitions()); + } + + @Override + protected void launchMainConsumers() { + notificationsConsumerExecutor.submit(() -> { + while (!stopped) { + try { + List> msgList = mainConsumer.poll(pollDuration); + if (msgList.isEmpty()) { + continue; + } + //log.info("----订阅入口,当前时间:{},数据:{}",LocalDateTime.now().toString(),msgList.size()); + //遥测数据 + consumersExecutor.submit(() -> { + Stream telemetryMsgStream = msgList.stream() + .filter(msg -> msg.getValue().hasTelemetryMsg()) + .map(ProtoQueueMsg::getValue); + forwardToTelemetryMsg(telemetryMsgStream); + }); + consumersExecutor.submit(() -> { + //数据接入的数据需要解析一下,重新丢回队列 telemetryMsg 类型 + Stream extensionMsgStream = msgList.stream() + .filter(msg -> msg.getValue().hasTransportExtensionMsg()) + .map(ProtoQueueMsg::getValue); + forwardToExtensionMsg(extensionMsgStream); + }); + //数据下发日志 + consumersExecutor.submit(() -> { + Stream controlMsgStream = msgList.stream() + .filter(msg -> msg.getValue().hasControlMsg()) + .map(ProtoQueueMsg::getValue); + forwardToControlMsg(controlMsgStream); + }); + //日志 + consumersExecutor.submit(() -> { + logSave(msgList); + }); + mainConsumer.commit(); + } catch (Exception e) { + if (!stopped) { + log.warn("Failed to obtain messages from queue.", e); + try { + TimeUnit.MILLISECONDS.sleep(pollDuration); + } catch (InterruptedException e2) { + log.trace("Failed to wait until the service has capacity to handle new requests", e2); + } + } + } + } + log.info("Queue Core Consumer stopped."); + }); + } + + /** + * 遥测值 + * + * @param telemetryMsgStream 遥测 + */ + private void forwardToTelemetryMsg(Stream telemetryMsgStream) { + //同一ts下可能存在多条数据,需要分组取最新值 + Map dtoMap = telemetryMsgStream + .parallel().flatMap(msg -> { + TelemetryMsg telemetryMsg = msg.getTelemetryMsg(); + return telemetryMsg.getTelemetryListList().parallelStream() + .flatMap(telemetry -> telemetry.getTsKvListList().parallelStream() + .map(tsKv -> new QueueMsgDTO(telemetry.getThingCode(), tsKv.getKey(), tsKv.getTs(), tsKv.getVal(), + msg.getOrigin(), msg.getTenantCode(), msg.getCompanyId(), msg.getDeptId()))); + }).collect(Collectors.toMap(item -> item.getThingCode() + item.getAttrKey() + item.getTs(), Function.identity(), (e1, e2) -> e2)); + List collect = dtoMap.values().stream() + .sorted(Comparator.comparing(QueueMsgDTO::getThingCode).thenComparing(QueueMsgDTO::getAttrKey).thenComparingLong(QueueMsgDTO::getTs)) + .collect(Collectors.toList()); + this.queueConfiguration.addPollNum(collect.size()); + //处理 + process(collect); + + } + + /** + * 数据接入 + * + * @param extensionMsgStream 数据接入 + */ + private void forwardToExtensionMsg(Stream extensionMsgStream) { + //log.info("数据接入数据"); + List list = extensionMsgStream.flatMap(msg -> { + ExtensionMsg extensionMsg = msg.getTransportExtensionMsg(); + return extensionMsg.getRelationList().stream().map(relation -> { + TransportExtendMsgEntity entity = new TransportExtendMsgEntity(); + entity.setTs(extensionMsg.getTs()); + entity.setConfigId(extensionMsg.getConfigId()); + entity.setExtendCalculationId(relation.getRelationId()); + entity.setInputMsg(new String(extensionMsg.getPayload().toByteArray())); + entity.setDebug(BooleanUtils.isTrue(relation.getDebug())); + entity.setTenantCode(msg.getTenantCode()); + entity.setCompanyId(msg.getCompanyId()); + entity.setDeptId(msg.getDeptId()); + entity.setOrigin(msg.getOrigin()); + calculation(entity, relation.getCalculationId()); + return entity; + }); + }).collect(Collectors.toList()); + //重新丢回队列 + for (TransportExtendMsgEntity entity : list) { + if (StringUtils.isNotBlank(entity.getErrorMsg())) { + continue; + } + try { + clusterService.pushMsgToCore(UUID.randomUUID(), + JsonConverter.convertToTelemetryProto(JsonParser.parseString(entity.getOutputMsg()), entity.getOrigin(), + entity.getTenantCode(), entity.getCompanyId(), entity.getDeptId()), + QueueCallback.EMPTY); + } catch (Exception e) { + log.error("转换队列标准格式异常:{}", e.getMessage()); + entity.setErrorMsg("转换队列标准格式异常: " + e.getMessage()); + } + } + saveLogAndSendSocket(list); + } + + private void saveLogAndSendSocket(List list) { + //异常、调试数据入库 + List msgList = list.stream().filter(item -> item.getDebug() || StringUtils.isNotBlank(item.getErrorMsg())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(msgList)) { + msgService.saveBatch(msgList); + } + if (CollectionUtil.isNotEmpty(list)) { + //推送前端 + List collect = list.parallelStream().map(msg -> { + JSONObject result = new JSONObject(); + result.put("id", String.valueOf(msg.getExtendCalculationId())); + result.put("inputMsg", msg.getInputMsg()); + result.put("outputMsg", msg.getOutputMsg()); + result.put("errorMsg", msg.getErrorMsg()); + result.put("lastDate", LocalDateTimeUtil.of(msg.getTs()).format(DatePattern.NORM_DATETIME_MS_FORMATTER)); + return result; + }).collect(Collectors.toList()); + applicationEventPublisher.publishEvent(new ExtensionSocketEvent(this, collect)); + } + } + + /** + * 数据下发日志 + * + * @param controlMsgStream 数据下发日志 + */ + private void forwardToControlMsg(Stream controlMsgStream) { + List list = controlMsgStream.map(msg -> { + ControlMsg controlMsg = msg.getControlMsg(); + TransportExtendMsgEntity entity = new TransportExtendMsgEntity(); + entity.setTs(controlMsg.getTs()); + entity.setConfigId(controlMsg.getConfigId()); + entity.setExtendCalculationId(controlMsg.getRelationId()); + entity.setDebug(controlMsg.getDebug()); + entity.setInputMsg(new String(controlMsg.getInMsg().toByteArray())); + entity.setOutputMsg(new String(controlMsg.getOutMsg().toByteArray())); + if (!controlMsg.getErrorMsg().isEmpty()) { + entity.setErrorMsg(new String(controlMsg.getErrorMsg().toByteArray())); + } + entity.setTenantCode(msg.getTenantCode()); + entity.setCompanyId(msg.getCompanyId()); + entity.setDeptId(msg.getDeptId()); + entity.setOrigin(msg.getOrigin()); + return entity; + }).collect(Collectors.toList()); + saveLogAndSendSocket(list); + } + + /** + * 数据解析 + * + * @param entity 消息 + * @param calculationId 计算缓存主键 + */ + protected void calculation(TransportExtendMsgEntity entity, Long calculationId) { + String inputMsg = entity.getInputMsg(); + try { + ScriptCache scriptCache = Objects.isNull(calculationId) ? null : scriptCacheService.get(String.valueOf(calculationId)); + if (Objects.isNull(scriptCache)) { + entity.setOutputMsg(inputMsg); + } + ScriptLanguage scriptLanguage = ScriptLanguage.get(scriptCache.getScriptType()); + ScriptEngine scriptEngine = scriptCreateService.createScriptEngine(scriptCache.getScriptEId(), scriptLanguage, scriptCache.getScriptBody(), scriptCache.getDebug()); + entity.setOutputMsg(scriptEngine.executeToStringAsync(new ScriptMsg(inputMsg, Maps.newHashMap())).get()); + } catch (Exception e) { + entity.setErrorMsg(e.getMessage()); + log.error("数据转换异常:{}", e.getMessage()); + } + } + + + /** + * 修改时间间隔 + * + * @param logDuration 时间间隔 + */ + public void setScheduledFuture(long logDuration) { + if (this.queueConfiguration.hasScheduledFuture()) { + this.queueConfiguration.getScheduledFuture().cancel(false); + } + this.queueConfiguration.setLogDuration(logDuration); + if (logDuration <= 0L) { + log.debug("queue log closed."); + return; + } + ScheduledFuture executorFuture = schedulerExecutor.scheduleAtFixedRate(() -> { + queueLogService.saveDto(new QueueLogDTO(new Date(), String.valueOf(mainConsumer.count()), String.valueOf(clusterService.getSendNum()), + queueConfiguration.getPollNumStr(), String.valueOf(clusterService.getSendOrigin()))); + this.queueConfiguration.reset(); + this.clusterService.reset(); + }, new Random().nextInt((int) logDuration), logDuration, TimeUnit.SECONDS); + this.queueConfiguration.setScheduledFuture(executorFuture); + } + + /** + * 启用/禁用调试 + * + * @param debug 启用/禁用 + */ + public void debug(Boolean debug) { + this.queueConfiguration.setDebug(debug); + } + + /** + * 获取相关参数 + */ + public JSONObject getParam() { + JSONObject param = new JSONObject(); + param.put("debug", this.queueConfiguration.getDebug()); + param.put("time", this.queueConfiguration.getLogDuration()); + return param; + } + + /** + * 日志 + * + * @param msgList 入口消息 + */ + private void logSave(List> msgList) { + //开启调试 + if (this.queueConfiguration.getDebug()) { + List collect = msgList.parallelStream() + .map(msg -> { + TransportMsg transportMsg = msg.getValue(); + QueueMsgLogEntity entity = new QueueMsgLogEntity(); + entity.setTime(System.currentTimeMillis()); + entity.setChannelName(transportMsg.getOrigin()); + try { + entity.setMsg(JsonFormat.printer().print(transportMsg.toBuilder())); + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } + return entity; + }).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(collect)) { + queueMsgLogService.saveBatch(collect); + } + } + } + + @Override + protected void stopMainConsumers() { + if (mainConsumer != null) { + mainConsumer.unsubscribe(); + } + } + + @Override + protected ServiceType getServiceType() { + return ServiceType.QUEUE_CORE; + } + + @Override + @PreDestroy + public void destroy() { + super.destroy(); + if (consumersExecutor != null) { + consumersExecutor.shutdownNow(); + } + if (notificationsConsumerExecutor != null) { + notificationsConsumerExecutor.shutdownNow(); + } + } + +} diff --git a/modules/dequeue/src/main/java/com/thing/queue/QueueConfiguration.java b/modules/dequeue/src/main/java/com/thing/queue/QueueConfiguration.java new file mode 100644 index 0000000..b5c6d5b --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/QueueConfiguration.java @@ -0,0 +1,76 @@ +package com.thing.queue; + +import lombok.Data; + +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 队列属性配置 + * + * @author zhenghh. 2022-11-30 + **/ +@Data +public class QueueConfiguration { + + /** + * 是否开启调试 + */ + private volatile Boolean debug; + /** + * 队列消息数 + */ + private AtomicLong queueNum; + /** + * 出口消息量 + */ + private AtomicLong pollNum; + /** + * 日志保存时间间隔 + */ + private Long logDuration; + /** + * log记录定时器 + */ + private ScheduledFuture scheduledFuture; + + public QueueConfiguration(Boolean debug) { + this.debug = debug; + this.queueNum = new AtomicLong(0L); + this.pollNum = new AtomicLong(0L); + this.scheduledFuture = null; + } + + public void reset() { + this.queueNum.set(0L); + this.pollNum.set(0L); + } + + public void addQueueNum(Long queueNum) { + this.queueNum.addAndGet(queueNum); + } + + public void addPollNum(Integer pollNum) { + this.pollNum.addAndGet(pollNum); + } + + public String getQueueNumStr() { + return String.valueOf(this.queueNum.get()); + } + + public String getPollNumStr() { + return String.valueOf(this.pollNum.get()); + } + + public Long getLogDuration() { + return logDuration; + } + + public void setLogDuration(Long logDuration) { + this.logDuration = logDuration; + } + + public boolean hasScheduledFuture() { + return scheduledFuture != null; + } +} diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueLogController.java b/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueLogController.java new file mode 100644 index 0000000..fcdedbb --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueLogController.java @@ -0,0 +1,113 @@ +package com.thing.queue.modules.controller; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.queue.DefaultCoreConsumerService; +import com.thing.queue.modules.dto.QueueLogDTO; +import com.thing.queue.modules.excel.QueueLogExcel; +import com.thing.queue.modules.service.QueueLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@RestController +@RequestMapping("queue/log") +@Tag(name = "队列消息数量日志") +public class QueueLogController { + @Autowired + private QueueLogService queueLogService; + @Autowired + private DefaultCoreConsumerService defaultCoreConsumerService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = queueLogService.page(params); + + return new Result>().ok(page); + } + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + queueLogService.removeById(ids); + + return new Result(); + } + + @GetMapping("clear") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear() { + + queueLogService.clear(); + + return new Result(); + } + + @GetMapping("debug/{debug}") + @Operation(summary="调试模式") + public Result debug(@PathVariable Boolean debug) { + defaultCoreConsumerService.debug(debug); + return new Result(); + } + + @GetMapping("param") + @Operation(summary="获取相关参数") + public Result getParam() { + JSONObject data = defaultCoreConsumerService.getParam(); + return new Result().ok(data); + } + + @GetMapping("interval/{time}") + @Operation(summary="修改时间(s)") + public Result interval(@PathVariable Long time) { + defaultCoreConsumerService.setScheduledFuture(time); + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = queueLogService.list(params); + List collect = list.stream() + .map(item -> new QueueLogExcel(DateUtil.format(item.getTime(), DatePattern.NORM_DATETIME_PATTERN), item.getChannelNum(), item.getEnterNum(), item.getPollNum(), item.getQueueNum())) + .collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "队列日志", null, QueueLogExcel.class, "队列日志.xls", response); + } +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueMsgLogController.java b/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueMsgLogController.java new file mode 100644 index 0000000..3ac3ff6 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/controller/QueueMsgLogController.java @@ -0,0 +1,97 @@ +package com.thing.queue.modules.controller; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.queue.modules.dto.QueueMsgLogDTO; +import com.thing.queue.modules.excel.QueueMsgLogExcel; +import com.thing.queue.modules.service.QueueMsgLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 队列消息日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@RestController +@RequestMapping("queue/msg/log") +@Tag(name = "队列消息日志") +public class QueueMsgLogController { + @Autowired + private QueueMsgLogService queueMsgLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "channelName", description = "通道"), + @Parameter(name = "msg", description = "消息"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = queueMsgLogService.page(params); + + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + queueMsgLogService.removeById(ids); + + return new Result(); + } + + @GetMapping("clear") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear() { + + queueMsgLogService.clear(); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "channelName", description = "通道"), + @Parameter(name = "msg", description = "消息"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = queueMsgLogService.list(params); + List collect = list.stream() + .map(item -> new QueueMsgLogExcel(LocalDateTimeUtil.of(item.getTime()).format(DatePattern.NORM_DATETIME_FORMATTER), item.getChannelName(), item.getMsg())) + .collect(Collectors.toList()); + ExcelUtils.exportExcel(collect,"队列消息日志", null, QueueMsgLogExcel.class, "队列消息日志.xls", response); + } +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueLogDTO.java b/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueLogDTO.java new file mode 100644 index 0000000..d1c480d --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueLogDTO.java @@ -0,0 +1,45 @@ +package com.thing.queue.modules.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "队列消息数量日志") +public class QueueLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "时间") + private Date time; + @Schema(description = "队列消息数") + private String queueNum; + @Schema(description = "进口消息量") + private String enterNum; + @Schema(description = "出口消息量") + private String pollNum; + @Schema(description = "通道数") + private String channelNum; + + public QueueLogDTO(Date time, String queueNum, String enterNum, String pollNum, String channelNum) { + this.time = time; + this.queueNum = queueNum; + this.enterNum = enterNum; + this.pollNum = pollNum; + this.channelNum = channelNum; + } +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueMsgLogDTO.java b/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueMsgLogDTO.java new file mode 100644 index 0000000..ff1384c --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/dto/QueueMsgLogDTO.java @@ -0,0 +1,29 @@ +package com.thing.queue.modules.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; + +/** +* 队列消息日志 +* +* @author zhh zhh +* @since 1.0 2022-11-30 +*/ +@Data +@Schema( name= "队列消息日志") +public class QueueMsgLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "所属通道") + private String channelName; + @Schema(description = "消息体") + private String msg; + @Schema(description = "时间") + private Long time; + + +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueLogEntity.java b/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueLogEntity.java new file mode 100644 index 0000000..15da26d --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueLogEntity.java @@ -0,0 +1,46 @@ +package com.thing.queue.modules.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("queue_log") +public class QueueLogEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 时间 + */ + private Date time; + /** + * 队列消息数 + */ + private String queueNum; + /** + * 进口消息量 + */ + private String enterNum; + /** + * 出口消息量 + */ + private String pollNum; + /** + * 通道数 + */ + private String channelNum; + +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueMsgLogEntity.java b/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueMsgLogEntity.java new file mode 100644 index 0000000..eb958c2 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/entity/QueueMsgLogEntity.java @@ -0,0 +1,35 @@ +package com.thing.queue.modules.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 队列消息日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("queue_msg_log") +public class QueueMsgLogEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 所属通道 + */ + private String channelName; + /** + * 消息体 + */ + private String msg; + /** + * 时间 + */ + private Long time; + +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueLogExcel.java b/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueLogExcel.java new file mode 100644 index 0000000..89c7480 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueLogExcel.java @@ -0,0 +1,28 @@ +package com.thing.queue.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class QueueLogExcel { + @Excel(name = "时间") + private String time; + @Excel(name = "通道数量") + private String channelNum; + @Excel(name = "进口消息量量") + private String enterNum; + @Excel(name = "出口消息量量") + private String pollNum; + @Excel(name = "队列消息数量") + private String queueNum; +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueMsgLogExcel.java b/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueMsgLogExcel.java new file mode 100644 index 0000000..1e48161 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/excel/QueueMsgLogExcel.java @@ -0,0 +1,29 @@ +package com.thing.queue.modules.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** +* 队列消息日志 +* +* @author zhh zhh +* @since 1.0 2022-11-30 +*/ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class QueueMsgLogExcel implements Serializable { + + private static final long serialVersionUID = -6163271674923604703L; + @Excel(name = "接收时间") + private String time; + @Excel(name = "所属通道") + private String channelName; + @Excel(name = "消息体") + private String msg; + +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueLogMapper.java b/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueLogMapper.java new file mode 100644 index 0000000..cc880a1 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueLogMapper.java @@ -0,0 +1,23 @@ +package com.thing.queue.modules.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.queue.modules.entity.QueueLogEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; + +/** +* 队列消息数量日志 +* +* @author zhh zhh +* @since 1.0 2022-11-30 +*/ +@Mapper +public interface QueueLogMapper extends PowerBaseMapper { + + /** + * 清空表 + */ + @Update("truncate table queue_log") + void clear(); +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueMsgLogMapper.java b/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueMsgLogMapper.java new file mode 100644 index 0000000..8fffee9 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/mapper/QueueMsgLogMapper.java @@ -0,0 +1,23 @@ +package com.thing.queue.modules.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.queue.modules.entity.QueueMsgLogEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; + +/** +* 队列消息日志 +* +* @author zhh zhh +* @since 1.0 2022-11-30 +*/ +@Mapper +public interface QueueMsgLogMapper extends PowerBaseMapper { + + /** + * 清空表 + */ + @Update("truncate table queue_msg_log") + void clear(); +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueLogService.java b/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueLogService.java new file mode 100644 index 0000000..0505758 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueLogService.java @@ -0,0 +1,29 @@ +package com.thing.queue.modules.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.queue.modules.dto.QueueLogDTO; +import com.thing.queue.modules.entity.QueueLogEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +public interface QueueLogService extends IBaseService { + + /** + * 清空表 + */ + void clear(); + + List list(Map params); + + PageData page(Map params); +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueMsgLogService.java b/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueMsgLogService.java new file mode 100644 index 0000000..981128c --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/service/QueueMsgLogService.java @@ -0,0 +1,28 @@ +package com.thing.queue.modules.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.queue.modules.dto.QueueMsgLogDTO; +import com.thing.queue.modules.entity.QueueMsgLogEntity; + +import java.util.List; +import java.util.Map; + +/** + * 队列消息日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +public interface QueueMsgLogService extends IBaseService { + + /** + * 清空表 + */ + void clear(); + + List list(Map params); + + PageData page(Map params); +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueLogServiceImpl.java b/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueLogServiceImpl.java new file mode 100644 index 0000000..709e5d2 --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueLogServiceImpl.java @@ -0,0 +1,58 @@ +package com.thing.queue.modules.service.impl; + + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.queue.modules.mapper.QueueLogMapper; +import com.thing.queue.modules.dto.QueueLogDTO; +import com.thing.queue.modules.entity.QueueLogEntity; +import com.thing.queue.modules.service.QueueLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 队列消息数量日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Service +public class QueueLogServiceImpl extends BaseServiceImpl implements QueueLogService { + @Override + public QueryWrapper getWrapper(Map params) { + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.ge(QueueLogEntity::getTime, startTime,StringUtils.isNotBlank(startTime)) + .le(QueueLogEntity::getTime, endTime,StringUtils.isNotBlank(endTime)) + .orderBy(QueueLogEntity::getTime,Boolean.FALSE); + return wrapper; + } + + /** + * 清空表 + */ + @Override + public void clear() { + this.clear(); + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params), QueueLogDTO.class); + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, QueueLogDTO.class); + } +} \ No newline at end of file diff --git a/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueMsgLogServiceImpl.java b/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueMsgLogServiceImpl.java new file mode 100644 index 0000000..fe30f9b --- /dev/null +++ b/modules/dequeue/src/main/java/com/thing/queue/modules/service/impl/QueueMsgLogServiceImpl.java @@ -0,0 +1,60 @@ +package com.thing.queue.modules.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.queue.modules.mapper.QueueMsgLogMapper; +import com.thing.queue.modules.dto.QueueMsgLogDTO; +import com.thing.queue.modules.entity.QueueMsgLogEntity; +import com.thing.queue.modules.service.QueueMsgLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 队列消息日志 + * + * @author zhh zhh + * @since 1.0 2022-11-30 + */ +@Service +public class QueueMsgLogServiceImpl extends BaseServiceImpl implements QueueMsgLogService { + @Override + public QueryWrapper getWrapper(Map params) { + String channelName = (String) params.get("channelName"); + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + String msg = (String) params.get("msg"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( QueueMsgLogEntity::getChannelName, channelName,StringUtils.isNotBlank(channelName)) + .like(QueueMsgLogEntity::getMsg, msg,StringUtils.isNotBlank(msg)) + .ge(QueueMsgLogEntity::getTime, StringUtils.isNotBlank(startTime) ? Long.parseLong(startTime) : -1L) + .le(QueueMsgLogEntity::getTime, StringUtils.isNotBlank(endTime) ? Long.parseLong(endTime) : -1L) + .orderBy(QueueMsgLogEntity::getTime,Boolean.FALSE); + return wrapper; + } + /** + * 清空表 + */ + @Override + public void clear() { + this.clear(); + } + + @Override + public List list(Map params) { + return mapper.selectListByQueryAs(getWrapper(params), QueueMsgLogDTO.class); + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(page, QueueMsgLogDTO.class); } + +} \ No newline at end of file diff --git a/modules/equipment/pom.xml b/modules/equipment/pom.xml new file mode 100644 index 0000000..40724c1 --- /dev/null +++ b/modules/equipment/pom.xml @@ -0,0 +1,28 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + com.thing.modules + equipment + jar + ThingBI Server Modules equipment + + UTF-8 + + + + + + com.thing.modules + quartz + + + + + diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/controller/CheckResultController.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/controller/CheckResultController.java new file mode 100644 index 0000000..b4baf90 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/controller/CheckResultController.java @@ -0,0 +1,118 @@ +package com.thing.eq.checkresult.controller; + + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.entity.CheckResultEntity; +import com.thing.eq.checkresult.excel.CheckResultExcel; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.publicorg.benchmarkingproject.dto.PublicBenchmarkingProjectDTO; +import com.thing.publicorg.benchmarkingproject.excel.PublicBenchmarkingProjectExcel; +import com.thing.sys.tenant.entity.SysTenantEntity; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** +* 点巡检计划检查结果 +* +* @author zy sina@xx.com +* @since 3.0 2021-10-14 +*/ +@RestController +@RequestMapping("checkresult/checkresult") +@Tag(name = "设备-点巡检计划检查结果") +public class CheckResultController { + @Autowired + private CheckResultService checkResultService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("checkresult:checkresult:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = checkResultService.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("checkresult:checkresult:info") + public Result get(@PathVariable("id") Long id){ + CheckResultDTO byId = checkResultService.getByIdAs(id, CheckResultDTO.class); + return new Result().ok(byId); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("checkresult:checkresult:save") + public Result save(@RequestBody CheckResultDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + checkResultService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("checkresult:checkresult:update") + public Result update(@RequestBody CheckResultDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + checkResultService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("checkresult:checkresult:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + checkResultService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("checkresult:checkresult:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = checkResultService.List(params); + List list1 = ConvertUtils.sourceToTarget(list, CheckResultExcel.class); + ExcelUtils.exportExcel(list1,null, "点巡检计划检查结果", CheckResultExcel.class,"点巡检计划检查结果.xls",response); + + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/dto/CheckResultDTO.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/dto/CheckResultDTO.java new file mode 100644 index 0000000..ba0f039 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/dto/CheckResultDTO.java @@ -0,0 +1,60 @@ +package com.thing.eq.checkresult.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + +/** +* 点巡检计划检查结果 +* +* @author zy sina@xx.com +* @since 3.0 2021-10-14 +*/ +@Data +@Schema( description= "点巡检计划检查结果") +public class CheckResultDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "巡检记录结果id") + @NotNull(message = "巡检记录结果id不能为空",groups = UpdateGroup.class) + @NotEmpty(message = "巡检记录结果id不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "巡检记录id") + private Long checkRecordId; + @Schema(description = "巡检结果类型:1:巡检记录") + private String type; + @Schema(description = "检查结果") + @NotNull(message = "检查结果不能为空",groups = UpdateGroup.class) + @NotEmpty(message = "检查结果不能为空",groups = UpdateGroup.class) + private String checkResult; + @Schema(description = "设备物id") + @NotNull(message = "检查结果设备物id不能为空",groups = UpdateGroup.class) + @NotEmpty(message = "检查结果设备物id不能为空",groups = UpdateGroup.class) + private Long thingId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "巡检开始时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkStart; + @Schema(description = "巡检结束时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkEnd; + @Schema(description = "巡检状态 0:未检(一项都没检查判定为未检) 1:已检 2:异常(设备检查项存在未检的或存在异常的判定为异常)") + private String checkStatus; + @Schema(description = "巡检计划id") + private Long checkPlanId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/entity/CheckResultEntity.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/entity/CheckResultEntity.java new file mode 100644 index 0000000..8985b03 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/entity/CheckResultEntity.java @@ -0,0 +1,79 @@ +package com.thing.eq.checkresult.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 点巡检计划检查结果 + * + * @author zy sina@xx.com + * @since 3.0 2021-10-14 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_check_result") +public class CheckResultEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @Id + private Long id; + /** + * 巡检记录id + */ + private Long checkRecordId; + /** + * 1:巡检记录 + */ + private String type; + /** + * 检查结果 + */ + private String checkResult; + /** + * 设备物id + */ + private Long thingId; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 巡检开始时间 + */ + private Date checkStart; + /** + * 巡检结束时间 + */ + private Date checkEnd; + /** + * 巡检状态 0:未检(一项都没检查判定为未检) 1:已检 2:异常(设备检查项存在未检的或存在异常的判定为异常) + */ + private String checkStatus; + /** + * 巡检计划id + */ + private Long checkPlanId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/excel/CheckResultExcel.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/excel/CheckResultExcel.java new file mode 100644 index 0000000..748ef96 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/excel/CheckResultExcel.java @@ -0,0 +1,31 @@ +package com.thing.eq.checkresult.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +/** + * 点巡检计划检查结果 + * + * @author zy sina@xx.com + * @since 3.0 2021-10-14 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class CheckResultExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "巡检记录id", orderNum = "2") + private Long checkRecordId; + @Excel(name = "1:巡检记录", orderNum = "3") + private String type; + @Excel(name = "检查结果", orderNum = "4") + private String checkResult; + @Excel(name = "设备物id", orderNum = "5") + private Long thingId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/mapper/CheckResultMapper.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/mapper/CheckResultMapper.java new file mode 100644 index 0000000..5c851be --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/mapper/CheckResultMapper.java @@ -0,0 +1,36 @@ +package com.thing.eq.checkresult.mapper; + + + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.entity.CheckResultEntity; +import io.lettuce.core.dynamic.annotation.Param; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 点巡检计划检查结果 +* +* @author zy sina@xx.com +* @since 3.0 2021-10-14 +*/ +@Mapper +public interface CheckResultMapper extends PowerBaseMapper { + /** + * 查巡检记录分页设备列表检查项 + */ + Page searchRecordThingByPage(Page page, @Param("params") Map params); + + /** + * 查巡检记录分页设备列表检查项 + */ + List searchRecordThing(Long checkRecordId); + + List getResultByThingIdAndCheckPlanId(@Param("thingId") Long thingId, @Param("checkPlanId") Long checkPlanId); + + List pageList(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/service/CheckResultService.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/service/CheckResultService.java new file mode 100644 index 0000000..b65c23c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/service/CheckResultService.java @@ -0,0 +1,57 @@ +package com.thing.eq.checkresult.service; + + + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.entity.CheckResultEntity; +import com.thing.eq.checkresult.mapper.CheckResultMapper; + +import java.util.List; +import java.util.Map; + +/** + * 点巡检计划检查结果 + * + * @author zy sina@xx.com + * @since 3.0 2021-10-14 + */ +public interface CheckResultService extends IBaseService { + /** + * 查巡检记录分页设备列表 + */ + Page searchRecordThingByPage(Map params); + /** + * 查巡检记录分页设备列表 + */ + List searchRecordThing(Long checkRecordId); + /** + * 修改巡检记录设备列表检查项结果 + */ + void updateRecord(CheckResultDTO checkResultDTO); + /** + * 查巡检记录巡检结果 + */ + List searchRecordCheckResult(Long checkRecordId, Long thingId); + + /** + * 查巡检记录巡检结果 + */ + List searchRecordCheckResultByRecordId(Long checkRecordId); + /** + * 删除巡检结果 + */ + void deleteCheckResultByRecordId(List recordIdList); + /** + * 根据巡检计划id和状态查巡检记录巡检结果 + */ + List searchCheckResultByPlanId(Long checkPlanId, String checkStatus); + + List getResultByThingIdAndCheckPlanId(Long thingId, Long checkPlanId); + + PageData pageList(Map params); + + List List(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/checkresult/service/impl/CheckResultServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/checkresult/service/impl/CheckResultServiceImpl.java new file mode 100644 index 0000000..5e5ba42 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/checkresult/service/impl/CheckResultServiceImpl.java @@ -0,0 +1,131 @@ +package com.thing.eq.checkresult.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.update.UpdateWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.mapper.CheckResultMapper; +import com.thing.eq.checkresult.entity.CheckResultEntity; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 点巡检计划检查结果 + * + * @author zy sina@xx.com + * @since 3.0 2021-10-14 + */ +@Service +public class CheckResultServiceImpl extends BaseServiceImpl implements CheckResultService { + + + @Autowired + private CheckResultMapper checkResultMapper; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public Page searchRecordThingByPage(Map params) { + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("page、limit不能为空"); + } + int page = Integer.parseInt(String.valueOf(params.get("page"))); + int limit = Integer.parseInt(String.valueOf(params.get("limit"))); + Page thingList = checkResultMapper.searchRecordThingByPage(new Page(page, limit), params); +// Page page1 = getPage(params); + return thingList; + } + + @Override + public List searchRecordThing(Long checkRecordId) { + List thingList = checkResultMapper.searchRecordThing(checkRecordId); + return thingList; + } + + @Override + public void updateRecord(CheckResultDTO checkResultDTO) { + + QueryWrapper wrapper = new QueryWrapper(); + if(!Objects.isNull(checkResultDTO.getId())){ + wrapper.eq("id", checkResultDTO.getId()); + }else if(!Objects.isNull(checkResultDTO.getCheckRecordId()) && !Objects.isNull(checkResultDTO.getThingId())){ + wrapper.eq("check_record_id", checkResultDTO.getCheckRecordId()); + wrapper.eq("thing_id", checkResultDTO.getThingId()); + } + + CheckResultEntity checkResultEntity = new CheckResultEntity(); + BeanUtils.copyProperties(checkResultDTO, checkResultEntity); + + this.update(checkResultEntity, wrapper); + } + + @Override + public List searchRecordCheckResult(Long checkRecordId, Long thingId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("check_record_id", checkRecordId); + if(!Objects.isNull(thingId)){ + wrapper.eq("thing_id", thingId); + } + + return mapper.selectListExt(wrapper); + } + + @Override + public List searchRecordCheckResultByRecordId(Long checkRecordId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("check_record_id", checkRecordId); + + return mapper.selectListExt(wrapper); + } + + @Override + public void deleteCheckResultByRecordId(List recordIdList) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("check_record_id", recordIdList); + mapper.deleteByQuery(wrapper); + } + + @Override + public List searchCheckResultByPlanId(Long checkPlanId,String checkStatus) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("check_plan_id", checkPlanId); + wrapper.eq("check_status", checkStatus); + return mapper.selectListExt(wrapper); + } + + @Override + public List getResultByThingIdAndCheckPlanId(Long thingId, Long checkPlanId) { + return checkResultMapper.getResultByThingIdAndCheckPlanId(thingId,checkPlanId); + } + + @Override + public PageData pageList(Map params) { + Page page = getPage(params); + List list = checkResultMapper.pageList(params); + PageData data = getPageData(list, page.getTotalRow(), CheckResultDTO.class); + return data; + } + + @Override + public List List(Map params) { + List list = checkResultMapper.pageList(params); + return list; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqBxController.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqBxController.java new file mode 100644 index 0000000..ea1bd94 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqBxController.java @@ -0,0 +1,423 @@ +package com.thing.eq.eqbxwx.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.extra.mail.MailUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqbxwx.dto.EqBxDTO; +import com.thing.eq.eqbxwx.entity.EqBxEntity; +import com.thing.eq.eqbxwx.excel.EqBxExcel; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqbxwx.service.EqWxPlanService; +import com.thing.eq.eqcheck.dto.ThingDTO; +import com.thing.eq.eqfilemanage.service.EqFileDeleteService; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.file.service.FileService; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.thing.dict.service.IotThingDictService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + + +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping(Constant.API_BASE + "/eqbx") +@Tag(name = "设备报修") +public class EqBxController { + @Autowired + private EqBxService eqBxService; + + @Autowired + private FileService fileService; + + @Autowired + private EqWxPlanService eqWxPlanService; + + @Autowired + private SysDeptService sysDeptService; + +// @Autowired +// private IotThingsServiceFeign iotThingsServiceFeign; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private SysUserService sysUserService; + @Autowired + private IotThingsService iotThingsService; + +// @Autowired +// private DictTypeServiceFeign dictTypeServiceFeign; + + @Autowired + private EqFileDeleteService eqFileDeleteService; + + @Autowired + private EqScrapService eqScrapService; + + @Autowired + private IotThingEntityService iotThingEntityService; + + @Autowired + private IotThingDictService iotThingDictService; + + @Autowired + private SysDictTypeService sysDictTypeService; + + @Autowired + private SysDictDataService sysDictDataService; + +// @Autowired +// private IotDictTypeService iotDictTypeService; +// +// @Autowired +// private ThingsService thingsService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "faultType", description = "故障类型"), + @Parameter(name = "wxStatus", description = "维修状态"), + @Parameter(name = "bxUser", description = "报修人"), + @Parameter(name = "rangeType", description = "0:报修页面,1:故障报修页面"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + @Parameter(name = "bxNo", description = "报修单号") + }) +// @RequiresPermissions("equipment:reportMalfunction:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = eqBxService.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("{id}/{relationTypeId}") + @Operation(summary = "信息") + @Parameters({@Parameter(name = "id", description = "报修信息的id")}) +// @RequiresPermissions("eqbx:eqbx:info") + public Result get(@PathVariable("id") Long id, @PathVariable("relationTypeId") Long relationTypeId) { + EqBxDTO data = eqBxService.get(id); + + if (Objects.isNull(data)) { + throw new SysException("保修记录不存在"); + } + IotThingBaseInfoDTO iotThingBaseInfoDTO = iotThingBaseInfoService.getByIdAs(data.getEqId(),IotThingBaseInfoDTO.class); + if (Objects.isNull(iotThingBaseInfoDTO)) { + throw new SysException("保修记录未关联设备"); + } + //查询是否报废 + EqScrapEntity eqScrapEntity = eqScrapService.getByEqId(id); + if(!Objects.isNull(eqScrapEntity)){ + data.setThingStatus("报废"); + }else { + data.setThingStatus("正常"); + } + data.setEqCode(iotThingBaseInfoDTO.getEqCode()); + data.setDevicePosition(iotThingBaseInfoDTO.getEqPosition()); + data.setDeviceStandard(iotThingBaseInfoDTO.getStandard()); + if (!Objects.isNull(iotThingBaseInfoDTO.getUseDeptId())) { + data.setDeptId(iotThingBaseInfoDTO.getUseDeptId()); + SysDeptDTO sysDeptDTO = sysDeptService.get(data.getDeptId()); + data.setDeptName(sysDeptDTO.getName()); + } + + IotThingEntityDTO iotThingEntityDTO = iotThingEntityService.getByIdAs(iotThingBaseInfoDTO.getThingId(),IotThingEntityDTO.class); + // 获取设备关联的物 +// Result response = iotThingsServiceFeign.getThingById(iotThingBaseInfoDTO.getThingId()); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } +// Map responseData = (Map) response.getData(); + if (!Objects.isNull(iotThingEntityDTO)) { + data.setDeviceCode(Objects.isNull(iotThingEntityDTO.getCode()) ? "" : iotThingEntityDTO.getCode()); + data.setDeviceName(Objects.isNull(iotThingEntityDTO.getName()) ? "" : iotThingEntityDTO.getName()); + } + String deviceName = iotThingBaseInfoService.getDeviceName(relationTypeId, iotThingBaseInfoDTO.getThingId()); + data.setDeviceType(deviceName); + + Map> document = fileService.getDocument(data.getId()); + data.setImageUrls(document.get("image")); + data.setAttachmentUrls(document.get("attachment")); + + return new Result().ok(data); + } + + @GetMapping + @Operation(summary = "获取维修部门信息(获取部门信息)") + public Result> getWxTeam() { + List list = eqBxService.getWxTeam(); + return new Result>().ok(list); + } + + + @GetMapping("deptId") + @Operation(summary = "获取维修成员信息(获取部门人员)") + @Parameters({@Parameter(name = "deptId", description = "维修组的id")}) + public Result> getWxUser(@RequestParam("deptId") Long deptId) { + List list = eqBxService.getWxUser(deptId); + return new Result>().ok(list); + } + + + @GetMapping("getDevice") + @Operation(summary = "获取设备信息") +// @Parameters({@Parameter(name = "deptId", value = "维修组的id", dataType = "Long")}) + public Result> getDevice() { + List list = eqBxService.getDevice(); + return new Result>().ok(list); + } + + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:reportMalfunction:add") + public Result save(@RequestBody EqBxDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + //查询是否报废 + eqScrapService.checkThingStatus(dto.getEqId()); + + saveInfo(dto); + return new Result(); + } + + public synchronized void saveInfo(EqBxDTO dto) { + eqBxService.saveBxInfo(dto); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:reportMalfunction:update") + @Transactional(rollbackFor = Exception.class) + public Result update(@RequestBody EqBxDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + //查询是否报废 + eqScrapService.checkThingStatus(dto.getEqId()); + + Long count = eqWxPlanService.getWxCountByBxId(dto.getId()); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("当前报修已存在维修记录无法修改数据"); + } + + if (!Objects.isNull(dto.getId()) && "1".equals(dto.getInWx())) { + dto.setWxStatus("1"); + eqBxService.saveWx(dto); + } else { + dto.setWxStatus("0"); + } + + eqBxService.updateDto(dto); + // 删除弃用的本地文件包含在服务器上的文件 + Long id = dto.getId(); + List imageUrls = dto.getImageUrls(); + List attachmentUrls = dto.getAttachmentUrls(); + eqBxService.deleteFile(id,imageUrls,attachmentUrls); + + fileService.deleteDocument(dto.getId()); + fileService.uploadText(dto.getImageUrls(), dto.getAttachmentUrls(), dto.getId()); + + if (!Objects.isNull(dto.getId())) { + if ("0".equals(dto.getInWx())) { + //发送维修人邮箱 + EqDTO data = iotThingsService.getInfo(dto.getEqId()); + String eqName = null; + if (data != null && StringUtils.isNotBlank(data.getName())) { + eqName = data.getName(); + } + //维修人员发送邮件 + if (StringUtils.isNotBlank(dto.getWxUserId())) { + StringBuilder sb = new StringBuilder(); + String userIdStr = dto.getWxUserId(); + List userIds = Arrays.stream(userIdStr.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + for (int i = 0; i < userIds.size(); i++) { + SysUserEntity sysUserEntity = sysUserService.getById(userIds.get(i)); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getEmail())) { + if (i == userIds.size() - 1) { + sb.append(sysUserEntity.getEmail()); + } else { + sb.append(sysUserEntity.getEmail()); + sb.append(","); + } + } + } + if (StringUtils.isNotBlank(sb.toString())) { + MailUtil.send(sb.toString(), "有设备报修,请及时查看", "设备:" + eqName + "故障描述:" + dto.getFaultDesc(), false, null); + } + } + + } + } + + return new Result(); + } + + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:reportMalfunction:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + List oldUrlList = new ArrayList<>(); + for (Long id : ids) { + + Long count = eqWxPlanService.getWxCountByBxId(id); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("已经进入维修的报修无法删除"); + } + + // 查询原始文件 + List curBxFile = fileService.getDocumentById(id); + if (CollectionUtil.isNotEmpty(curBxFile)) { + oldUrlList.addAll(curBxFile.stream().map(item -> item.getUrl()).collect(Collectors.toList())); + } + fileService.deleteDocument(id); + } + + eqBxService.delete(ids); + + eqFileDeleteService.deleteFile(oldUrlList); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name = "faultType", value = "故障类型", paramType = "query", dataType = "String"), +// @Parameter(name = "wxStatus", value = "维修状态", paramType = "query", dataType = "String"), +// @Parameter(name = "bxUser", value = "报修人", paramType = "query", dataType = "String"), +// @Parameter(name = "rangeType", value = "0:报修页面,1:故障报修页面", paramType = "query", dataType = "int"), +// @Parameter(name = "eqIds", value = "设备id,多个用,隔开", paramType = "query", dataType = "String"), +// @Parameter(name = "bxNo", value = "报修单号", paramType = "query", dataType = "String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query", dataType = "String") +// }) +// @RequiresPermissions("equipment:reportMalfunction:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqBxService.exportData(params); + + //调整 + String dictTypeStr = "mulfunctionType"; + List dictTypeList1 = StringUtils.isNotBlank(dictTypeStr) ? Arrays.asList(dictTypeStr.split(",")) : null; + List dictTypeList = sysDictTypeService.getDictListByDictTypeList(dictTypeList1); +// Result resultResponse = dictTypeServiceFeign.getDictListByDictTypeList("mulfunctionType"); +// if (Objects.isNull(resultResponse) && resultResponse.getCode() != 0) { +// throw new RenException(!Objects.isNull(resultResponse) ? resultResponse.getMsg() : "服务异常"); +// } +// List dictTypeList = (List) resultResponse.getData(); + Map dictMap = null; + if (CollectionUtil.isNotEmpty(dictTypeList)) { + dictMap = dictTypeList.stream().collect(Collectors.toMap(item -> String.valueOf(item.getDictType()), item -> item.getDataList())); + } + + Map storeThingInfo = new HashMap<>(); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + for (EqBxDTO eqBxEntity : list) { + EqBxExcel eqBxExcel = new EqBxExcel(); + BeanUtils.copyProperties(eqBxEntity, eqBxExcel); + if (StringUtils.isNotBlank(eqBxExcel.getFaultType())) { + eqBxExcel.setFaultType(eqWxPlanService.getDictValue(dictMap, "mulfunctionType", eqBxExcel.getFaultType())); + } + // 查询设备信息 + ThingDTO thingDTO = new ThingDTO(); + if (storeThingInfo.containsKey(eqBxEntity.getEqId())) { + thingDTO = storeThingInfo.get(eqBxEntity.getEqId()); + } else { + iotThingBaseInfoService.getThingInfo(thingDTO, eqBxEntity.getEqId(), null, "0", String.valueOf(params.get("relationTypeId"))); + storeThingInfo.put(eqBxEntity.getEqId(), thingDTO); + } + + if (!Objects.isNull(thingDTO)) { + BeanUtils.copyProperties(thingDTO, eqBxExcel); + } + excels.add(eqBxExcel); + } + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqBxExcel eqBxExcel : excels ){ + if (idList.contains(eqBxExcel.getId())){ + newList.add(eqBxExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "维修记录", EqBxExcel.class,"维修记录.xls",response); +// ExcelUtils.exportExcel(response, "设备报修", newList, EqBxExcel.class); + } + + + @PostMapping("/receiveRepair") + @Operation(summary = "接收报修") + @Transactional(rollbackFor = Exception.class) + @LogOperation("导出") +// @RequiresPermissions("eqbx:eqbx:export") + public void receiveRepair(@RequestBody EqBxDTO dto) { + try { + eqBxService.receiveRepair(dto); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxPlanController.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxPlanController.java new file mode 100644 index 0000000..43ceeb3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxPlanController.java @@ -0,0 +1,463 @@ +package com.thing.eq.eqbxwx.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.*; +import com.thing.eq.eqbxwx.entity.EqBxEntity; +import com.thing.eq.eqbxwx.excel.EqWxPlanExcel; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqbxwx.service.EqWxPlanService; +import com.thing.eq.eqbxwx.service.EqWxReplacementService; +import com.thing.eq.eqcheck.dto.ThingDTO; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.file.service.FileService; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.entity.SysDictTypeEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping("eqwxplan/eqwxplan") +@Tag(name = "设备维修计划") +public class EqWxPlanController { + @Autowired + private EqWxPlanService eqWxPlanService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private EqBxService eqBxService; + + @Autowired + private EqWxReplacementService eqWxReplacementService; + + @Autowired + private SysUserService sysUserService; + +// @Autowired +// private DictTypeServiceFeign dictTypeServiceFeign; + + @Autowired + private SysDeptService sysDeptService; + + @Autowired + private SysDictTypeService sysDictTypeService; + + @Autowired + private SysDictDataService sysDictDataService; + + @Autowired + private EqScrapService eqScrapService; + + @Autowired + private FileService fileService; + +// @Autowired +// private IotDictTypeService iotDictTypeService; + + @GetMapping("page") + @Operation(summary= "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "relationTypeId", description = "结构类型id"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + @Parameter(name = "name", description = "设备名称"), + @Parameter(name = "useDeptId", description = "使用部门id"), + @Parameter(name = "startTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间"), + @Parameter(name = "wxStatus", description = "维修状态"), + @Parameter(name = "wxNo", description = "维修记录单号"), + @Parameter(name = "relationTypeId", description = "结构类型id"), + @Parameter(name = "userId", description = "用户id") + }) +// @RequiresPermissions("equipment:maintainRecords:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + if (Objects.isNull(params.get("relationTypeId"))) { + throw new SysException("结构类型id不能为空"); + } + PageData pageList = eqWxPlanService.page(params); + List pageData = pageList.getList(); + + Map storeThingsMap = new HashMap<>(); + Map storeEqBxMap = new HashMap<>(); + + List resultList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(pageData)) { + for (EqWxPlanDTO data : pageData) { + EqWxInfoRes eqWxInfoRes = new EqWxInfoRes(); + BeanUtils.copyProperties(data, eqWxInfoRes); + //调整 + String userId = data.getWxUser(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqWxInfoRes.setWxUseText(name); + // 查询设备信息 + ThingDTO thingDTO = new ThingDTO(); + if (storeThingsMap.containsKey(data.getThingId())) { + thingDTO = storeThingsMap.get(data.getThingId()); + } else { + iotThingBaseInfoService.getThingInfo(thingDTO, data.getThingId(), null, "1", String.valueOf(params.get("relationTypeId"))); + storeThingsMap.put(data.getThingId(), thingDTO); + } + + eqWxInfoRes.setBaseInfo(thingDTO); + // 查询报修信息 + if (storeEqBxMap.containsKey(data.getEqBxId())) { + eqWxInfoRes.setBxInfo(storeEqBxMap.get(data.getEqBxId())); + } else { + EqBxEntity eqBxEntity = eqBxService.getById(data.getEqBxId()); + if (!Objects.isNull(eqBxEntity)) { + BxInfoDTO bxInfoDTO = new BxInfoDTO(); + BeanUtils.copyProperties(eqBxEntity, bxInfoDTO); + eqWxInfoRes.setBxInfo(bxInfoDTO); + storeEqBxMap.put(data.getEqBxId(), bxInfoDTO); + } else { + storeEqBxMap.put(data.getEqBxId(), null); + } + } + + resultList.add(eqWxInfoRes); + } + } + + PageData page = new PageData<>(resultList, CollectionUtil.isEmpty(resultList) ? 0 : pageList.getTotal()); + + return new Result>().ok(page); + } + + @GetMapping("{id}/{relationTypeId}") + @Operation(summary= "信息") +// @RequiresPermissions("eqwxplan:eqwxplan:info") + public Result get(@PathVariable("id") Long id, @PathVariable(value = "relationTypeId") Long relationTypeId) { + EqWxPlanDTO data = eqWxPlanService.getByIdAs(id,EqWxPlanDTO.class); + + if (Objects.isNull(data)) { + throw new SysException("维修计划不存在"); + } + + EqWxInfoRes eqWxInfoRes = new EqWxInfoRes(); + BeanUtils.copyProperties(data, eqWxInfoRes); + // 查询设备信息 + ThingDTO thingDTO = new ThingDTO(); + iotThingBaseInfoService.getThingInfo(thingDTO, data.getThingId(), null, "1", String.valueOf(relationTypeId)); + eqWxInfoRes.setBaseInfo(thingDTO); + + //调整 + String userId = data.getWxUser(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqWxInfoRes.setWxUseText(name); + + BxInfoDTO bxInfoDTO = new BxInfoDTO(); + // 查询报修信息 + EqBxEntity eqBxEntity = eqBxService.getById(data.getEqBxId()); + if (!Objects.isNull(eqBxEntity)) { + BeanUtils.copyProperties(eqBxEntity, bxInfoDTO); + eqWxInfoRes.setBxInfo(bxInfoDTO); + + Map> document = fileService.getDocument(data.getEqBxId()); + bxInfoDTO.setImageUrls(document.get("image")); + bxInfoDTO.setAttachmentUrls(document.get("attachment")); + } + // 查询维修备件信息 + eqWxInfoRes.setReplacements(eqWxReplacementService.getWxReplacementByWxPlanId(data.getId())); + + return new Result().ok(eqWxInfoRes); + } + + @PostMapping + @Operation(summary= "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:maintainRecords:add") + public Result save(@RequestBody EqWxPlanAddDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + if (CollectionUtil.isEmpty(dto.getThingIds())) { + throw new SysException("请选择待维修设备"); + } + //查询是否报废 + eqScrapService.checkThingStatus(Long.parseLong(dto.getThingIds().get(0))); + + if (StringUtils.isBlank(dto.getWxStatus())) { + dto.setWxStatus("1"); + } + if (StringUtils.isBlank(dto.getStopStatus())) { + dto.setStopStatus("1"); + } + + saveInfo(dto); + return new Result(); + } + + public synchronized void saveInfo(EqWxPlanAddDTO dto) { + eqWxPlanService.saveRepairPlan(dto); + } + + @PutMapping + @Operation(summary= "修改") + @LogOperation("修改") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:maintainRecords:update") + public Result update(@RequestBody EqWxPlanUpdateDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + if (Objects.isNull(dto.getThingId())) { + throw new SysException("维修设备id不能为空"); + } + //查询是否报废 + eqScrapService.checkThingStatus(dto.getThingId()); + + updateInfo(dto); + + return new Result(); + } + + public synchronized void updateInfo(EqWxPlanUpdateDTO dto) { + eqWxPlanService.updateRepairPlan(dto); + } + + @DeleteMapping + @Operation(summary= "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:maintainRecords:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + for (Long id : ids) { + EqWxPlanDTO eqWxPlanDTO = eqWxPlanService.getByIdAs(id,EqWxPlanDTO.class); + if (!Objects.isNull(eqWxPlanDTO) && "2".equals(eqWxPlanDTO.getWxStatus())) { + throw new SysException("不能删除已完成的维修记录"); + } + } + + eqWxPlanService.batchDelete(ids); + + List idList = Arrays.asList(ids); + + eqWxReplacementService.deleteWxReplacementByWxPlanId(idList); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary= "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name = "relationTypeId", description = "结构类型id", paramType = "query", required = true, dataType = "String"), +// @Parameter(name = "eqIds", description = "设备id,多个用,隔开", paramType = "query", dataType = "String"), +// @Parameter(name = "name", description = "设备名称", paramType = "query", dataType = "String"), +// @Parameter(name = "useDeptId", description = "使用部门id", paramType = "query", dataType = "Long"), +// @Parameter(name = "startTime", description = "开始时间", paramType = "query", dataType = "String"), +// @Parameter(name = "endTime", description = "结束时间", paramType = "query", dataType = "String"), +// @Parameter(name = "wxStatus", description = "维修状态", paramType = "query", dataType = "String"), +// @Parameter(name = "wxNo", description = "维修记录单号", paramType = "query", dataType = "String"), +// @Parameter(name = "relationTypeId", description = "结构类型id", paramType = "query", dataType = "String") +// }) +// @RequiresPermissions("equipment:maintainRecords:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqWxPlanService.exportList(params); + + //调整 + String dictTypeStr = "mulfunctionType,faultReason,maintainLevel,repairType,urdencyDegree"; + List dictTypeList1 = StringUtils.isNotBlank(dictTypeStr) ? Arrays.asList(dictTypeStr.split(",")) : null; + List dictTypeList = sysDictTypeService.getDictListByDictTypeList(dictTypeList1); +// Result resultResponse = dictTypeServiceFeign.getDictListByDictTypeList("mulfunctionType,faultReason,maintainLevel,repairType,urdencyDegree"); +// +// if (Objects.isNull(resultResponse) && resultResponse.getCode() != 0) { +// throw new SysException(!Objects.isNull(resultResponse) ? resultResponse.getMsg() : "服务异常"); +// } +// +// List dictTypeList = (List) resultResponse.getData(); + + Map dictMap = null; + if (CollectionUtil.isNotEmpty(dictTypeList)) { + dictMap = dictTypeList.stream().collect(Collectors.toMap(item -> String.valueOf(item.getDictType()), item -> item.getDataList())); + } + + Map storeThingInfo = new HashMap<>(); + Map storeUserName = new HashMap<>(); + Map storeEqBxEntity = new HashMap<>(); + Map deptName = new HashMap<>(); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + for (EqWxPlanDTO data : list) { + EqWxPlanExcel eqWxPlanExcel = new EqWxPlanExcel(); + BeanUtils.copyProperties(data, eqWxPlanExcel); + + if (storeUserName.containsKey(data.getWxUser())) { + eqWxPlanExcel.setWxUser(storeUserName.get(data.getWxUser())); + } else { + //调整 + String userId = data.getWxUser(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqWxPlanExcel.setWxUser(name); + storeUserName.put(data.getWxUser(), eqWxPlanExcel.getWxUser()); + } + // 查询设备信息 + ThingDTO thingDTO = new ThingDTO(); + + if (storeThingInfo.containsKey(data.getThingId())) { + thingDTO = storeThingInfo.get(data.getThingId()); + } else { + iotThingBaseInfoService.getThingInfo(thingDTO, data.getThingId(), null, "1", String.valueOf(params.get("relationTypeId"))); + storeThingInfo.put(data.getThingId(), thingDTO); + } + + if (!Objects.isNull(thingDTO) && !Objects.isNull(thingDTO.getUseDeptId())) { + if (deptName.containsKey(thingDTO.getUseDeptId())) { + eqWxPlanExcel.setUseDeptName(deptName.get(thingDTO.getUseDeptId())); + } else { + SysDeptDTO deptDTO = sysDeptService.get(thingDTO.getUseDeptId()); + if (!Objects.isNull(deptDTO)) { + eqWxPlanExcel.setUseDeptName(deptDTO.getName()); + deptName.put(thingDTO.getUseDeptId(), deptDTO.getName()); + } + } + } + BeanUtils.copyProperties(thingDTO, eqWxPlanExcel); + + // 查询报修信息 + EqBxEntity eqBxEntity = null; + if (storeEqBxEntity.containsKey(data.getEqBxId())) { + eqBxEntity = storeEqBxEntity.get(data.getEqBxId()); + } else { + eqBxEntity = eqBxService.getById(data.getEqBxId()); + storeEqBxEntity.put(data.getEqBxId(), eqBxEntity); + } + if (!Objects.isNull(eqBxEntity)) { + BeanUtils.copyProperties(eqBxEntity, eqWxPlanExcel); + } + + if (StringUtils.isNotBlank(eqWxPlanExcel.getFaultType())) { + eqWxPlanExcel.setFaultType(getDictValue(dictMap, "mulfunctionType", eqWxPlanExcel.getFaultType())); + } + if (StringUtils.isNotBlank(eqWxPlanExcel.getFaultReason())) { + eqWxPlanExcel.setFaultReason(getDictValue(dictMap, "faultReason", eqWxPlanExcel.getFaultReason())); + } + if (StringUtils.isNotBlank(eqWxPlanExcel.getWxLevel())) { + eqWxPlanExcel.setWxLevel(getDictValue(dictMap, "maintainLevel", eqWxPlanExcel.getWxLevel())); + } + if (StringUtils.isNotBlank(eqWxPlanExcel.getWxType())) { + eqWxPlanExcel.setWxType(getDictValue(dictMap, "repairType", eqWxPlanExcel.getWxType())); + } + if (StringUtils.isNotBlank(eqWxPlanExcel.getUrgentLevel())) { + eqWxPlanExcel.setUrgentLevel(getDictValue(dictMap, "urdencyDegree", eqWxPlanExcel.getUrgentLevel())); + } + eqWxPlanExcel.setId(data.getId()); + excels.add(eqWxPlanExcel); + } + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqWxPlanExcel eqWxPlanExcel : excels ){ + if (idList.contains(eqWxPlanExcel.getId())){ + newList.add(eqWxPlanExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "维修记录", EqWxPlanExcel.class,"维修记录.xls",response); + } + + + public String getDictValue(Map dictMap, String dictType, String curValue) { + //调整 + if (CollectionUtil.isEmpty(dictMap)) { + return ""; + } + List temp = new ArrayList<>(); + Object obj = dictMap.get(dictType); + temp= JSON.parseArray(JSON.toJSONString(obj),SysDictDataEntity.class); + + if (CollectionUtil.isEmpty(temp)) { + return ""; + } + Map dictObject = new HashMap<>(); + for (SysDictDataEntity dictData:temp){ + dictObject.put(dictData.getDictValue(),dictData.getDictLabel()); + } + +// List temp = (List) dictMap.get(dictType); +// if (CollectionUtil.isEmpty(temp)) { +// return ""; +// } +// Map dictObject = temp.stream().collect(Collectors.toMap(item -> String.valueOf(item.get("dictValue")), item -> String.valueOf(item.get("dictLabel")))); + String faultType = StringUtils.isNotBlank(dictObject.get(curValue)) ? dictObject.get(curValue) : "当前字典值不存在"; + return faultType; + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxReplacementController.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxReplacementController.java new file mode 100644 index 0000000..3abf1b0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/controller/EqWxReplacementController.java @@ -0,0 +1,115 @@ +package com.thing.eq.eqbxwx.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.EqWxReplacementDTO; +import com.thing.eq.eqbxwx.excel.EqWxReplacementExcel; +import com.thing.eq.eqbxwx.service.EqWxReplacementService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 维修备件 +* +* @author zy zy@xx.com +* @since 3.0 2021-10-20 +*/ +@RestController +@RequestMapping("eqwxre/eqwxreplacement") +@Tag(name="维修备件") +public class EqWxReplacementController { + @Autowired + private EqWxReplacementService eqWxReplacementService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("eqwxre:eqwxreplacement:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqWxReplacementService.getPageData(params,EqWxReplacementDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("eqwxre:eqwxreplacement:info") + public Result get(@PathVariable("id") Long id){ + EqWxReplacementDTO data = eqWxReplacementService.getByIdAs(id,EqWxReplacementDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("eqwxre:eqwxreplacement:save") + public Result save(@RequestBody EqWxReplacementDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqWxReplacementService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("eqwxre:eqwxreplacement:update") + public Result update(@RequestBody EqWxReplacementDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqWxReplacementService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("eqwxre:eqwxreplacement:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqWxReplacementService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("eqwxre:eqwxreplacement:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqWxReplacementService.listAs(params,EqWxReplacementDTO.class); + ExcelUtils.exportExcel(list,null, "维修备件", EqWxReplacementExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, null, "维修备件", list, EqWxReplacementExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/BxInfoDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/BxInfoDTO.java new file mode 100644 index 0000000..ec12d0b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/BxInfoDTO.java @@ -0,0 +1,54 @@ +package com.thing.eq.eqbxwx.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; + +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/19 0019 17:56:58 + */ +@Data +public class BxInfoDTO { + + @Schema(description = "id") + private Long id; + + @Schema(description = "报修单号") + private String bxNo; + + @Schema(description = "发生时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date happenDate; + + @Schema(description = "报修人") + private String bxUser; + + @Schema(description = "故障部位") + private String faultPart; + + @Schema(description = "故障类别") + private String faultType; + + @Schema(description = "故障等级") + private String faultLevel; + + @Schema(description = "故障描述") + private String faultDesc; + + @Schema(description = "图片路径") + List imageUrls; + + @Schema(description = "附件路径") + List attachmentUrls; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/CommonDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/CommonDTO.java new file mode 100644 index 0000000..7a430c5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/CommonDTO.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqbxwx.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Map; + +/** + * @author :xiezw + * @date :2022/6/24 15:25 + * @description : + */ +@Data +@Schema(description = "导出公共参数") +public class CommonDTO { + private Map params; + private Long[] ids; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxDTO.java new file mode 100644 index 0000000..154dcaf --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxDTO.java @@ -0,0 +1,144 @@ +package com.thing.eq.eqbxwx.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@Schema(description = "设备报修") +public class EqBxDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "报修id(修改时必填)", required = true) + @NotNull(message = "报修id不能为空", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "故障类别") + private String faultType; + + @Schema(description = "维修班组") + private String wxTeam; + + @Schema(description = "维修人员") + private String wxUserId; + + @Schema(description = "紧急程度") + private String urgentLevel; + + @Schema(description = "是否停机;0不停机 1停机,默认0") + private String shutdown; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "创建人") + private Long creator; + + @Schema(description = "更新人") + private Long updater; + + @Schema(description = "故障描述") + @Length(message = "故障描述长度不能超过200", max = 200) + private String faultDesc; + + @Schema(description = "故障原因") + @Length(message = "故障原因长度不能超过200", max = 200) + private String faultReason; + + @Schema(description = "设备id") + private Long eqId; + + @Schema(description = "发生时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date happenDate; + + @Schema(description = "报修人") + private String bxUser; + + @Schema(description = "报修人手机号") + private String bxUserMobile; + + @Schema(description = "故障等级") + private String faultLevel; + + @Schema(description = "审核人") + private Long reviewer; + + @Schema(description = "报修状态 0:未处理 1:维修中 2:已完成") + private String wxStatus; + + @Schema(description = "维修级别") + private String wxLevel; + + @Schema(description = "报修单号") + private String bxNo; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "设备名称") + private String deviceName; + + @Schema(description = "物编号") + private String deviceCode; + + @Schema(description = "设备编号") + private String eqCode; + + @Schema(description = "规格型号") + private String deviceStandard; + + @Schema(description = "设备类型/设备类别") + private String deviceType; + + @Schema(description = "设备位置") + private String devicePosition; + + @Schema(description = "设备状态") + private String thingStatus; + + @Schema(description = "报修状态(ACTIVE:有人接单,INACTIVE:无人接单)") + private String repairStatus; + + @Schema(description = "当前用户") + private String userName; + + @Schema(description = "报修受理时间") + private Date repairTime; + + @Schema(description = "图片路径") + List imageUrls; + + @Schema(description = "附件路径") + List attachmentUrls; + + @Schema(description = "是否进入维修 0:不进入维修 1:进入维修") + private String inWx; + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "修改时间") + private Date updateDate; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxResDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxResDTO.java new file mode 100644 index 0000000..7ed1f22 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqBxResDTO.java @@ -0,0 +1,21 @@ +package com.thing.eq.eqbxwx.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@Schema(description = "设备报修") +public class EqBxResDTO extends EqBxDTO { + private static final long serialVersionUID = 1L; + + @Schema(description = "维修人员列表") + private List wxUserIdList; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxDTO.java new file mode 100644 index 0000000..cb50043 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxDTO.java @@ -0,0 +1,59 @@ +package com.thing.eq.eqbxwx.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 设备维修 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备维修") +public class EqWxDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "故障原因") + private String faultReason; + @Schema(description = "维修班组") + private String wxTeam; + @Schema(description = "紧急程度") + private String urgentLevel; + @Schema(description = "是否停机;0不停机 1停机,默认0") + private String shutdown; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "工作描述") + private String workDesc; + @Schema(description = "设备报修id") + private Long eqBxId; + @Schema(description = "维修级别") + private String wxLevel; + @Schema(description = "维修状态") + private String wxStatus; + @Schema(description = "维修单号") + private String wxNo; + @Schema(description = "停机时长(单位分钟)") + private String shutdownTime; + @Schema(description = "维修开始时间") + private Date wxStartTime; + @Schema(description = "维修结束时间") + private Date wxEndTime; + @Schema(description = "维修费用") + private String wxFee; + @Schema(description = "维修类别") + private String wxType; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxInfoRes.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxInfoRes.java new file mode 100644 index 0000000..e0efdf2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxInfoRes.java @@ -0,0 +1,30 @@ +package com.thing.eq.eqbxwx.dto; + + +import com.thing.eq.eqcheck.dto.ThingDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/20 0020 13:48:18 + */ +@Data +public class EqWxInfoRes extends EqWxPlanDTO { + + @Schema(description = "维修人员名称") + private String wxUseText; + @Schema(description = "维修人员列表") + private List wxUserIdList; + @Schema(description = "设备物信息") + private ThingDTO baseInfo; + + @Schema(description = "报修信息") + private BxInfoDTO bxInfo; + + @Schema(description = "维修备件列表") + private List replacements; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanAddDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanAddDTO.java new file mode 100644 index 0000000..b220348 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanAddDTO.java @@ -0,0 +1,23 @@ +package com.thing.eq.eqbxwx.dto; + +import com.thing.common.core.validator.group.AddGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/19 0019 16:10:25 + */ +@Data +public class EqWxPlanAddDTO extends EqWxPlanDTO { + + @Schema(description = "设备物id列表(新增时必填)",required = true) + @NotNull(message = "维修设备物id不能为空",groups = AddGroup.class) + private List thingIds; + @Schema(description = "维修备件列表") + private List replacements; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanDTO.java new file mode 100644 index 0000000..50fce0e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanDTO.java @@ -0,0 +1,98 @@ +package com.thing.eq.eqbxwx.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Length; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@Schema(description = "设备维修计划") +public class EqWxPlanDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id(修改时必填)",required = true) + @NotNull(message = "维修记录id不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "维修班组") + private String wxTeam; + + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "修改时间") + private Date updateDate; + // @Schema(description = "创建时间") +// private Date createDate; + @Schema(description = "更新人") + private Long updater; + + @Schema(description = "设备报修id") + private Long eqBxId; + + @Schema(description = "维修单号") + private String wxNo; + // @Schema(description = "维修计划时间") +// @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// private Date wxPlanTime; + @Schema(description = "计划维修人员id") + private String wxUser; + @Schema(description = "工作天数") + private String workTime; + @Schema(description = "设备物id") + private Long thingId; + @Schema(description = "故障原因") + private String faultCause; + @Schema(description = "维修状态 0:未处理 1:维修中 2:已完成") + private String wxStatus; + @Schema(description = "维修级别(新增时必填)",required = true) + @NotNull(message = "维修级别不能为空",groups = AddGroup.class) + @NotBlank(message = "维修级别不能为空",groups = AddGroup.class) + private String wxLevel; + @Schema(description = "维修类别(新增时必填)",required = true) + @NotNull(message = "维修类别不能为空",groups = AddGroup.class) + @NotBlank(message = "维修类别不能为空",groups = AddGroup.class) + private String wxType; + @Schema(description = "紧急程度(新增时必填)",required = true) + @NotNull(message = "紧急程度不能为空",groups = AddGroup.class) + @NotBlank(message = "紧急程度不能为空",groups = AddGroup.class) + private String urgentLevel; + @Schema(description = "是否停机 0:不停机 1:停机") + private String stopStatus; + @Schema(description = "停机时长") + private String stopTime; + @Schema(description = "开始时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date startTime; + @Schema(description = "结束时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date endTime; + @Schema(description = "工作描述") + @Length(max = 200,message = "工作描述长度不能超过200") + private String workDesc; + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户编码") + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanUpdateDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanUpdateDTO.java new file mode 100644 index 0000000..9eee452 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxPlanUpdateDTO.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqbxwx.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/19 0019 16:10:25 + */ +@Data +public class EqWxPlanUpdateDTO extends EqWxPlanDTO { + @Schema(description = "维修备件列表",required = true) + private List replacements; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxReplacementDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxReplacementDTO.java new file mode 100644 index 0000000..f998447 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/EqWxReplacementDTO.java @@ -0,0 +1,28 @@ +package com.thing.eq.eqbxwx.dto; + +import com.thing.eq.eqcheck.dto.ThingDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 维修备件 +* +* @author zy zy@xx.com +* @since 3.0 2021-10-20 +*/ +@Data +@Schema(description = "维修备件") +public class EqWxReplacementDTO extends ThingDTO implements Serializable { + private static final long serialVersionUID = 1L; + @Schema(description = "id") + private Long id; + @Schema(description = "维修记录(计划)id") + private Long wxPlanId; +// @Schema(description = "备件物id") +// private Long thingId; + @Schema(description = "使用数量") + private Integer useCount; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/ReplaceMentUseDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/ReplaceMentUseDTO.java new file mode 100644 index 0000000..7d1b1ab --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/ReplaceMentUseDTO.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqbxwx.dto; + +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/11 0011 14:36:19 + */ +@Data +public class ReplaceMentUseDTO { + private Long thingId; + private Integer totalUse; + + private List wxUseDTOList; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/WxUseDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/WxUseDTO.java new file mode 100644 index 0000000..02ac338 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/dto/WxUseDTO.java @@ -0,0 +1,14 @@ +package com.thing.eq.eqbxwx.dto; + +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/11 0011 14:37:59 + */ +@Data +public class WxUseDTO { + private Long eqWxId; + private Integer useCount; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqBxEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqBxEntity.java new file mode 100644 index 0000000..93287ec --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqBxEntity.java @@ -0,0 +1,188 @@ +package com.thing.eq.eqbxwx.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_bx") +public class EqBxEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 故障类型 + */ + private String faultType; + /** + * 维修班组 + */ + private String wxTeam; + /** + * 维修组成员 + */ + private String wxUserId; + /** + * 紧急程度 + */ + private String urgentLevel; + /** + * 是否停机;0不停机 1停机,默认0 + */ + private String shutdown; + /** + * 部门id + */ + + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 故障描述 + */ + private String faultDesc; + /** + * 设备id + */ + private Long eqId; + /** + * 发生时间 + */ + private Date happenDate; + /** + * 报修人 + */ + private String bxUser; + /** + * 报修人手机号 + */ + private String bxUserMobile; + /** + * 故障等级 + */ + private String faultLevel; + /** + * 审核人 + */ + private Long reviewer; + /** + * 维修状态 + */ + private String wxStatus; + + /** + * 维修级别 + */ + private String wxLevel; + + /** + * + * 报修单号 + */ + private String bxNo; + +// /** +// * 部门名称 +// */ +// private String deptName; + +// /** +// * 设备名称 +// */ +// private String deviceName; +// +// /** +// * 设备型号 +// */ +// private String deviceCode; + + +// /** +// * 规格型号 +// */ +// private String deviceStandard; + + +// /** +// * 故障原因 +// */ +// private String faultReason; + + /** + * 报修状态(ACTIVE:有人接单,INACTIVE:无人接单) + */ + private String repairStatus; + +// /** +// * 当前用户 +// */ +// private String userName; + + /** + * 报修受理时间 + */ + private Date repairTime; + + +// /** +// * 报修页面查看范围:0-所有,1:未处理以及处理中 +// */ +// private Integer rangeType; + + + + /** + * 图片路径 + */ +// List imageUrls; + + /** + * 附件路径 + */ +// List attachmentUrls; + /** + * 是否进入报修 + */ + private String inWx; + +// /** +// * 设备编号 +// */ +// private String eqCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxEntity.java new file mode 100644 index 0000000..deff722 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxEntity.java @@ -0,0 +1,102 @@ +package com.thing.eq.eqbxwx.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备维修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_wx") +public class EqWxEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 故障原因 + */ + private String faultReason; + /** + * 维修班组 + */ + private String wxTeam; + /** + * 紧急程度 + */ + private String urgentLevel; + /** + * 是否停机;0不停机 1停机,默认0 + */ + private String shutdown; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 工作描述 + */ + private String workDesc; + /** + * 设备报修id + */ + private Long eqBxId; + /** + * 维修级别 + */ + private String wxLevel; + /** + * 维修状态 + */ + private String wxStatus; + /** + * 维修单号 + */ + private String wxNo; + /** + * 停机时长(单位分钟) + */ + private String shutdownTime; + /** + * 维修开始时间 + */ + private Date wxStartTime; + /** + * 维修结束时间 + */ + private Date wxEndTime; + /** + * 维修费用 + */ + private String wxFee; + /** + * 维修类别 + */ + private String wxType; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxPlanEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxPlanEntity.java new file mode 100644 index 0000000..1ee41ab --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxPlanEntity.java @@ -0,0 +1,121 @@ +package com.thing.eq.eqbxwx.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_wx_plan") +public class EqWxPlanEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 维修班组 + */ + private String wxTeam; + + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 设备报修id + */ + private Long eqBxId; + /** + * 维修单号 + */ + private String wxNo; + /** + * 维修人员 + */ + private String wxUser; + /** + * 工作天数 + */ + private String workTime; + /** + * 设备物id + */ + private Long thingId; + /** + * 故障原因 + */ + private String faultCause; + /** + * 维修状态 + */ + private String wxStatus; + /** + * 维修级别 + */ + private String wxLevel; + /** + * 维修类别 + */ + private String wxType; + /** + * 紧急程度 + */ + private String urgentLevel; + /** + * 是否停机 0:不停机 1:停机 + */ + private String stopStatus; + /** + * 停机时长 + */ + private String stopTime; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 描述 + */ + private String workDesc; + /** + * 部门id + */ + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxReplacementEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxReplacementEntity.java new file mode 100644 index 0000000..0b19ef2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/entity/EqWxReplacementEntity.java @@ -0,0 +1,39 @@ +package com.thing.eq.eqbxwx.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 维修备件 + * + * @author zy zy@xx.com + * @since 3.0 2021-10-20 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_wx_replacement") +public class EqWxReplacementEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 维修记录(计划)id + */ + private Long wxPlanId; + /** + * 备件物id + */ + private Long thingId; + /** + * 使用数量 + */ + private Integer useCount; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/BaseExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/BaseExcel.java new file mode 100644 index 0000000..b518df6 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/BaseExcel.java @@ -0,0 +1,25 @@ +package com.thing.eq.eqbxwx.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/9 0009 9:58:15 + */ +@Data +public class BaseExcel { + @Excel(name = "设备编号",orderNum ="3") + private String eqCode; + @Excel(name = "设备名称",orderNum ="4") + private String thingName; + @Excel(name = "规格型号",orderNum ="5") + private String thingStandard; + @Excel(name = "使用部门",orderNum ="6") + private String useDeptName; + @Excel(name = "设备类型",orderNum ="7") + private String deviceType; + @Excel(name = "设备状态",orderNum ="8") + private String thingStatus; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqBxExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqBxExcel.java new file mode 100644 index 0000000..55b4721 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqBxExcel.java @@ -0,0 +1,57 @@ +package com.thing.eq.eqbxwx.excel; + + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; + +import lombok.Data; + +import java.util.Date; + + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(30) +@HeadRowHeight(30) +@ColumnWidth(35) +public class EqBxExcel{ + @ExcelIgnore() + private Long id; + @Excel(name = "报修单号", orderNum = "1") + private String bxNo; + + @Excel(name = "处理状态(未维修_0,维修中_1,已完成_2)",orderNum = "2") + private String wxStatus; + + @Excel(name = "报修人" ) + private String bxUser; + + @Excel(name = "发生时间", orderNum = "3") + private Date happenDate; + + @Excel(name = "故障类别") + private String faultType; + + @Excel(name = "故障描述") + private String faultDesc; + + @Excel(name = "设备名称") + private String thingName; + + @Excel(name = "设备编号") + private String eqCode; + + @Excel(name = "规格型号") + private String thingStandard; + + @Excel(name = "设备状态") + private String thingStatus; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxExcel.java new file mode 100644 index 0000000..fd85b7a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxExcel.java @@ -0,0 +1,60 @@ +package com.thing.eq.eqbxwx.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备维修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqWxExcel { + @Excel(name = "id", orderNum = "1") + private Long id; + @Excel(name = "故障原因", orderNum = "2") + private String faultReason; + @Excel(name = "维修班组", orderNum = "3") + private String wxTeam; + @Excel(name = "紧急程度", orderNum = "4") + private String urgentLevel; + @Excel(name = "是否停机;0不停机 1停机,默认0", orderNum = "5") + private String shutdown; + @Excel(name = "创建人", orderNum = "6") + private Long creator; + @Excel(name = "创建时间", orderNum = "7") + private Date createDate; + @Excel(name = "更新人", orderNum = "8") + private Long updater; + @Excel(name = "更新时间", orderNum = "9") + private Date updateDate; + @Excel(name = "工作描述", orderNum = "10") + private String workDesc; + @Excel(name = "设备报修id", orderNum = "11") + private Long eqBxId; + @Excel(name = "维修级别", orderNum = "12") + private String wxLevel; + @Excel(name = "维修状态", orderNum = "13") + private String wxStatus; + @Excel(name = "维修单号", orderNum = "14") + private String wxNo; + @Excel(name = "停机时长(单位分钟)", orderNum = "15") + private String shutdownTime; + @Excel(name = "维修开始时间", orderNum = "16") + private Date wxStartTime; + @Excel(name = "维修结束时间", orderNum = "17") + private Date wxEndTime; + @Excel(name = "维修费用", orderNum = "18") + private String wxFee; + @Excel(name = "维修类别", orderNum = "19") + private String wxType; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxPlanExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxPlanExcel.java new file mode 100644 index 0000000..ac43bb9 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxPlanExcel.java @@ -0,0 +1,55 @@ +package com.thing.eq.eqbxwx.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(30) +@HeadRowHeight(30) +@ColumnWidth(35) +public class EqWxPlanExcel extends BaseExcel { + @ExcelIgnore() + private Long id; + @Excel(name = "维修单号", orderNum = "1") + private String wxNo; + @Excel(name = "维修状态", orderNum = "2") + private String wxStatus; + @Excel(name = "报修人", orderNum = "8") + private String bxUser; + @Excel(name = "发生时间", orderNum = "9") + private Date happenDate; + @Excel(name = "工作描述", orderNum = "10") + private String workDesc; + @Excel(name = "故障类别", orderNum = "11") + private String faultType; + @Excel(name = "故障原因", orderNum = "12") + private String faultReason; + @Excel(name = "故障描述", orderNum = "13") + private String faultDesc; + @Excel(name = "维修人员", orderNum = "14") + private String wxUser; + @Excel(name = "维修级别", orderNum = "15") + private String wxLevel; + @Excel(name = "维修类别", orderNum = "16") + private String wxType; + @Excel(name = "紧急程度", orderNum = "17") + private String urgentLevel; + @Excel(name = "开始时间", orderNum = "18") + private Date startTime; + @Excel(name = "结束时间", orderNum = "19") + private Date endTime; + @Excel(name = "停机时长", orderNum = "20") + private String stopTime; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxReplacementExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxReplacementExcel.java new file mode 100644 index 0000000..712e43d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/excel/EqWxReplacementExcel.java @@ -0,0 +1,28 @@ +package com.thing.eq.eqbxwx.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +/** + * 维修备件 + * + * @author zy zy@xx.com + * @since 3.0 2021-10-20 + */ +@Data +@ContentRowHeight(30) +@HeadRowHeight(30) +@ColumnWidth(35) +public class EqWxReplacementExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "维修记录(计划)id", orderNum = "2") + private Long wxPlanId; + @Excel(name = "备件物id", orderNum = "3") + private Long thingId; + @Excel(name = "使用数量", orderNum = "4") + private Integer useCount; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqBxMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqBxMapper.java new file mode 100644 index 0000000..c34f063 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqBxMapper.java @@ -0,0 +1,38 @@ +package com.thing.eq.eqbxwx.mapper; + + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqbxwx.dto.EqBxDTO; +import com.thing.eq.eqbxwx.entity.EqBxEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Mapper +public interface EqBxMapper extends PowerBaseMapper { +// List selectEqBxPage(Page page, +// @Param("faultType") String faultType, +// @Param("bxUser") String bxUser, +// @Param("wxStatus") String wxStatus, +// @Param("rangeType") Integer rangeType, +// @Param("bxNo") String bxNo, +// List eqIds +// +// ); + List selectEqBxPage(Map params); + + EqBxEntity selectEqBxEntity(@Param("id") Long id); + + int findBxCountByEqId(@Param("eqId") Long eqId); + + EqBxEntity getListBxNo(String bxNo); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxMapper.java new file mode 100644 index 0000000..28cc2b2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxMapper.java @@ -0,0 +1,17 @@ +package com.thing.eq.eqbxwx.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqbxwx.entity.EqWxEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 设备维修 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqWxMapper extends PowerBaseMapper { + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxPlanMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxPlanMapper.java new file mode 100644 index 0000000..feaf1f5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxPlanMapper.java @@ -0,0 +1,22 @@ +package com.thing.eq.eqbxwx.mapper; + + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqbxwx.entity.EqWxPlanEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 设备维修计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqWxPlanMapper extends PowerBaseMapper { + + List getEqWxList(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxReplacementMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxReplacementMapper.java new file mode 100644 index 0000000..bb828c4 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/mapper/EqWxReplacementMapper.java @@ -0,0 +1,17 @@ +package com.thing.eq.eqbxwx.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqbxwx.entity.EqWxReplacementEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 维修备件 +* +* @author zy zy@xx.com +* @since 3.0 2021-10-20 +*/ +@Mapper +public interface EqWxReplacementMapper extends PowerBaseMapper { + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqBxService.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqBxService.java new file mode 100644 index 0000000..3bdd136 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqBxService.java @@ -0,0 +1,48 @@ +package com.thing.eq.eqbxwx.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqbxwx.dto.EqBxDTO; +import com.thing.eq.eqbxwx.entity.EqBxEntity; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.dto.SysUserDTO; + + +import java.util.List; +import java.util.Map; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqBxService extends IBaseService { + List exportData(Map params); + + List getWxUser(Long deptId); + + List getWxTeam(); + + + List getDevice(); + + EqBxDTO get(Long id); + + void receiveRepair(EqBxDTO dto); + + void delete(Long[] ids); + + void saveBxInfo(EqBxDTO dto); + + void saveWx(EqBxDTO dto); + + int findBxCountByEqId(Long eqId); + + void deleteFile(Long id, List imageUrls, List attachmentUrls); + + PageData pageList(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxPlanService.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxPlanService.java new file mode 100644 index 0000000..f0bf068 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxPlanService.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqbxwx.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqbxwx.dto.EqWxPlanAddDTO; +import com.thing.eq.eqbxwx.dto.EqWxPlanDTO; +import com.thing.eq.eqbxwx.dto.EqWxPlanUpdateDTO; +import com.thing.eq.eqbxwx.entity.EqWxPlanEntity; + +import java.util.List; +import java.util.Map; + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqWxPlanService extends IBaseService { + + List exportList(Map params); + + void saveRepairPlan(EqWxPlanAddDTO dto); + + void updateRepairPlan(EqWxPlanUpdateDTO dto); + + /** + * 设备维修计划条件查询 + */ + List searchRepairPlanByBxId(Long eqBxId); + + String getDictValue(Map dictMap, String dictType, String curValue); + + /** + * 设备维修计划条件查询 + */ + Long getWxCountByBxId(Long eqBxId); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxReplacementService.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxReplacementService.java new file mode 100644 index 0000000..5c511af --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxReplacementService.java @@ -0,0 +1,23 @@ +package com.thing.eq.eqbxwx.service; + + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqbxwx.dto.EqWxReplacementDTO; +import com.thing.eq.eqbxwx.entity.EqWxReplacementEntity; + +import java.util.List; + +/** + * 维修备件 + * + * @author zy zy@xx.com + * @since 3.0 2021-10-20 + */ +public interface EqWxReplacementService extends IBaseService { + void deleteWxReplacementByWxPlanId(Long wxPlanId); + + void deleteWxReplacementByWxPlanId(List wxPlanIds); + + List getWxReplacementByWxPlanId(Long wxPlanId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxService.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxService.java new file mode 100644 index 0000000..2388038 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/EqWxService.java @@ -0,0 +1,15 @@ +package com.thing.eq.eqbxwx.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqbxwx.entity.EqWxEntity; + +/** + * 设备维修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqWxService extends IBaseService { + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqBxServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqBxServiceImpl.java new file mode 100644 index 0000000..6dffb9f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqBxServiceImpl.java @@ -0,0 +1,326 @@ +package com.thing.eq.eqbxwx.service.impl; + +import cn.hutool.core.collection.CollectionUtil;; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.dto.EqBxDTO; +import com.thing.eq.eqbxwx.dto.EqWxPlanAddDTO; +import com.thing.eq.eqbxwx.entity.EqBxEntity; +import com.thing.eq.eqbxwx.mapper.EqBxMapper; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqbxwx.service.EqWxPlanService; +import com.thing.eq.eqfilemanage.service.EqFileDeleteService; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.file.service.FileService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysDeptMapper; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +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.util.*; +import java.util.stream.Collectors; + +/** + * 设备报修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqBxServiceImpl extends BaseServiceImpl implements EqBxService { + @Autowired + private EqBxMapper eqBxMapper; + + @Autowired + private SysUserMapper sysUserMapper; + + @Autowired + private SysDeptMapper sysDeptMapper; + + @Autowired + private SysUserService userService; + + @Autowired + private FileService fileService; + +// @Autowired +// private EmailMessage emailMessage; + + @Autowired + private EqWxPlanService eqWxPlanService; + + @Autowired + private EqFileDeleteService eqFileDeleteService; + + @Autowired + private EqScrapService eqScrapService; + + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + return wrapper; + } + + + + @Override + public PageData pageList(Map params) { + //分页参数 + long curPage = 1; + long limit = 10; + + if (params.get(Constant.PAGE) != null) { + curPage = Long.parseLong((String) params.get(Constant.PAGE)); + } + if (params.get(Constant.LIMIT) != null) { + limit = Long.parseLong((String) params.get(Constant.LIMIT)); + } + Page page = new Page<>(curPage, limit); + + getParam(params); + + params.put("page",page); + + List eqBxEntities = eqBxMapper.selectEqBxPage(params); + ArrayList eqBxDTOS = new ArrayList<>(); + for (EqBxDTO eqBxEntity : eqBxEntities) { + EqBxDTO eqBxDTO = new EqBxDTO(); + BeanUtils.copyProperties(eqBxEntity, eqBxDTO); + + Map> document = fileService.getDocument(eqBxDTO.getId()); + eqBxDTO.setImageUrls(document.get("image")); + eqBxDTO.setAttachmentUrls(document.get("attachment")); + + EqScrapEntity eqScrapEntity = eqScrapService.getByEqId(eqBxEntity.getEqId()); + if(!Objects.isNull(eqScrapEntity)){ + eqBxDTO.setThingStatus("报废"); + }else { + eqBxDTO.setThingStatus("正常"); + } + + eqBxDTOS.add(eqBxDTO); + } + + PageData pageData = new PageData<>(eqBxDTOS, eqBxDTOS.size()); + return pageData; + } + + @Override + public List exportData(Map params) { + + getParam(params); + + List eqBxEntities = eqBxMapper.selectEqBxPage(params); + + return eqBxEntities; + } + + public Map getParam(Map params) { + String faultType = getParam(params.get("faultType"), String.class); + String bxUser = getParam(params.get("bxUser"), String.class); + String wxStatus = getParam(params.get("wxStatus"), String.class); + Integer rangeType = Integer.parseInt(getParam(params.get("rangeType"), Integer.class)); + String bxNo = getParam(params.get("bxNo"), String.class); + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + } + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + params.put("faultType", faultType); + params.put("bxUser", bxUser); + params.put("wxStatus", wxStatus); + params.put("rangeType", rangeType); + params.put("bxNo", bxNo); + params.put("eqIds", eqthingIds); + + return params; + } + + public String getParam(Object obj, Class clazz) { + if (clazz.equals(Integer.class)) { + return Objects.isNull(obj) ? "0" : String.valueOf(obj); + } + + return Objects.isNull(obj) ? "" : String.valueOf(obj); + } + + + @Override + public List getWxUser(Long deptId) { + try { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("dept_id", deptId); + List list = sysUserMapper.selectListByQuery(queryWrapper); + ArrayList sysUserDTOS = new ArrayList<>(); + for (SysUserEntity userEntity : list) { + SysUserDTO userDTO = new SysUserDTO(); + BeanUtils.copyProperties(userEntity, userDTO); + sysUserDTOS.add(userDTO); + } + return sysUserDTOS; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + @Override + public List getWxTeam() { + List sysDeptEntities = sysDeptMapper.selectListByQuery(null); + ArrayList sysDeptDTOS = new ArrayList<>(); + for (SysDeptEntity sysDeptEntity : sysDeptEntities) { + SysDeptDTO deptDTO = new SysDeptDTO(); + BeanUtils.copyProperties(sysDeptEntity, deptDTO); + sysDeptDTOS.add(deptDTO); + } + return sysDeptDTOS; + } + + @Override + public List getDevice() { + return null; + } + + @Override + public EqBxDTO get(Long id) { + EqBxEntity eqBxEntity = eqBxMapper.selectEqBxEntity(id); + EqBxDTO eqBxDTO = new EqBxDTO(); + if (eqBxEntity != null) { + BeanUtils.copyProperties(eqBxEntity, eqBxDTO); + } + Map> document = fileService.getDocument(eqBxDTO.getId()); + eqBxDTO.setImageUrls(document.get("image")); + eqBxDTO.setAttachmentUrls(document.get("attachment")); + return eqBxDTO; + } + + @Override + public void receiveRepair(EqBxDTO dto) { + EqBxEntity eqBxEntity = new EqBxEntity(); + Long id = userService.getByUsername(dto.getUserName()).getId(); + eqBxEntity.setWxUserId(String.valueOf(id)); + eqBxEntity.setRepairStatus("ACTIVE"); + Date date = new Date(); + eqBxEntity.setRepairTime(date); + eqBxMapper.update(eqBxEntity); + } + + @Override + public void delete(Long[] ids) { + delete(ids); + if (ids != null && ids.length > 0) { + for (Long id : ids) { + fileService.deleteDocument(id); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveBxInfo(EqBxDTO dto) { + saveInfo(dto); + } + + public void saveInfo(EqBxDTO dto) { +// QueryWrapper wrapper = new QueryWrapper(); +// wrapper.orderBy("bx_no",false); + EqBxEntity eqBxEntity = mapper.getListBxNo(dto.getBxNo()); + // 第一个单号用lastNo + String lastNo = ""; + if (Objects.isNull(eqBxEntity)) { + eqBxEntity = new EqBxEntity(); + } + lastNo = SerialNumberUnit.generateNumber("BX", eqBxEntity.getBxNo()); + + dto.setBxNo(lastNo); + dto.setTenantCode(SecurityUser.getTenantCode()); +// Long eqBxId = IdWorker.getId(); +// dto.setId(eqBxId); + + if ("1".equals(dto.getInWx())) { + dto.setWxStatus("1"); + saveWx(dto); + } else { + dto.setWxStatus("0"); + } + saveDto(dto); + + fileService.uploadText(dto.getImageUrls(), dto.getAttachmentUrls(), dto.getId()); +// if (StringUtils.isNotBlank(dto.getWxUserId())) { +// HashMap map = new HashMap<>(); +// map.put("userId", dto.getWxUserId()); +// map.put("desc", dto.getFaultDesc()); +// emailMessage.sendMessage(map); +// } + } + + @Override + public void saveWx(EqBxDTO dto) { + EqWxPlanAddDTO eqWxPlanAddDTO = new EqWxPlanAddDTO(); + eqWxPlanAddDTO.setThingIds(Arrays.asList(String.valueOf(dto.getEqId()))); + eqWxPlanAddDTO.setEqBxId(dto.getId()); + eqWxPlanAddDTO.setWxUser(dto.getWxUserId()); + eqWxPlanAddDTO.setFaultCause(dto.getFaultReason()); + eqWxPlanAddDTO.setWxLevel(dto.getWxLevel()); + eqWxPlanAddDTO.setUrgentLevel(dto.getUrgentLevel()); + eqWxPlanAddDTO.setStopStatus(dto.getShutdown()); + eqWxPlanAddDTO.setWxStatus(dto.getWxStatus()); + + eqWxPlanService.saveRepairPlan(eqWxPlanAddDTO); + } + + @Override + public int findBxCountByEqId(Long eqId) { + return eqBxMapper.findBxCountByEqId(eqId); + } + + @Override + public void deleteFile(Long id, List imageUrls, List attachmentUrls) { + // 查询原始文件 + List curBxFile = fileService.getDocumentById(id); + List oldUrlList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(curBxFile)) { + oldUrlList.addAll(curBxFile.stream().map(item -> item.getUrl()).collect(Collectors.toList())); + } + // 新提交的文件url + List newUrlList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(imageUrls)) { + newUrlList.addAll(imageUrls.stream().map(item -> item.getUrl()).collect(Collectors.toList())); + } + if (CollectionUtil.isNotEmpty(attachmentUrls)) { + newUrlList.addAll(attachmentUrls.stream().map(item -> item.getUrl()).collect(Collectors.toList())); + } + // 取新旧差集 即删除的文件url + oldUrlList.removeAll(newUrlList); + + if (CollectionUtil.isNotEmpty(oldUrlList)) { + eqFileDeleteService.deleteFile(oldUrlList); + } + } + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxPlanServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxPlanServiceImpl.java new file mode 100644 index 0000000..ff54dff --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxPlanServiceImpl.java @@ -0,0 +1,359 @@ +package com.thing.eq.eqbxwx.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.dto.*; +import com.thing.eq.eqbxwx.entity.EqWxPlanEntity; +import com.thing.eq.eqbxwx.entity.EqWxReplacementEntity; +import com.thing.eq.eqbxwx.mapper.EqWxPlanMapper; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqbxwx.service.EqWxPlanService; +import com.thing.eq.eqbxwx.service.EqWxReplacementService; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqpartrecord.entity.EqPartRecordEntity; +import com.thing.eq.eqpartrecord.service.EqPartRecordService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +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.util.*; +import java.util.stream.Collectors; + +/** + * 设备维修计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqWxPlanServiceImpl extends BaseServiceImpl implements EqWxPlanService { + + @Autowired + private EqWxReplacementService eqWxReplacementService; + + @Autowired + private EqWxPlanMapper eqWxPlanMapper; + + @Autowired + private EqBxService eqBxService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private EqPartRecordService eqPartRecordService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public PageData page(Map params) { +// IPage page = getPage(params, "create_date", false); + Page page = getPage(params); + //查询 + getQueryMap(params); + List list = eqWxPlanMapper.getEqWxList(params); + List targetList = ConvertUtils.sourceToTarget(list, EqWxPlanDTO.class); + return new PageData<>(targetList, page.getTotalRow()); + } + + @Override + public List exportList(Map params) { + //查询 + getQueryMap(params); + List list = eqWxPlanMapper.getEqWxList(params); + List targetList = ConvertUtils.sourceToTarget(list, EqWxPlanDTO.class); + return targetList; + } + + + public Map getQueryMap(Map params) { + //查询 + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + params.put("eqIds", eqthingIds); + } + + String useDeptId = String.valueOf(params.get("useDeptId")); + if (!Objects.isNull(params.get("useDeptId")) && StringUtils.isNotBlank(useDeptId)) { + params.put("useDeptId", Long.parseLong(useDeptId)); + } else { + params.remove("useDeptId"); + } + + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + + if (!Objects.isNull(params.get("startTime")) && StringUtils.isNotBlank(String.valueOf(params.get("startTime"))) + && !Objects.isNull("endTime") && StringUtils.isNotBlank(String.valueOf(params.get("endTime")))) { + Date start = DateTimeUtils.parse(String.valueOf(params.get("startTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + Date end = DateTimeUtils.parse(String.valueOf(params.get("endTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + + params.put("startTime", start); + params.put("endTime", end); + } else { + params.remove("startTime"); + params.remove("endTime"); + } + return params; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveRepairPlan(EqWxPlanAddDTO dto) { + savePlan(dto); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateRepairPlan(EqWxPlanUpdateDTO dto) { + EqWxPlanDTO eqWxPlanDTO = getByIdAs(dto.getId(),EqWxPlanUpdateDTO.class); + if (!Objects.isNull(eqWxPlanDTO) && "2".equals(eqWxPlanDTO.getWxStatus())) { + throw new SysException("当前维修记录已完成不可修改"); + } + + updateDto(dto); + + eqWxReplacementService.deleteWxReplacementByWxPlanId(dto.getId()); + + List replacementDTOS = dto.getReplacements(); + + List replaceMentUseDTOList = new ArrayList<>(); + + if (CollectionUtil.isNotEmpty(replacementDTOS)) { + replacementDTOS.forEach(replacementDTO -> { + replacementDTO.setWxPlanId(dto.getId()); + eqWxReplacementService.saveDto(replacementDTO); + + ReplaceMentUseDTO replaceMentUseDTO = new ReplaceMentUseDTO(); + replaceMentUseDTO.setThingId(replacementDTO.getThingId()); + replaceMentUseDTO.setTotalUse(replacementDTO.getUseCount()); + + WxUseDTO wxUseDTO = new WxUseDTO(); + wxUseDTO.setEqWxId(dto.getId()); + wxUseDTO.setUseCount(replacementDTO.getUseCount()); + + List temp = new ArrayList<>(); + temp.add(wxUseDTO); + + replaceMentUseDTO.setWxUseDTOList(temp); + + replaceMentUseDTOList.add(replaceMentUseDTO); + }); + } + + if ("2".equals(dto.getWxStatus()) && !Objects.isNull(eqWxPlanDTO.getEqBxId())) { + EqBxDTO eqBxDTO = new EqBxDTO(); + eqBxDTO.setId(eqWxPlanDTO.getEqBxId()); + eqBxDTO.setWxStatus("2"); + eqBxService.updateDto(eqBxDTO); + + updateStock(replaceMentUseDTOList); + } + } + +// public void updateStock(List replacementDTOS,Long eqWxId) { +// List eqPartRecordEntityList = new ArrayList<>(); +// for (EqWxReplacementDTO replacementDTO : replacementDTOS) { +// //校验库存,扣除库存 +// IotThingBaseInfoDTO data = iotThingBaseInfoService.getByThingsIdForUpdate(replacementDTO.getThingId()); +// //如果大于库存,不可操作 +// if (!Objects.isNull(data) && StringUtils.isNotBlank(data.getStock())) { +// Integer stock = Integer.parseInt(data.getStock()); +// if (replacementDTO.getUseCount() > Integer.parseInt(data.getStock())) { +// throw new RenException("备件库存不足,请确认库存数量"); +// } +// data.setStock(String.valueOf(stock - replacementDTO.getUseCount())); +// iotThingBaseInfoService.update(data); +// } +// +// EqPartRecordEntity eqPartRecordEntity = new EqPartRecordEntity(); +// eqPartRecordEntity.setEqPartId(replacementDTO.getThingId()); +// eqPartRecordEntity.setUseCount(String.valueOf(replacementDTO.getUseCount())); +// eqPartRecordEntity.setEqOperateId(eqWxId); +// eqPartRecordEntity.setType("0"); +// eqPartRecordEntityList.add(eqPartRecordEntity); +// } +// +// //更新部件更换记录 +// eqPartRecordService.insertBatch(eqPartRecordEntityList); +// } + + public void updateStock(List replaceMentUseDTOList) { + List eqPartRecordEntityList = new ArrayList<>(); + for (ReplaceMentUseDTO replaceMentUseDTO : replaceMentUseDTOList) { + //校验库存,扣除库存 + IotThingBaseInfoDTO data = iotThingBaseInfoService.getByThingsIdForUpdate(replaceMentUseDTO.getThingId()); + //如果大于库存,不可操作 + if(Objects.isNull(data)){ + throw new SysException("备件不存在"); + } + if(StringUtils.isBlank(data.getStock())){ + throw new SysException("备件库存不足,请确认库存数量"); + } + Integer stock = Integer.parseInt(data.getStock()); + if (replaceMentUseDTO.getTotalUse() > Integer.parseInt(data.getStock())) { + throw new SysException("备件库存不足,请确认库存数量"); + } + data.setStock(String.valueOf(stock - replaceMentUseDTO.getTotalUse())); + iotThingBaseInfoService.updateDto(data); + + List wxUseDTOList = replaceMentUseDTO.getWxUseDTOList(); + if (CollectionUtil.isNotEmpty(wxUseDTOList)) { + for (WxUseDTO wxUseDTO : wxUseDTOList) { + EqPartRecordEntity eqPartRecordEntity = new EqPartRecordEntity(); + eqPartRecordEntity.setEqPartId(replaceMentUseDTO.getThingId()); + eqPartRecordEntity.setUseCount(String.valueOf(wxUseDTO.getUseCount())); + eqPartRecordEntity.setEqOperateId(wxUseDTO.getEqWxId()); + eqPartRecordEntity.setType("0"); + + eqPartRecordEntityList.add(eqPartRecordEntity); + } + } + } + //更新部件更换记录 + eqPartRecordService.saveBatch(eqPartRecordEntityList); + } + + public void savePlan(EqWxPlanAddDTO dto) { + List thingIds = dto.getThingIds(); + List replacementDTOS = dto.getReplacements(); + + // 获取单号 生成单号同步锁 + QueryWrapper wrapper = new QueryWrapper(); + wrapper.orderBy("wx_no",false); + EqWxPlanEntity eqWxPlanEntity = mapper.selectOneExt(wrapper); + // 上一条数据的单号 第一条单号为数据库查询出来的 + 1 ,其它的单号为第一条单号的自增 +1 + String lastNo = ""; + + Map totalUse = new HashMap<>(); + Map> wxUseDtoMap = new HashMap<>(); + + for (String thingId : thingIds) { +// Long id = IdWorker.getId(); +// dto.setId(id); + dto.setThingId(Long.parseLong(thingId)); + // 第一个单号用lastNo + if (Objects.isNull(eqWxPlanEntity)) { + eqWxPlanEntity = new EqWxPlanEntity(); + } + lastNo = SerialNumberUnit.generateNumber("WX", eqWxPlanEntity.getWxNo()); + dto.setWxNo(lastNo); + saveDto(dto); + BeanUtils.copyProperties(dto, eqWxPlanEntity); + if (CollectionUtil.isEmpty(replacementDTOS)) { + continue; + } + // 新增时只有当状态为已完成才去计算备件总使用数 + if ("2".equals(dto.getWxStatus())) { + replacementDTOS.forEach(replacementDTO -> { + replacementDTO.setWxPlanId(dto.getId()); + + WxUseDTO wxUseDTO = new WxUseDTO(); + wxUseDTO.setEqWxId(dto.getId()); + wxUseDTO.setUseCount(replacementDTO.getUseCount()); + List temp = new ArrayList<>(); + if (wxUseDtoMap.containsKey(replacementDTO.getThingId())) { + temp = wxUseDtoMap.get(replacementDTO.getThingId()); + temp.add(wxUseDTO); + } else { + temp.add(wxUseDTO); + } + wxUseDtoMap.put(replacementDTO.getThingId(), temp); + + if (totalUse.containsKey(replacementDTO.getThingId())) { + totalUse.put(replacementDTO.getThingId(), totalUse.get(replacementDTO.getThingId()) + (Objects.isNull(replacementDTO.getUseCount()) ? 0 : replacementDTO.getUseCount())); + } else { + totalUse.put(replacementDTO.getThingId(), (Objects.isNull(replacementDTO.getUseCount()) ? 0 : replacementDTO.getUseCount())); + } + }); + }else { + replacementDTOS.forEach(replacementDTO -> { + replacementDTO.setWxPlanId(dto.getId()); + }); + } + eqWxReplacementService.saveDto(ConvertUtils.sourceToTarget(replacementDTOS, EqWxReplacementEntity.class)); + } + + + // 重新赋值备件的各个使用总数 + if ("2".equals(dto.getWxStatus()) && CollectionUtil.isNotEmpty(replacementDTOS)) { + List replaceMentUseDTOList = new ArrayList<>(); + for (Map.Entry entry : totalUse.entrySet()) { + ReplaceMentUseDTO replaceMentUseDTO = new ReplaceMentUseDTO(); + replaceMentUseDTO.setThingId(entry.getKey()); + replaceMentUseDTO.setTotalUse(entry.getValue()); + + replaceMentUseDTO.setWxUseDTOList(wxUseDtoMap.get(entry.getKey())); + + replaceMentUseDTOList.add(replaceMentUseDTO); + } + + updateStock(replaceMentUseDTOList); + } + } + + + @Override + public List searchRepairPlanByBxId(Long eqBxId) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("eq_bx_id", eqBxId); + + List eqWxPlanEntities = mapper.selectListExt(wrapper); + return eqWxPlanEntities; + } + + + @Override + public String getDictValue(Map dictMap, String dictType, String curValue) { + if (CollectionUtil.isEmpty(dictMap)) { + return ""; + } + + //调整 +// List> temp = (List>) dictMap.get(dictType); + List temp = new ArrayList<>(); + Object obj = dictMap.get(dictType); + temp= JSON.parseArray(JSON.toJSONString(obj),SysDictDataEntity.class); + + if (CollectionUtil.isEmpty(temp)) { + return ""; + } + Map dictObject = new HashMap<>(); + for (SysDictDataEntity dictData:temp){ + dictObject.put(dictData.getDictValue(),dictData.getDictLabel()); + } + +// Map dictObject = temp.stream().collect(Collectors.toMap(item -> String.valueOf(item.get("dictValue")), item -> String.valueOf(item.get("dictLabel")))); + String faultType = StringUtils.isNotBlank(dictObject.get(curValue)) ? dictObject.get(curValue) : "当前字典值不存在"; + return faultType; + } + + @Override + public Long getWxCountByBxId(Long eqBxId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("eq_bx_id", eqBxId); + + return mapper.selectCountByQuery(wrapper); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxReplacementServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxReplacementServiceImpl.java new file mode 100644 index 0000000..6038b19 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxReplacementServiceImpl.java @@ -0,0 +1,68 @@ +package com.thing.eq.eqbxwx.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.dto.EqWxReplacementDTO; +import com.thing.eq.eqbxwx.entity.EqWxReplacementEntity; +import com.thing.eq.eqbxwx.mapper.EqWxReplacementMapper; +import com.thing.eq.eqbxwx.service.EqWxReplacementService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 维修备件 + * + * @author zy zy@xx.com + * @since 3.0 2021-10-20 + */ +@Service +public class EqWxReplacementServiceImpl extends BaseServiceImpl implements EqWxReplacementService { + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void deleteWxReplacementByWxPlanId(Long wxPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("wx_plan_id", wxPlanId); + mapper.deleteByQuery(wrapper); + } + + @Override + public void deleteWxReplacementByWxPlanId(List wxPlanIds) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("wx_plan_id", wxPlanIds); + mapper.deleteByQuery(wrapper); + } + + @Override + public List getWxReplacementByWxPlanId(Long wxPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("wx_plan_id", wxPlanId); + + List entities = mapper.selectListExt(wrapper); + + List dtoList = ConvertUtils.sourceToTarget(entities, EqWxReplacementDTO.class); + + if (CollectionUtil.isNotEmpty(dtoList)) { + dtoList.forEach(replacementDTO -> { + iotThingBaseInfoService.getThingInfo(replacementDTO, replacementDTO.getThingId(), null, "0", ""); + }); + } + + return dtoList; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxServiceImpl.java new file mode 100644 index 0000000..276b50a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqbxwx/service/impl/EqWxServiceImpl.java @@ -0,0 +1,30 @@ +package com.thing.eq.eqbxwx.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.dto.EqWxDTO; +import com.thing.eq.eqbxwx.entity.EqWxEntity; +import com.thing.eq.eqbxwx.mapper.EqWxMapper; +import com.thing.eq.eqbxwx.service.EqWxService; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 设备维修 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqWxServiceImpl extends BaseServiceImpl implements EqWxService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByController.java new file mode 100644 index 0000000..694cf23 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByController.java @@ -0,0 +1,283 @@ +package com.thing.eq.eqby.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqby.dto.EqByDTO; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.dto.EqInfo; +import com.thing.eq.eqby.excel.EqByExcel; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqby.service.EqByService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.*; + + +/** +* 设备保养 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqby") +@Tag(name="设备-设备保养") +public class EqByController { + @Autowired + private EqByService eqByService; + + @Autowired + private EqByPlanService eqByPlanService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "byNo", description = "保养单号"), + @Parameter(name = "startTime", description = "保养开始时间"), + @Parameter(name = "endTime", description = "保养结束时间"), + @Parameter(name = "relationTypeId", description = "结构类型id") + }) +// @RequiresPermissions("equipment:upkeepRecords:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqByService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}/{relationTypeId}") + @Operation(summary = "信息") +// @RequiresPermissions("eqby:eqby:info") + public Result get(@PathVariable("id") Long id, @PathVariable("relationTypeId") String relationTypeId){ + EqByDTO data = eqByService.getEqByInfo(id,relationTypeId); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:upkeepPlan:execution") + public Result save(@RequestBody EqByDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + //更新保养计划的计划时间 + EqByPlanDTO eqByPlanDTO = eqByPlanService.getByIdAs(dto.getEqByPlanId(),EqByPlanDTO.class); + if (Objects.isNull(eqByPlanDTO)) { + throw new SysException("保养计划不存在"); + } + if (StringUtils.isBlank(eqByPlanDTO.getByNo())) { + throw new SysException("当前保养计划不存在单号,无法执行"); + } + + if (StringUtils.isBlank(eqByPlanDTO.getLoopType())) { + throw new SysException("循环类型不能为空"); + } + if (StringUtils.equals("1", eqByPlanDTO.getPlanStatus())) { + throw new SysException("计划已经停用,不可执行"); + } + if (eqByPlanDTO.getPlanEndTime() !=null){ +// if (System.currentTimeMillis() > eqByPlanDTO.getPlanEndTime().getTime() ){ +// throw new SysException("计划时间已经截止,不可执行"); +// } + if (eqByPlanDTO.getByPlanTime().getTime() > eqByPlanDTO.getPlanEndTime().getTime()){ + throw new SysException("计划开始时间大于结束时间,不可执行"); + } + } + saveInfo(dto, eqByPlanDTO); + + return new Result(); + } + + public synchronized void saveInfo(EqByDTO dto, EqByPlanDTO eqByPlanDTO) { + eqByService.saveEqBy(dto, eqByPlanDTO); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:upkeepRecords:update") + public Result update(@RequestBody EqByDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqByService.updateEqBy(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("equipment:upkeepRecords:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqByService.deleteEqBy(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name = "byNo", value = "保养单号", paramType = "query", dataType="String"), +// @Parameter(name = "startTime", value = "保养开始时间", paramType = "query", dataType="String"), +// @Parameter(name = "endTime", value = "保养结束时间", paramType = "query", dataType="String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query",required = true, dataType="String") +// }) +// @RequiresPermissions("equipment:upkeepRecords:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqByService.listExport(params); + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqByExcel eqByExcel : list ){ + if (idList.contains(eqByExcel.getId())){ + newList.add(eqByExcel); + } + } + }else{ + newList.addAll(list); + } + ExcelUtils.exportExcel(newList,null, "设备保养", EqByExcel.class,"设备保养.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "设备保养.xls", "设备保养", newList, EqByExcel.class); + } + + @GetMapping("list") + @Operation(summary = "测试") + @LogOperation("测试") + public JSONObject getList() { + List infos = new ArrayList<>(); + EqInfo info = new EqInfo(); + info.setName("成品车间"); + info.setEqTypeName("底数"); + info.setCode("1111"); + info.setStandard("1222"); + info.setEqTypeId("1122"); + info.setUseDeptName("1233"); + infos.add(info); + EqInfo info2 = new EqInfo(); + info2.setName("成品车间"); + info2.setEqTypeName("抄见"); + info2.setCode("2222"); + info2.setStandard("2333"); + info2.setEqTypeId("2233"); + info2.setUseDeptName("2344"); + infos.add(info2); + EqInfo info3 = new EqInfo(); + info3.setName("成品车间"); + info3.setEqTypeName("实用"); + info3.setCode("1111"); + info3.setStandard("1111"); + info3.setEqTypeId("1111"); + info3.setUseDeptName("1111"); + infos.add(info3); + + EqInfo info11 = new EqInfo(); + info11.setName("芯片车间"); + info11.setEqTypeName("底数"); + info11.setCode("3333"); + info11.setStandard("3444"); + info11.setEqTypeId("3344"); + info11.setUseDeptName("3455"); + infos.add(info11); + EqInfo info12 = new EqInfo(); + info12.setName("芯片车间"); + info12.setEqTypeName("抄见"); + info12.setCode("4444"); + info12.setStandard("4555"); + info12.setEqTypeId("4455"); + info12.setUseDeptName("4566"); + infos.add(info12); + EqInfo info13 = new EqInfo(); + info13.setName("芯片车间"); + info13.setEqTypeName("实用"); + info13.setCode("1111"); + info13.setStandard("1111"); + info13.setEqTypeId("1111"); + info13.setUseDeptName("1111"); + infos.add(info13); + + EqInfo info31 = new EqInfo(); + info31.setName("冷却系统"); + info31.setEqTypeName("底数"); + info31.setCode("142"); + info31.setStandard("242"); + info31.setEqTypeId("342"); + info31.setUseDeptName("442"); + infos.add(info31); + EqInfo info32 = new EqInfo(); + info32.setName("冷却系统"); + info32.setEqTypeName("抄见"); + info32.setCode("158"); + info32.setStandard("258"); + info32.setEqTypeId("358"); + info32.setUseDeptName("458"); + infos.add(info32); + EqInfo info33 = new EqInfo(); + info33.setName("冷却系统"); + info33.setEqTypeName("实用"); + info33.setCode("16"); + info33.setStandard("16"); + info33.setEqTypeId("16"); + info33.setUseDeptName("16"); + infos.add(info33); + + EqInfo info21 = new EqInfo(); + info21.setName("生活用水"); + info21.setEqTypeName("底数"); + info21.setCode("666"); + info21.setStandard("777"); + info21.setEqTypeId("888"); + info21.setUseDeptName("999"); + infos.add(info21); + EqInfo info22 = new EqInfo(); + info22.setName("生活用水"); + info22.setEqTypeName("抄见"); + info22.setCode("678"); + info22.setStandard("788"); + info22.setEqTypeId("899"); + info22.setUseDeptName("1234"); + infos.add(info22); + EqInfo info23 = new EqInfo(); + info23.setName("生活用水"); + info23.setEqTypeName("实用"); + info23.setCode("12"); + info23.setStandard("11"); + info23.setEqTypeId("11"); + info23.setUseDeptName("235"); + infos.add(info23); + JSONObject object = new JSONObject(); + object.put("data",infos); + return object; + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByDetailController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByDetailController.java new file mode 100644 index 0000000..976b153 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByDetailController.java @@ -0,0 +1,119 @@ +package com.thing.eq.eqby.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.checkresult.excel.CheckResultExcel; +import com.thing.eq.eqby.dto.EqByDetailDTO; +import com.thing.eq.eqby.excel.EqByDetailExcel; +import com.thing.eq.eqby.service.EqByDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 保养明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping("eqbydetail/eqbydetail") +@Tag(name="设备-保养明细") +public class EqByDetailController { + @Autowired + private EqByDetailService eqByDetailService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("eqbydetail:eqbydetail:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqByDetailService.getPageData(params,EqByDetailDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqbydetail:eqbydetail:info") + public Result get(@PathVariable("id") Long id){ + EqByDetailDTO data = eqByDetailService.getByIdAs(id,EqByDetailDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("eqbydetail:eqbydetail:save") + public Result save(@RequestBody EqByDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqByDetailService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("eqbydetail:eqbydetail:update") + public Result update(@RequestBody EqByDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqByDetailService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("eqbydetail:eqbydetail:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqByDetailService.deleteByByIds(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("eqbydetail:eqbydetail:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqByDetailService.listAs(params,EqByDetailDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqByDetailExcel.class); + ExcelUtils.exportExcel(list1,null, "保养明细", EqByDetailExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, null, "保养明细", list, EqByDetailExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByPlanController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByPlanController.java new file mode 100644 index 0000000..acd4724 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByPlanController.java @@ -0,0 +1,200 @@ +package com.thing.eq.eqby.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.dto.EqByPlanStatusDTO; +import com.thing.eq.eqby.excel.EqByPlanExcel; +import com.thing.eq.eqby.service.EqByPlanService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.*; + + +/** +* 设备保养计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqbyplan") +@Tag(name="设备-设备保养计划") +public class EqByPlanController { + @Autowired + private EqByPlanService eqByPlanService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = + "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "byStatus", description = "保养状态0未保养,1已保养"), + @Parameter(name = "startTime", description = "保养计划开始时间"), + @Parameter(name = "endTime", description = "保养计划结束时间"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + @Parameter(name = "byNo", description = "保养计划单号"), + @Parameter(name = "relationTypeId", description = "结构类型id"), + @Parameter(name = "checkUserId", description = "保养人员id"), + }) +// @RequiresPermissions("equipment:upkeepPlan:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqByPlanService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}/{relationTypeId}") + @Operation(summary = "保养计划信息") +// @RequiresPermissions("eqbyplan:eqbyplan:info") + public Result get(@PathVariable("id") Long id,@PathVariable("relationTypeId") String relationTypeId){ + EqByPlanDTO data = eqByPlanService.getById(id,relationTypeId); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存保养计划") + @LogOperation("保存保养计划") +// @RequiresPermissions("equipment:upkeepPlan:add") + public Result save(@RequestBody EqByPlanDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (CollectionUtil.isEmpty(dto.getThingsIds())) { + throw new SysException("设备不能为空"); + } + if (StringUtils.isNotBlank(dto.getRemindTime()) && StringUtils.isNotBlank(dto.getRemindTimeUnit()) + && StringUtils.isNotBlank(dto.getLoopCycle()) && StringUtils.isNotBlank(dto.getCycleUnit())) { + if (StringUtils.equals(dto.getRemindTimeUnit(),dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime()) >= Long.parseLong(dto.getLoopCycle())) { + throw new SysException("提醒时间不可大于循环周期"); + } + }else{ + if (StringUtils.equals("day",dto.getRemindTimeUnit()) && StringUtils.equals("hour",dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime())*24 >= Long.parseLong(dto.getLoopCycle())){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + if (StringUtils.equals("hour",dto.getRemindTimeUnit()) && StringUtils.equals("day",dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime()) >= Long.parseLong(dto.getLoopCycle())*24){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + } + } + saveInfo(dto); + return new Result(); + } + + public synchronized void saveInfo(EqByPlanDTO dto){ + eqByPlanService.saveByPlan(dto); + } + + @PostMapping("/update/status") + @Operation(summary = "更新保养计划状态(计划启用或停用)") + @LogOperation("更新保养计划状态") +// @RequiresPermissions("equipment:upkeepPlan:disableOrEnable") + public Result savePlanStatus(@RequestBody EqByPlanStatusDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (StringUtils.equals("1",dto.getPlanStatus())){ + eqByPlanService.updateByPlanStatus(dto.getId(),dto.getPlanStatus()); + }else{ + EqByPlanDTO data = eqByPlanService.getByIdAs(dto.getId(),EqByPlanDTO.class); + if (data != null ){ + data.setPlanStatus(dto.getPlanStatus()); + Date date = new Date(); + Date planStartDate = data.getByPlanTime(); + String dateStr = DateTimeUtils.format(date,DateTimeUtils.DATE_PATTERN_STR); + String dateStr1 = " 00:00:00"; + if (planStartDate != null){ + dateStr1 = DateTimeUtils.format(planStartDate,DateTimeUtils.DATE_TIME_PATTERN_STR).substring(10,19); + } + Date newPlanStartDate = DateTimeUtils.stringToDate(dateStr+dateStr1,DateTimeUtils.DATE_TIME_PATTERN_STR); + data.setByPlanTime(newPlanStartDate); + eqByPlanService.updateDto(data); + } + } + return new Result(); + } + + @PutMapping + @Operation(summary = "修改保养计划") + @LogOperation("修改保养计划") +// @RequiresPermissions("equipment:upkeepPlan:update") + public Result update(@RequestBody EqByPlanDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqByPlanService.updateByPlan(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除保养计划") + @LogOperation("删除保养计划") +// @RequiresPermissions("equipment:upkeepPlan:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqByPlanService.deleteByIds(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name = "byStatus", value = "保养状态0未保养,1已保养", paramType = "query", dataType="String"), +// @Parameter(name = "startTime", value = "保养计划开始时间", paramType = "query", dataType="String"), +// @Parameter(name = "endTime", value = "保养计划结束时间", paramType = "query", dataType="String"), +// @Parameter(name = "eqIds", value = "设备id,多个用,隔开", paramType = "query", dataType = "String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query",required = true, dataType="String"), +// @Parameter(name = "byNo", value = "保养计划单号", paramType = "query", dataType = "String") +// }) +// @RequiresPermissions("equipment:upkeepPlan:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqByPlanService.listExport(params); + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqByPlanExcel eqByPlanExcel : list ){ + if (idList.contains(eqByPlanExcel.getId())){ + newList.add(eqByPlanExcel); + } + } + }else{ + newList.addAll(list); + } + ExcelUtils.exportExcel(newList,null, "设备保养计划", EqByPlanExcel.class,"设备保养计划.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "设备保养计划.xls", "设备保养计划", newList, EqByPlanExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateController.java new file mode 100644 index 0000000..f2ce9fc --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateController.java @@ -0,0 +1,146 @@ +package com.thing.eq.eqby.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqby.dto.EqByTemplateDTO; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.excel.EqByDetailExcel; +import com.thing.eq.eqby.excel.EqByTemplateDetailExcel; +import com.thing.eq.eqby.excel.EqByTemplateExcel; +import com.thing.eq.eqby.service.EqByTemplateDetailService; +import com.thing.eq.eqby.service.EqByTemplateService; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; +import java.util.Map; + + +/** +* 保养模板 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqbytemplate") +@Tag(name="设备-保养模板") +public class EqByTemplateController { + @Autowired + private EqByTemplateService eqByTemplateService; + @Autowired + private EqByTemplateDetailService eqByTemplateDetailService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "eqTypeId", description = "设备类型id"), + @Parameter(name = "name", description = "模板名称或部位名称") + }) +// @RequiresPermissions("equipment:MaintenanceParts:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqByTemplateService.page(params); + List datas =page.getList(); + for (EqByTemplateDTO dto:datas){ + List dataDeatils = eqByTemplateDetailService.getDetails(dto.getId()); + dto.setDatas(dataDeatils); + } + return new Result>().ok(page); + } + + @GetMapping("/list") + @Operation(summary = "获取所有模板列表") +// @RequiresPermissions("eqbytemplate:eqbytemplate:info") + public Result> getList(){ + List datas = eqByTemplateService.getList();; + for (EqByTemplateDTO dto:datas){ + List dataDeatils = eqByTemplateDetailService.getDetails(dto.getId()); + dto.setDatas(dataDeatils); + } + + return new Result>().ok(datas); + } + + @GetMapping("{id}") + @Operation(summary = "保养模板信息") +// @RequiresPermissions("eqbytemplate:eqbytemplate:info") + public Result get(@PathVariable("id") Long id){ + EqByTemplateDTO data = eqByTemplateService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存保养模板") + @LogOperation("保存保养模板") +// @RequiresPermissions("equipment:MaintenanceParts:add") + public Result save(@RequestBody EqByTemplateDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setTenantCode(SecurityUser.getTenantCode()); + dto.setCreateDate(new Date()); + eqByTemplateService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改保养模板") + @LogOperation("修改保养模板") +// @RequiresPermissions("equipment:MaintenanceParts:update") + public Result update(@RequestBody EqByTemplateDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqByTemplateService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除保养模板") + @LogOperation("删除保养模板") +// @RequiresPermissions("equipment:MaintenanceParts:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqByTemplateService.delete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出保养模板") + @LogOperation("导出保养模板") +// @RequiresPermissions("equipment:MaintenanceParts:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqByTemplateService.listAs(params,EqByTemplateDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqByTemplateExcel.class); + ExcelUtils.exportExcel(list1,null, "保养模板", EqByTemplateExcel.class,"保养模板.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "保养模板", list, EqByTemplateExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateDetailController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateDetailController.java new file mode 100644 index 0000000..49f9656 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/EqByTemplateDetailController.java @@ -0,0 +1,127 @@ +package com.thing.eq.eqby.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.excel.EqByTemplateDetailExcel; +import com.thing.eq.eqby.service.EqByTemplateDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 保养模板明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqbytemplatedetail") +@Tag(name="设备-保养模板明细") +public class EqByTemplateDetailController { + @Autowired + private EqByTemplateDetailService eqByTemplateDetailService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqByTemplateDetailService.getPageData(params,EqByTemplateDetailDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("details/{templateId}") + @Operation(summary = "根据模板id获取模板明细") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:info") + public Result> getDetails(@PathVariable("templateId") Long templateId){ + List datas = eqByTemplateDetailService.getDetails(templateId); + + return new Result>().ok(datas); + } + + @GetMapping("{id}") + @Operation(summary = "保养模板明细信息") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:info") + public Result get(@PathVariable("id") Long id){ + EqByTemplateDetailDTO data = eqByTemplateDetailService.getByIdAs(id,EqByTemplateDetailDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存保养模板明细") + @LogOperation("保存保养模板明细") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:save") + public Result save(@RequestBody EqByTemplateDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqByTemplateDetailService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改保养模板明细") + @LogOperation("修改保养模板明细") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:update") + public Result update(@RequestBody EqByTemplateDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqByTemplateDetailService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除保养模板明细") + @LogOperation("删除保养模板明细") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqByTemplateDetailService.deleteByTemplateIds(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("eqbytemplatedetail:eqbytemplatedetail:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqByTemplateDetailService.listAs(params,EqByTemplateDetailDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqByTemplateDetailExcel.class); + ExcelUtils.exportExcel(list1,null, "保养模板明细", EqByTemplateDetailExcel.class,"保养模板明细.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "保养模板明细", list, EqByTemplateDetailExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/controller/SendMailController.java b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/SendMailController.java new file mode 100644 index 0000000..5e80649 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/controller/SendMailController.java @@ -0,0 +1,41 @@ +package com.thing.eq.eqby.controller; + + +import cn.hutool.extra.mail.MailUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.eq.task.EqRemindRecordTask; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + + +/** +* 发送邮件 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping("/sendmail") +@Tag(name="设备-发送邮件") +public class SendMailController { + @Autowired + private EqRemindRecordTask eqRemindRecordTask; + @GetMapping + @Operation(summary = "发送邮件") + @LogOperation("发送邮件") + public void sendMail() { + MailUtil.send("2456101676@qq.com","到期提醒","有计划即将到期,请及时处理",false,null); + } + + @GetMapping("planremindrecord") + @Operation(summary = "延期提醒告警(计划)") + @LogOperation("延期提醒告警(计划)") + public void sendMail4Plan() { + eqRemindRecordTask.sendMessage(); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDTO.java new file mode 100644 index 0000000..c4c66a3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDTO.java @@ -0,0 +1,83 @@ +package com.thing.eq.eqby.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.dto.BaseDTO; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 设备保养 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备保养") +public class EqByDTO extends BaseDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "保养班组") + private String byTeam; + @Schema(description = "保养级别") + private String byLevel; + @Schema(description = "是否停机;0不停机 1停机,默认0") + private String shutdown; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "工作描述") + private String workDesc; + @Schema(description = "设备保养计划id") + private Long eqByPlanId; + @Schema(description = "保养状态") + private String byStatus; + @Schema(description = "保养单号") + private String byNo; + @Schema(description = "停机时长(单位分钟)") + private String shutdownTime; + @Schema(description = "保养开始时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date byStartTime; + @Schema(description = "保养结束时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date byEndTime; + @Schema(description = "保养费用") + private String byFee; + @Schema(description = "保养人员") + private String byUser; + @Schema(description = "保养时长") + private String byTime; + + @Schema(description = "保养部位明细记录") + private List eqByDetailDTOList; + @Schema(description = "设备信息") + private EqInfo thingsInfo; + + @Schema(description = "部件更换list") + private List thingParts; + @Schema(description = "部件更换信息") + private List thingPartInfos; + + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "修改时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDetailDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDetailDTO.java new file mode 100644 index 0000000..8247ec4 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByDetailDTO.java @@ -0,0 +1,45 @@ +package com.thing.eq.eqby.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 保养明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "保养明细") +public class EqByDetailDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "保养id") + private Long byId; + @Schema(description = "部位名称") + private String name; + @Schema(description = "描述") + private String description; + @Schema(description = "编号") + private String bwNo; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "是否保养,0未保养,1保养,默认1") + private String byYesno; + @Schema(description = "保养模板id") + private Long byTemplateId; + @Schema(description = "保养部位id") + private Long byTemplateDetailId; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanDTO.java new file mode 100644 index 0000000..4de2e77 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanDTO.java @@ -0,0 +1,97 @@ +package com.thing.eq.eqby.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 设备保养计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备保养计划") +public class EqByPlanDTO extends BaseDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "保养班组") + private String byTeam; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "保养状态 0待保养,1已保养") + private String byStatus; + @Schema(description = "保养单号") + private String byNo; + @Schema(description = "保养计划时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date byPlanTime; + @Schema(description = "保养级别") + private String byLevel; + @Schema(description = "保养人员") + private String byUser; + @Schema(description = "保养描述") + private String byDesc; + @Schema(description = "设备id") + private Long thingsId; + @Schema(description = "设备idlist") + private List thingsIds; + @Schema(description = "设备信息") + private EqInfo thingsInfo; + @Schema(description = "保养模板id(多个,隔开)") + private String byTemplateId; + @Schema(description = "保养模板信息") + private List byTemplateInfos; + @Schema(description = "部件更换list") + private List thingParts; + @Schema(description = "部件更换信息") + private List thingPartInfos; + + @Schema(description = "提醒时间(提前多少天提醒)") + private String remindTime; + + @Schema(description = "计划状态,是否停用0启用,1停用") + private String planStatus; + + @Schema(description = "循环类型,0单次 1多次") + private String loopType; + + @Schema(description = "循环周期(天)") + private String loopCycle; + + @Schema(description = "保养计划截止时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date planEndTime; + + @Schema(description = "循环周期单位(hour/day)") + private String cycleUnit; + + @Schema(description = "提醒时间单位(hour/day)") + private String remindTimeUnit; + + private Long deptId; + + private Long tenantCode; + + private String isRemind; + + @Schema(description = "用户组id") + private Long groupId; + @Schema(description = "用户组名称,展示用") + private String groupName; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartDTO.java new file mode 100644 index 0000000..98cf36b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartDTO.java @@ -0,0 +1,40 @@ +package com.thing.eq.eqby.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 保养计划更换 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "保养计划部件更换") +public class EqByPlanPartDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "更换数量") + private String useCount; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "设备保养id") + private Long eqByPlanId; + @Schema(description = "部件id") + private Long eqPartId; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartInfoDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartInfoDTO.java new file mode 100644 index 0000000..e8199c1 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanPartInfoDTO.java @@ -0,0 +1,38 @@ +package com.thing.eq.eqby.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 保养计划更换 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "保养计划部件更换") +public class EqByPlanPartInfoDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "更换数量") + private String useCount; + @Schema(description = "部件编号") + private String code; + @Schema(description = "部件名称") + private String name; + @Schema(description = "部件规格") + private String standard; + @Schema(description = "部件ID") + private String eqPartId; + + @Schema(description = "库存") + private String stock; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanStatusDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanStatusDTO.java new file mode 100644 index 0000000..879ab60 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByPlanStatusDTO.java @@ -0,0 +1,29 @@ +package com.thing.eq.eqby.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 设备保养 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备保养状态") +public class EqByPlanStatusDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "计划状态,是否停用0启用,1停用") + private String planStatus; + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDTO.java new file mode 100644 index 0000000..7584f0c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDTO.java @@ -0,0 +1,49 @@ +package com.thing.eq.eqby.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 保养模板 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "保养模板") +public class EqByTemplateDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "保养模板") + @NotBlank(message = "保养模板不能为空") + private String name; + @Schema(description = "保养工具") + private String byTool; + @Schema(description = "保养目的") + private String byPurpose; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "描述") + private String description; + @Schema(description = "设备类型id") + private Long deviceTypeId; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "模板明细") + private List datas; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDetailDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDetailDTO.java new file mode 100644 index 0000000..8375bd9 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqByTemplateDetailDTO.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqby.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +import java.io.Serializable; +import java.util.Date; + +/** +* 保养模板明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "保养模板明细") +public class EqByTemplateDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "保养模板id") + private Long byTemplateId; + @Schema(description = "部位名称") + @NotBlank(message = "部位名称不能为空") + private String name; + @Schema(description = "描述") + private String description; + @Schema(description = "编号") + private String bwNo; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqInfo.java b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqInfo.java new file mode 100644 index 0000000..0a581ed --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/dto/EqInfo.java @@ -0,0 +1,39 @@ +package com.thing.eq.eqby.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.io.Serializable; + +/** +* 设备信息 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备信息") +public class EqInfo implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "物编号") + private String code; + @Schema(description = "设备名称") + private String name; + @Schema(description = "设备规格") + private String standard; + @Schema(description = "设备类型Id") + private String eqTypeId; + @Schema(description = "设备类型") + private String eqTypeName; + @Schema(description = "使用部门id") + private Long useDeptId; + @Schema(description = "使用部门名称") + private String useDeptName; + @Schema(description = "是否报废 0未报废 1已报废") + private String scrap; + @Schema(description = "报废是否处理 0未处理 1已处理") + private String scrapDisposal; + @Schema(description = "设备编号") + private String eqCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByDetailEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByDetailEntity.java new file mode 100644 index 0000000..6aa9d0a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByDetailEntity.java @@ -0,0 +1,68 @@ +package com.thing.eq.eqby.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 保养明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by_detail") +public class EqByDetailEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 保养id + */ + private Long byId; + /** + * 部位名称 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 编号 + */ + private String bwNo; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 是否保养,0未保养,1保养,默认1 + */ + private String byYesno; + /** + * 保养模板id + */ + private Long byTemplateId; + /** + * 保养部位id + */ + private Long byTemplateDetailId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByEntity.java new file mode 100644 index 0000000..b83462e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByEntity.java @@ -0,0 +1,103 @@ +package com.thing.eq.eqby.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备保养 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by") +public class EqByEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 保养班组 + */ + private String byTeam; + /** + * 保养级别 + */ + private String byLevel; + /** + * 是否停机;0不停机 1停机,默认0 + */ + private String shutdown; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 工作描述 + */ + private String workDesc; + /** + * 设备保养计划id + */ + private Long eqByPlanId; + /** + * 保养状态 + */ + private String byStatus; + /** + * 保养单号 + */ + private String byNo; + /** + * 停机时长(单位分钟) + */ + private String shutdownTime; + /** + * 保养开始时间 + */ + private Date byStartTime; + /** + * 保养结束时间 + */ + private Date byEndTime; + /** + * 保养费用 + */ + private String byFee; + /** + * 保养人员 + */ + private String byUser; + /** + * 保养时长 + */ + private String byTime; + /** + * 部门id + */ + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanEntity.java new file mode 100644 index 0000000..2a0b67c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanEntity.java @@ -0,0 +1,127 @@ +package com.thing.eq.eqby.entity; + + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备保养计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by_plan") +public class EqByPlanEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 保养班组 + */ + private String byTeam; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 保养状态 + */ + private String byStatus; + /** + * 保养单号 + */ + private String byNo; + /** + * 保养计划时间 + */ + private Date byPlanTime; + /** + * 保养级别 + */ + private String byLevel; + /** + * 保养人员 + */ + private String byUser; + /** + * 保养描述 + */ + private String byDesc; + /** + * 设备id + */ + private Long thingsId; + /** + * 保养模板id(多个,隔开) + */ + private String byTemplateId; + /** + * 提醒时间(提前多少天提醒) + */ + private String remindTime; + /** + * 计划状态,是否停用0启用,1停用 + */ + private String planStatus; + /** + * 循环类型,0单次 1多次 + */ + private String loopType; + /** + * 循环周期(天) + */ + private String loopCycle; + /** + * 保养计划截止时间 + */ + private Date planEndTime; + /** + * 循环周期单位(hour/day) + */ + private String cycleUnit; + /** + * 提醒时间单位(hour/day) + */ + private String remindTimeUnit; + /** + * 部门id + */ + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 是否已提醒 0未提醒 1已提醒 + */ + private String isRemind; + + /** + * 用户组id + */ + private Long groupId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanPartEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanPartEntity.java new file mode 100644 index 0000000..2726413 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByPlanPartEntity.java @@ -0,0 +1,56 @@ +package com.thing.eq.eqby.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by_plan_part") +public class EqByPlanPartEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 更换数量 + */ + private String useCount; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 设备保养计划id + */ + private Long eqByPlanId; + + /** + * 设备部件id + */ + private Long eqPartId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateDetailEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateDetailEntity.java new file mode 100644 index 0000000..f7a96ad --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateDetailEntity.java @@ -0,0 +1,56 @@ +package com.thing.eq.eqby.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 保养模板明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by_template_detail") +public class EqByTemplateDetailEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 保养模板id + */ + private Long byTemplateId; + /** + * 部位名称 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 编号 + */ + private String bwNo; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateEntity.java new file mode 100644 index 0000000..8d24bff --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/entity/EqByTemplateEntity.java @@ -0,0 +1,69 @@ +package com.thing.eq.eqby.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 保养模板 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_by_template") +public class EqByTemplateEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 保养模板 + */ + private String name; + /** + * 保养工具 + */ + private String byTool; + /** + * 保养目的 + */ + private String byPurpose; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 描述 + */ + private String description; + /** + * 设备类型id + */ + private Long deviceTypeId; + + /** + * 部门id + */ + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByDetailExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByDetailExcel.java new file mode 100644 index 0000000..5e1446b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByDetailExcel.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqby.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 保养明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqByDetailExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "保养id", orderNum= "2") + private Long byId; + @Excel(name = "部位名称", orderNum= "3") + private String name; + @Excel(name = "描述", orderNum= "4") + private String desc; + @Excel(name = "编号", orderNum= "5") + private String bwNo; + @Excel(name = "创建者", orderNum= "6") + private Long creator; + @Excel(name = "创建时间", orderNum= "7") + private Date createDate; + @Excel(name = "更新者", orderNum= "8") + private Long updater; + @Excel(name = "更新时间", orderNum= "9") + private Date updateDate; + @Excel(name = "是否保养,0未保养,1保养,默认1", orderNum= "10") + private String byYesno; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByExcel.java new file mode 100644 index 0000000..eeffa40 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByExcel.java @@ -0,0 +1,50 @@ +package com.thing.eq.eqby.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备保养 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqByExcel{ + @ExcelIgnore() + private Long id; + @Excel(name= "保养单号", orderNum= "1") + private String byNo; + @Excel(name= "设备编号", orderNum= "2") + private String eqCode; + @Excel(name= "设备名称", orderNum= "3") + private String name; + @Excel(name= "规格型号", orderNum= "4") + private String standard; + @Excel(name= "使用部门", orderNum= "5") + private String useDeptName; + @Excel(name= "保养开始时间", orderNum= "6") + private Date byStartTime; + @Excel(name= "保养结束时间", orderNum= "7") + private Date byEndTime; + @Excel(name= "保养费用", orderNum= "8") + private String byFee; + @Excel(name= "保养用时", orderNum= "9") + private String byTime; + @Excel(name= "保养级别", orderNum= "10") + private String byLevel; + @Excel(name= "保养人员", orderNum= "11") + private String byUser; + @Excel(name= "工作描述", orderNum= "12") + private String workDesc; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByPlanExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByPlanExcel.java new file mode 100644 index 0000000..617142d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByPlanExcel.java @@ -0,0 +1,52 @@ +package com.thing.eq.eqby.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备保养计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqByPlanExcel { + @ExcelIgnore() + private Long id; + @Excel(name = "保养单号", orderNum = "1") + private String byNo; + @Excel(name = "保养状态", orderNum = "2") + private String byStatus; + @Excel(name = "设备编号", orderNum = "3") + private String eqCode; + @Excel(name = "设备名称", orderNum = "4") + private String name; + @Excel(name = "使用部门", orderNum = "5") + private String useDeptName; + @Excel(name = "保养计划时间", orderNum = "6") + private Date byPlanTime; + @Excel(name = "时间间隔", orderNum = "7") + private String loopCycle; + @Excel(name = "循环类型", orderNum = "8") + private String loopType; + @Excel(name = "保养级别", orderNum = "9") + private String byLevel; + @Excel(name = "保养人员", orderNum = "10") + private String byUser; + @Excel(name = "保养描述", orderNum = "11") + private String byDesc; + @Excel(name = "计划状态", orderNum = "12") + private String planStatus; + private String cycleUnit; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateDetailExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateDetailExcel.java new file mode 100644 index 0000000..6c95729 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateDetailExcel.java @@ -0,0 +1,40 @@ +package com.thing.eq.eqby.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 保养模板明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqByTemplateDetailExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "保养模板id", orderNum = "2") + private Long byTemplateId; + @Excel(name = "部位名称", orderNum = "3") + private String name; + @Excel(name = "描述", orderNum = "4") + private String desc; + @Excel(name = "编号", orderNum = "5") + private String bwNo; + @Excel(name = "创建者", orderNum = "6") + private Long creator; + @Excel(name = "创建时间", orderNum = "7") + private Date createDate; + @Excel(name = "更新者", orderNum = "8") + private Long updater; + @Excel(name = "更新时间", orderNum = "9") + private Date updateDate; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateExcel.java new file mode 100644 index 0000000..8a46e8a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/excel/EqByTemplateExcel.java @@ -0,0 +1,41 @@ +package com.thing.eq.eqby.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 保养模板 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqByTemplateExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "保养模板", orderNum = "2") + private String name; + @Excel(name = "保养工具", orderNum = "3") + private String byTool; + @Excel(name = "保养目的", orderNum = "4") + private String byPurpose; + @Excel(name = "创建者", orderNum = "5") + private Long creator; + @Excel(name = "创建时间", orderNum = "6") + private Date createDate; + @Excel(name = "更新者", orderNum = "7") + private Long updater; + @Excel(name = "更新时间", orderNum = "8") + private Date updateDate; + @Excel(name = "描述", orderNum = "9") + private String desc; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByDetailMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByDetailMapper.java new file mode 100644 index 0000000..0334d1d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByDetailMapper.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqby.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByDetailDTO; +import com.thing.eq.eqby.entity.EqByDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 保养明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByDetailMapper extends PowerBaseMapper { + + void deleteByById(@Param("byId") Long byId); + + void deleteByByIds(Long[] byIds); + + List getByById(@Param("byId") Long byId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByMapper.java new file mode 100644 index 0000000..24bb575 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByMapper.java @@ -0,0 +1,28 @@ +package com.thing.eq.eqby.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByDTO; +import com.thing.eq.eqby.entity.EqByEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备保养 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByMapper extends PowerBaseMapper { + + List getListData(Map params); + + List getList(Map params); + + int findByRecordCount(@Param("byPlanId") Long byPlanId); + + List findByEqByPlanId(@Param("byPlanId") Long byPlanId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanMapper.java new file mode 100644 index 0000000..d6e90d5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanMapper.java @@ -0,0 +1,38 @@ +package com.thing.eq.eqby.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.entity.EqByPlanEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备保养计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByPlanMapper extends PowerBaseMapper { + + void updateByPlanStatus(@Param("id") Long id, @Param("planStatus") String planStatus); + + List getListData(Map params); + + List getPlanList(@Param("byStatus") String byStatus, @Param("planStatus") String planStatus); + + int getByCountByEqId(@Param("eqId") Long eqId); + + List getList(Map params); + + int findByPlanByTempleteId(@Param("templateId") String templateId); + + void updateIsRemind(@Param("id") Long id, @Param("isRemind") String isRemind); + + List getPlanListByPlanStatusAndType(@Param("planStatus") String planStatus); + + List getByGroupId(@Param("id") Long id, @Param("tenantCode") Long tenantCode); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanPartMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanPartMapper.java new file mode 100644 index 0000000..3e0d9a8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByPlanPartMapper.java @@ -0,0 +1,27 @@ +package com.thing.eq.eqby.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqby.entity.EqByPlanPartEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 部件使用记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByPlanPartMapper extends PowerBaseMapper { + + void deleteByEqByPlanId(@Param("eqByPlanId") Long eqByPlanId); + + void deleteByEqByPlanIds(Long[] eqByPlanIds); + + List getByEqByPlanId(@Param("eqByPlanId") Long eqByPlanId); + + int getByCountByEqId(@Param("eqId") Long eqId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateDetailMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateDetailMapper.java new file mode 100644 index 0000000..caacfe3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateDetailMapper.java @@ -0,0 +1,27 @@ +package com.thing.eq.eqby.mapper; + + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.entity.EqByTemplateDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 保养模板明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByTemplateDetailMapper extends PowerBaseMapper { + + List getDetails(@Param("templateId") Long templateId); + + void deleteByTemplateId(@Param("templateId") Long templateId); + + void deleteByTemplateIds(Long[] byTemplateIds); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateMapper.java new file mode 100644 index 0000000..babb5ea --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/mapper/EqByTemplateMapper.java @@ -0,0 +1,30 @@ +package com.thing.eq.eqby.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByTemplateDTO; +import com.thing.eq.eqby.entity.EqByTemplateEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 保养模板 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqByTemplateMapper extends PowerBaseMapper { + + List getList(); + + List getListData(Map params); + + int getTemplateByName(@Param("name") String name, @Param("tenantCode") Long tenantCode); + + int getTemplateByNameNoId(@Param("name") String name, @Param("id") Long id, @Param("tenantCode") Long tenantCode); + + EqByTemplateDTO getByName(String name); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByDetailService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByDetailService.java new file mode 100644 index 0000000..e5b5599 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByDetailService.java @@ -0,0 +1,23 @@ +package com.thing.eq.eqby.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByDetailDTO; +import com.thing.eq.eqby.entity.EqByDetailEntity; + +import java.util.List; + +/** + * 保养明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByDetailService extends IBaseService { + + void deleteByById(Long id); + + void deleteByByIds(Long[] ids); + + List getByById(Long id); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanPartService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanPartService.java new file mode 100644 index 0000000..719678f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanPartService.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqby.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByPlanPartDTO; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqby.entity.EqByPlanPartEntity; + +import java.util.List; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByPlanPartService extends IBaseService { + + void deleteByEqByPlanId(Long eqByPlanId); + + void deleteByEqByPlanIds(Long[] ids); + + List getByEqByPlanId(Long id); + + int getByCountByEqId(Long id); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanService.java new file mode 100644 index 0000000..7aae9ac --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByPlanService.java @@ -0,0 +1,47 @@ +package com.thing.eq.eqby.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.entity.EqByPlanEntity; +import com.thing.eq.eqby.excel.EqByPlanExcel; + +import java.util.List; +import java.util.Map; + +/** + * 设备保养计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByPlanService extends IBaseService { + + void saveByPlan(EqByPlanDTO dto); + + void updateByPlan(EqByPlanDTO dto); + + void deleteByIds(Long[] ids); + + EqByPlanDTO getById(Long id, String relationTypeId); + + void updateByPlanStatus(Long id, String planStatus); + + List listExport(Map params); + + List getPlanList(String byStatus, String planStatus); + + int getByCountByEqId(Long eqId); + + int findByPlanByTempleteId(String templeteId); + + void updateIsRemind(Long id, String s); + + List getPlanListByPlanStatusAndType(String planStatus); + + List getByGroupId(Long id, Long tenantCode); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByService.java new file mode 100644 index 0000000..ae3a416 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByService.java @@ -0,0 +1,37 @@ +package com.thing.eq.eqby.service; + + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByDTO; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.entity.EqByEntity; +import com.thing.eq.eqby.excel.EqByExcel; + +import java.util.List; +import java.util.Map; + +/** + * 设备保养 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByService extends IBaseService { + + void saveEqBy(EqByDTO dto, EqByPlanDTO eqByPlanDTO); + + void updateEqBy(EqByDTO dto); + + void deleteEqBy(Long[] ids); + + EqByDTO getEqByInfo(Long id, String relationTypeId); + + List listExport(Map params); + + int findByRecordCount(Long id); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateDetailService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateDetailService.java new file mode 100644 index 0000000..3c10392 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateDetailService.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqby.service; + + + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.entity.EqByTemplateDetailEntity; + +import java.util.List; + +/** + * 保养模板明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByTemplateDetailService extends IBaseService { + + List getDetails(Long templateId); + + void deleteByTemplateId(Long templateId); + + void deleteByTemplateIds(Long[] ids); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateService.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateService.java new file mode 100644 index 0000000..5aa5a0b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/EqByTemplateService.java @@ -0,0 +1,33 @@ +package com.thing.eq.eqby.service; + + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByTemplateDTO; +import com.thing.eq.eqby.entity.EqByTemplateEntity; + +import java.util.List; +import java.util.Map; + +/** + * 保养模板 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqByTemplateService extends IBaseService { + + List getList(); + + void save(EqByTemplateDTO dto); + + void update(EqByTemplateDTO dto); + + void delete(Long[] ids); + + EqByTemplateDTO get(Long id); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByDetailServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByDetailServiceImpl.java new file mode 100644 index 0000000..fc3a308 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByDetailServiceImpl.java @@ -0,0 +1,48 @@ +package com.thing.eq.eqby.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.EqByDetailDTO; +import com.thing.eq.eqby.entity.EqByDetailEntity; +import com.thing.eq.eqby.mapper.EqByDetailMapper; +import com.thing.eq.eqby.service.EqByDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 保养明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByDetailServiceImpl extends BaseServiceImpl implements EqByDetailService { + + @Autowired + private EqByDetailMapper eqByDetailDao; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void deleteByById(Long byId) { + eqByDetailDao.deleteByById(byId); + } + + @Override + public void deleteByByIds(Long[] byIds) { + eqByDetailDao.deleteByByIds(byIds); + } + + @Override + public List getByById(Long byId) { + return eqByDetailDao.getByById(byId); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanPartServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanPartServiceImpl.java new file mode 100644 index 0000000..0060427 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanPartServiceImpl.java @@ -0,0 +1,53 @@ +package com.thing.eq.eqby.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqby.entity.EqByPlanPartEntity; +import com.thing.eq.eqby.mapper.EqByPlanPartMapper; +import com.thing.eq.eqby.service.EqByPlanPartService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByPlanPartServiceImpl extends BaseServiceImpl implements EqByPlanPartService { + + @Autowired + private EqByPlanPartMapper eqByPlanPartDao; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void deleteByEqByPlanId(Long eqByPlanId) { + eqByPlanPartDao.deleteByEqByPlanId(eqByPlanId); + } + + @Override + public void deleteByEqByPlanIds(Long[] eqByPlanIds) { + eqByPlanPartDao.deleteByEqByPlanIds(eqByPlanIds); + } + + @Override + public List getByEqByPlanId(Long eqByPlanId) { + return eqByPlanPartDao.getByEqByPlanId(eqByPlanId); + } + + @Override + public int getByCountByEqId(Long eqId) { + return eqByPlanPartDao.getByCountByEqId(eqId); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanServiceImpl.java new file mode 100644 index 0000000..051805e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByPlanServiceImpl.java @@ -0,0 +1,425 @@ +package com.thing.eq.eqby.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.*; +import com.thing.eq.eqby.entity.EqByPlanEntity; +import com.thing.eq.eqby.entity.EqByPlanPartEntity; +import com.thing.eq.eqby.excel.EqByPlanExcel; +import com.thing.eq.eqby.mapper.EqByPlanMapper; +import com.thing.eq.eqby.service.*; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.service.EqUserGroupService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.entity.SysDictTypeEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +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.util.*; +import java.util.stream.Collectors; + +/** + * 设备保养计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByPlanServiceImpl extends BaseServiceImpl implements EqByPlanService { + + @Autowired + private EqByPlanPartService eqByPlanPartService; + @Autowired + private IotThingsService iotThingsService; + @Autowired + private EqByTemplateService eqByTemplateService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private EqByTemplateDetailService eqByTemplateDetailService; + @Autowired + private EqByPlanMapper eqByPlanDao; + @Autowired + private SysUserService sysUserService; + +// @Autowired +// private DictTypeServiceFeign dictTypeServiceFeign; + @Autowired + private EqByService eqByService; + @Autowired + private EqUserGroupService eqUserGroupService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private SysDictTypeService sysDictTypeService; + + @Autowired + private SysDictDataService sysDictDataService; + +// @Autowired +// private IotDictTypeService iotDictTypeService; + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = getPage(params); + +// IPage thingsEntityPage = baseDao.selectPage(page, getWrapper(params)); + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + params.put("eqIdList", eqthingIds); + } + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + String relationTypeId = params.get("relationTypeId").toString(); + List eqByPlansDTOS = eqByPlanDao.getListData(params); + for (EqByPlanDTO eqByPlansDTO : eqByPlansDTOS) { + EqInfo thingsInfo = iotThingsService.getThingInfo(eqByPlansDTO.getThingsId(),relationTypeId); + if (thingsInfo != null) { + if (thingsInfo.getUseDeptId() != null){ + SysDeptEntity sysDeptEntity = sysDeptService.getByIdAs(thingsInfo.getUseDeptId(),SysDeptEntity.class); + thingsInfo.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + eqByPlansDTO.setThingsInfo(thingsInfo); + } + if (eqByPlansDTO.getGroupId() !=null){ + EqUserGroupDTO data = eqUserGroupService.getById(eqByPlansDTO.getGroupId()); + if (data != null ){ + eqByPlansDTO.setGroupName(data.getName()); + } + } + } + return new PageData<>(eqByPlansDTOS, eqByPlansDTOS.size()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveByPlan(EqByPlanDTO dto) { + saveInfo(dto); + } + + public void saveInfo(EqByPlanDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.orderBy("by_no",false); + + EqByPlanEntity eqByPlanEntity = mapper.selectOneExt(wrapper); + // 上一条数据的单号 第一条单号为数据库查询出来的 + 1 ,其它的单号为第一条单号的自增 +1 + String lastNo = ""; + for (Long thingId : dto.getThingsIds()) { + //Long id = IdWorker.getId(); +// dto.setId(id); + dto.setThingsId(thingId); + dto.setByStatus("0"); + dto.setPlanStatus("0"); + dto.setIsRemind("0"); + dto.setTenantCode(SecurityUser.getTenantCode()); + // 第一个单号用lastNo + if (Objects.isNull(eqByPlanEntity)) { + eqByPlanEntity = new EqByPlanEntity(); + } + lastNo = SerialNumberUnit.generateNumber("BY", eqByPlanEntity.getByNo()); + dto.setByNo(lastNo); + //保存保养计划 + saveDto(dto); + BeanUtils.copyProperties(dto, eqByPlanEntity); + + //保存保养计划部件 + List eqByParts = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(dto.getThingParts())) { + for (EqByPlanPartDTO eqpart : dto.getThingParts()) { + //调整 + // eqpart.setEqByPlanId(id); + eqByParts.add(eqpart); + } + eqByPlanPartService.saveDto(ConvertUtils.sourceToTarget(eqByParts, EqByPlanPartEntity.class)); + } + } + } + + @Override + public void updateByPlan(EqByPlanDTO dto) { + int count = eqByService.findByRecordCount(dto.getId()); + if (count > 0){ + throw new SysException("该计划已被执行过,不可编辑"); + } + //更新保养计划 + if (StringUtils.isNotBlank(dto.getRemindTime()) && StringUtils.isNotBlank(dto.getRemindTimeUnit()) + && StringUtils.isNotBlank(dto.getLoopCycle()) && StringUtils.isNotBlank(dto.getCycleUnit())) { + if (StringUtils.equals(dto.getRemindTimeUnit(),dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime()) >= Long.parseLong(dto.getLoopCycle())) { + throw new SysException("提醒时间不可大于循环周期"); + } + }else{ + if (StringUtils.equals("day",dto.getRemindTimeUnit()) && StringUtils.equals("hour",dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime())*24 >= Long.parseLong(dto.getLoopCycle())){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + if (StringUtils.equals("hour",dto.getRemindTimeUnit()) && StringUtils.equals("day",dto.getCycleUnit())){ + if (Long.parseLong(dto.getRemindTime()) >= Long.parseLong(dto.getLoopCycle())*24){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + } + } + updateDto(dto); + //先删除保养计划更换部件 + eqByPlanPartService.deleteByEqByPlanId(dto.getId()); + //保存保养计划部件 + List eqByParts = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(dto.getThingParts())) { + for (EqByPlanPartDTO eqpart : dto.getThingParts()) { + eqpart.setEqByPlanId(dto.getId()); + eqByParts.add(eqpart); + } + eqByPlanPartService.saveDto(ConvertUtils.sourceToTarget(eqByParts, EqByPlanPartEntity.class)); + } + } + + @Override + public void deleteByIds(Long[] ids) { + //删除计划 + for (Long id : ids){ + //如果有保养记录,则不可删除 + int count = eqByService.findByRecordCount(id); + if (count > 0){ + throw new SysException("存在保养记录,不可删除,请确认"); + } + } + batchDelete(ids); + eqByPlanPartService.deleteByEqByPlanIds(ids); + } + + @Override + public EqByPlanDTO getById(Long id,String relationTypeId) { + //保养计划详情 + EqByPlanDTO dto = getByIdAs(id,EqByPlanDTO.class); + //设备信息 + EqInfo eqInfo = iotThingsService.getThingInfo(dto.getThingsId(),relationTypeId); + dto.setThingsInfo(eqInfo); + //模板信息 + if (StringUtils.isNotBlank(dto.getByTemplateId())) { + List byTemplateInfos = new ArrayList<>(); + List byTemplateIds = Arrays.asList(dto.getByTemplateId().split(",")); + if (CollectionUtil.isNotEmpty(byTemplateIds)) { + for (String byTemplateId : byTemplateIds) { + EqByTemplateDTO eqByTemplateDTO = eqByTemplateService.getByIdAs(Long.parseLong(byTemplateId),EqByTemplateDTO.class); + byTemplateInfos.add(eqByTemplateDTO); + } + } + dto.setByTemplateInfos(byTemplateInfos); + } + //计划更换部件信息 + List thingPartInfos = eqByPlanPartService.getByEqByPlanId(id); + if (CollectionUtil.isNotEmpty(thingPartInfos)){ + for (EqByPlanPartInfoDTO part :thingPartInfos){ + String stock = iotThingBaseInfoService.getStock(part.getEqPartId()); + if (StringUtils.isNotBlank(stock)){ + part.setStock(stock); + } + } + } + dto.setThingPartInfos(thingPartInfos); + if (dto.getGroupId() !=null){ + EqUserGroupDTO data = eqUserGroupService.getById(dto.getGroupId()); + if (data != null ){ + dto.setGroupName(data.getName()); + } + } + return dto; + } + + @Override + public void updateByPlanStatus(Long id, String planStatus) { + eqByPlanDao.updateByPlanStatus(id, planStatus); + } + + @Override + public List listExport(Map params) { + List excels = new ArrayList<>(); + String eqIds; + List eqthingIds; + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + params.put("eqIdList", eqthingIds); + } + //调整 + String dictTypeStr = "byLevel"; + List dictTypeList1 = StringUtils.isNotBlank(dictTypeStr) ? Arrays.asList(dictTypeStr.split(",")) : null; + List dictTypeList = sysDictTypeService.getDictListByDictTypeList(dictTypeList1); +// Result resultResponse = dictTypeServiceFeign.getDictListByDictTypeList("byLevel"); +// if (Objects.isNull(resultResponse) && resultResponse.getCode() != 0) { +// throw new RenException(!Objects.isNull(resultResponse) ? resultResponse.getMsg() : "服务异常"); +// } +// List dictTypeList = (List) resultResponse.getData(); + + Map dictMap = null; + if (CollectionUtil.isNotEmpty(dictTypeList)) { + dictMap = dictTypeList.stream().collect(Collectors.toMap(item -> String.valueOf(item.getDictType()), item -> item.getDataList())); + } + String relationTypeId = params.get("relationTypeId").toString(); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqByPlansDTOS = eqByPlanDao.getList(params); + for (EqByPlanDTO eqByPlansDTO : eqByPlansDTOS) { + EqByPlanExcel excel = new EqByPlanExcel(); + BeanUtils.copyProperties(eqByPlansDTO, excel); + if (StringUtils.isNotBlank(eqByPlansDTO.getByLevel())){ + if (dictMap != null){ + excel.setByLevel(getDictValue(dictMap,"byLevel",eqByPlansDTO.getByLevel())); + } + } + if (StringUtils.isNotBlank(eqByPlansDTO.getByStatus())) { + if (StringUtils.equals("0", eqByPlansDTO.getByStatus())) { + excel.setByStatus("待保养"); + } else { + excel.setByStatus("已保养"); + } + } + EqInfo thingsInfo = iotThingsService.getThingInfo(eqByPlansDTO.getThingsId(),relationTypeId); + if (thingsInfo != null) { + excel.setEqCode(thingsInfo.getEqCode()); + excel.setName(thingsInfo.getName()); + if (thingsInfo.getUseDeptId() != null) { + } + { + SysDeptEntity sysDeptEntity = sysDeptService.getByIdAs(thingsInfo.getUseDeptId(),SysDeptEntity.class); + excel.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : ""); + } + } + if (eqByPlansDTO.getByPlanTime() != null) { +// excel.setByPlanTime(DateUtils.format(eqByPlansDTO.getByPlanTime(),DateUtils.DATE_TIME_PATTERN)); + excel.setByPlanTime(eqByPlansDTO.getByPlanTime()); + } + if (StringUtils.isNotBlank(eqByPlansDTO.getLoopType())) { + if (StringUtils.equals("0", eqByPlansDTO.getLoopType())) { + excel.setLoopType("单次"); + } else { + excel.setLoopType("多次"); + } + } + if (StringUtils.isNotBlank(eqByPlansDTO.getLoopCycle())) { + if (StringUtils.isNotBlank(eqByPlansDTO.getCycleUnit())){ + if (StringUtils.equals("hour", eqByPlansDTO.getCycleUnit())) { + excel.setLoopCycle(excel.getLoopCycle()+"小时"); + } else { + excel.setLoopCycle(excel.getLoopCycle()+"天"); + } + } + } + if (StringUtils.isNotBlank(eqByPlansDTO.getByUser())) { + StringBuilder sb = new StringBuilder(); + String userIdStr = eqByPlansDTO.getByUser(); + List userIds = Arrays.stream(userIdStr.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + for (int i = 0; i < userIds.size(); i++) { + + SysUserEntity sysUserEntity = sysUserService.getByIdAs(userIds.get(i),SysUserEntity.class); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + if (i == userIds.size() - 1) { + sb.append(sysUserEntity.getRealName()); + } else { + sb.append(sysUserEntity.getRealName()); + sb.append(","); + } + } + } + excel.setByUser(sb.toString()); + } + if (StringUtils.isNotBlank(eqByPlansDTO.getPlanStatus())) { + if (StringUtils.equals("0", eqByPlansDTO.getPlanStatus())) { + excel.setPlanStatus("启用"); + } else { + excel.setPlanStatus("禁用"); + } + } + excels.add(excel); + } + return excels; + } + + public String getDictValue(Map dictMap, String dictType, String curValue) { + if (CollectionUtil.isEmpty(dictMap)) { + return ""; + } + //调整 + List temp = new ArrayList<>(); + Object obj = dictMap.get(dictType); + temp= JSON.parseArray(JSON.toJSONString(obj),SysDictDataEntity.class); + + if (CollectionUtil.isEmpty(temp)) { + return ""; + } + Map dictObject = new HashMap<>(); + for (SysDictDataEntity dictData:temp){ + dictObject.put(dictData.getDictValue(),dictData.getDictLabel()); + } + +// List temp = (List) dictMap.get(dictType); +// if (CollectionUtil.isEmpty(temp)) { +// return ""; +// } +// Map dictObject = temp.stream().collect(Collectors.toMap(item -> String.valueOf(item.get("dictValue")), item -> String.valueOf(item.get("dictLabel")))); + String faultType = StringUtils.isNotBlank(dictObject.get(curValue)) ? dictObject.get(curValue) : "当前字典值不存在"; + return faultType; + } + + @Override + public List getPlanList(String byStatus, String planStatus) { + return eqByPlanDao.getPlanList(byStatus, planStatus); + } + + @Override + public int getByCountByEqId(Long eqId) { + return eqByPlanDao.getByCountByEqId(eqId); + } + + @Override + public int findByPlanByTempleteId(String templeteId) { + return eqByPlanDao.findByPlanByTempleteId(templeteId); + } + + @Override + public void updateIsRemind(Long id, String isRemind) { + eqByPlanDao.updateIsRemind(id,isRemind); + } + + @Override + public List getPlanListByPlanStatusAndType(String planStatus) { + return eqByPlanDao.getPlanListByPlanStatusAndType(planStatus); + } + + @Override + public List getByGroupId(Long id, Long tenantCode) { + return eqByPlanDao.getByGroupId(id,tenantCode); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByServiceImpl.java new file mode 100644 index 0000000..3095fba --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByServiceImpl.java @@ -0,0 +1,492 @@ +package com.thing.eq.eqby.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.keygen.KeyGenerators; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.*; +import com.thing.eq.eqby.entity.EqByDetailEntity; +import com.thing.eq.eqby.entity.EqByEntity; +import com.thing.eq.eqby.excel.EqByExcel; +import com.thing.eq.eqby.mapper.EqByMapper; +import com.thing.eq.eqby.service.EqByDetailService; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqby.service.EqByService; +import com.thing.eq.eqby.service.EqByTemplateDetailService; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import com.thing.eq.eqpartrecord.entity.EqPartRecordEntity; +import com.thing.eq.eqpartrecord.service.EqPartRecordService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.entity.SysDictTypeEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +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.util.*; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.keygen.KeyGenerators.flexId; + +/** + * 设备保养 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByServiceImpl extends BaseServiceImpl implements EqByService { + @Autowired + private EqByDetailService eqByDetailService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private IotThingsService iotThingsService; + @Autowired + private EqByPlanService eqByPlanService; + @Autowired + private EqPartRecordService eqPartRecordService; + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + @Autowired + private EqByTemplateDetailService eqByTemplateDetailService; + @Autowired + private EqByMapper eqByDao; + @Autowired + private SysUserService sysUserService; + @Autowired + private SysDictTypeService sysDictTypeService; + @Autowired + private SysDictDataService sysDictDataService; +// @Autowired +// private DictTypeServiceFeign dictTypeServiceFeign; + +// @Autowired +// private IotDictTypeService iotDictTypeService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public PageData page(Map params) { + Page page = getPage(params); + +// IPage eqByEntityPage = baseDao.selectPage(page, getWrapper(params)); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqBysDTOS = eqByDao.getListData(params); + if (Objects.isNull(params.get("relationTypeId"))) { + throw new SysException("结构类型id不能为空"); + } + String relationTypeId = params.get("relationTypeId").toString(); + for (EqByDTO eqBysDTO : eqBysDTOS) { + //获取保养计划 + EqByPlanDTO dto = eqByPlanService.getByIdAs(eqBysDTO.getEqByPlanId(),EqByPlanDTO.class); + if (dto != null && dto.getThingsId() != null) { + EqInfo thingsInfo = iotThingsService.getThingInfo(dto.getThingsId(),relationTypeId); + if (thingsInfo != null ) { + if (thingsInfo.getUseDeptId() != null){ + SysDeptEntity sysDeptEntity = sysDeptService.getByIdAs(thingsInfo.getUseDeptId(),SysDeptEntity.class); + thingsInfo.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + eqBysDTO.setThingsInfo(thingsInfo); + } + } + } + return new PageData<>(eqBysDTOS, page.getTotalRow()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveEqBy(EqByDTO dto,EqByPlanDTO eqByPlanDTO) { + saveInfoNew(dto, eqByPlanDTO); + } + + public void saveInfo(EqByDTO dto, EqByPlanDTO eqByPlanDTO) { + //保存保养记录 调整 + //Long id = IdWorker.getId(); +// dto.setId(id); + dto.setCreator(null); + dto.setCreateDate(null); + dto.setUpdater(null); + dto.setUpdateDate(null); + //部位保养明细记录 + if (CollectionUtil.isNotEmpty(dto.getEqByDetailDTOList())) { + for (EqByDetailDTO eqByDetailDTO : dto.getEqByDetailDTOList()) { + EqByTemplateDetailDTO detailDTO = eqByTemplateDetailService.getByIdAs(eqByDetailDTO.getByTemplateDetailId(),EqByTemplateDetailDTO.class); + BeanUtils.copyProperties(detailDTO, eqByDetailDTO); + //调整 +// eqByDetailDTO.setId(IdWorker.getId()); +// eqByDetailDTO.setById(id); + } + } + List eqByDTOS = eqByDao.findByEqByPlanId(eqByPlanDTO.getId()); + //循环类型是多次的,需要更新下次的计划时间 + if (StringUtils.equals(eqByPlanDTO.getLoopType(), "1")) { + Date nextPlanTime = null; + if (StringUtils.isNotBlank(eqByPlanDTO.getCycleUnit()) && StringUtils.isNotBlank(eqByPlanDTO.getLoopCycle())) { + if (StringUtils.equals(eqByPlanDTO.getCycleUnit(), "day")) { + String nextPlanTimes = DateTimeUtils.addDateDays(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else { + String nextPlanTimes = DateTimeUtils.addDateHours(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + } + Boolean byResult = false; + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime()==null){ + continue; + } + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < nextPlanTime.getTime() ){ + byResult = true; + } + } + } + if (byResult){ + throw new SysException("本次保养计划已存在保养记录,不可执行"); + } + if (dto.getByStartTime() != null){ + if (dto.getByStartTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime()){ + throw new SysException("保养计划已经截止,不可执行"); + } + if (dto.getByStartTime().getTime() < eqByPlanDTO.getByPlanTime().getTime() || dto.getByStartTime().getTime() >= nextPlanTime.getTime() ){ + throw new SysException("保养计划不在计划执行时间内,不可执行"); + } + } + //更新计划为已保养 + eqByPlanDTO.setByStatus("1"); + } else { + Boolean byResult = false; + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime()==null){ + continue; + } + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < eqByPlanDTO.getPlanEndTime().getTime() ){ + byResult = true; + } + } + } + if (byResult){ + throw new SysException("已存在保养记录,不可执行"); + } + if (dto.getByStartTime() != null){ + if (dto.getByStartTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime()){ + throw new SysException("保养计划已经截止,不可执行"); + } + if (dto.getByStartTime().getTime() < eqByPlanDTO.getByPlanTime().getTime() || dto.getByStartTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime() ){ + throw new SysException("保养计划不在计划执行时间内,不可执行"); + } + } + //单次计划更新计划的状态 + eqByPlanDTO.setByStatus("1"); + eqByPlanDTO.setPlanStatus("1"); + } + eqByPlanService.updateDto(eqByPlanDTO); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("eq_by_plan_id",eqByPlanDTO.getId()); + wrapper.orderBy("LENGTH(by_no)",false); + wrapper.orderBy("by_no",false); + EqByEntity eqByEntity = mapper.selectOneExt(wrapper); + // 上一条数据的单号 第一条单号为数据库查询出来的 + 1 ,其它的单号为第一条单号的自增 +1 + String lastNo = ""; + // 第一个单号用lastNo + if (Objects.isNull(eqByEntity)) { + eqByEntity = new EqByEntity(); + } + lastNo = SerialNumberUnit.generateChildNumber(eqByPlanDTO.getByNo(), eqByEntity.getByNo()); + dto.setByNo(lastNo); + //保存保养记录 + saveDto(dto); + + //保存保养明细记录 + eqByDetailService.saveDto(ConvertUtils.sourceToTarget(dto.getEqByDetailDTOList(), EqByDetailEntity.class)); + //更新库存 调整 +// updateStock(id, dto); + //更新部件更换记录 + eqPartRecordService.saveDto(ConvertUtils.sourceToTarget(dto.getThingParts(), EqPartRecordEntity.class)); + } + + public void saveInfoNew(EqByDTO dto, EqByPlanDTO eqByPlanDTO) { + //保存保养记录 调整 +// Long id = IdWorker.getId(); +// dto.setId(id); + dto.setCreator(null); + dto.setCreateDate(null); + dto.setUpdater(null); + dto.setUpdateDate(null); + //部位保养明细记录 + if (CollectionUtil.isNotEmpty(dto.getEqByDetailDTOList())) { + for (EqByDetailDTO eqByDetailDTO : dto.getEqByDetailDTOList()) { + EqByTemplateDetailDTO detailDTO = eqByTemplateDetailService.getByIdAs(eqByDetailDTO.getByTemplateDetailId(),EqByTemplateDetailDTO.class); + BeanUtils.copyProperties(detailDTO, eqByDetailDTO); + //调整 +// eqByDetailDTO.setId(IdWorker.getId()); +// eqByDetailDTO.setById(id); + } + } + //当前时间作为保养执行时间 + Long currentTime = System.currentTimeMillis(); + if (dto.getByStartTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime()){ + throw new SysException("保养计划已经截止,不可执行"); + } + //循环类型是多次的,需要更新下次的计划时间 + if (StringUtils.equals(eqByPlanDTO.getLoopType(), "1")) { + Date nextPlanStartDate = null; + if (StringUtils.isNotBlank(eqByPlanDTO.getCycleUnit())){ + //下次计划开始时间 + if (StringUtils.equals(eqByPlanDTO.getCycleUnit(), "day")){ + String nextPlanStartDates = DateTimeUtils.addDateDays(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanStartDate = DateTimeUtils.stringToDate(nextPlanStartDates, DateTimeUtils.DATE_TIME_PATTERN_STR); + }else{ + String nextPlanStartDates = DateTimeUtils.addDateHours(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanStartDate = DateTimeUtils.stringToDate(nextPlanStartDates, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + if (dto.getByStartTime().getTime() < eqByPlanDTO.getByPlanTime().getTime() || dto.getByStartTime().getTime() >= nextPlanStartDate.getTime() ){ + throw new SysException("本次执行计划时间不在计划时间内,不可执行"); + } + } + //校验本次计划该设备是否被保养过 + List eqByDTOS = eqByDao.findByEqByPlanId(eqByPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < nextPlanStartDate.getTime() ){ + throw new SysException("本次计划该设备已经执行过"); + } + } + } + //本次计划更新成已保养 + eqByPlanDTO.setByStatus("1"); + } else { + if (dto.getByStartTime().getTime() < eqByPlanDTO.getByPlanTime().getTime() || dto.getByStartTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime() ){ + throw new SysException("执行计划时间不在计划时间内,不可执行"); + } + List eqByDTOS = eqByDao.findByEqByPlanId(eqByPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < eqByPlanDTO.getPlanEndTime().getTime()){ + throw new SysException("该计划设备已经执行过"); + } + } + } + //单次计划更新计划的状态 + eqByPlanDTO.setByStatus("1"); + } + eqByPlanService.updateDto(eqByPlanDTO); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("eq_by_plan_id",eqByPlanDTO.getId()); + wrapper.orderBy("LENGTH(by_no)",false); + wrapper.orderBy("by_no",false); + EqByEntity eqByEntity = mapper.selectOneExt(wrapper); + // 上一条数据的单号 第一条单号为数据库查询出来的 + 1 ,其它的单号为第一条单号的自增 +1 + String lastNo = ""; + // 第一个单号用lastNo + if (Objects.isNull(eqByEntity)) { + eqByEntity = new EqByEntity(); + } + lastNo = SerialNumberUnit.generateChildNumber(eqByPlanDTO.getByNo(), eqByEntity.getByNo()); + dto.setByNo(lastNo); + //保存保养记录 + saveDto(dto); + + //保存保养明细记录 + eqByDetailService.saveDto(ConvertUtils.sourceToTarget(dto.getEqByDetailDTOList(), EqByDetailEntity.class)); + //更新库存 调整 +// updateStock(id, dto); + //更新部件更换记录 + eqPartRecordService.saveDto(ConvertUtils.sourceToTarget(dto.getThingParts(), EqPartRecordEntity.class)); + } + + + public void updateStock(Long id, EqByDTO dto) { + //备件更换记录 + if (CollectionUtil.isNotEmpty(dto.getThingParts())) { + for (EqPartRecordDTO eqPartRecordDTO : dto.getThingParts()) { + eqPartRecordDTO.setEqOperateId(id); + eqPartRecordDTO.setType("1"); + //校验库存,扣除库存 + IotThingBaseInfoDTO data = iotThingBaseInfoService.getByThingsIdForUpdate(eqPartRecordDTO.getEqPartId()); + //如果大于库存,不可操作 + if(Objects.isNull(data)){ + throw new SysException("备件不存在"); + } + if(StringUtils.isBlank(data.getStock())){ + throw new SysException("备件库存不足,请确认库存数量"); + } + + if (Long.parseLong(eqPartRecordDTO.getUseCount()) > Long.parseLong(data.getStock())) { + throw new SysException("备件库存不足,请确认库存数量"); + } + data.setStock(String.valueOf(Long.parseLong(data.getStock()) - Long.parseLong(eqPartRecordDTO.getUseCount()))); + iotThingBaseInfoService.updateDto(data); + } + } + } + + @Override + public void updateEqBy(EqByDTO dto) { + //更新保养记录 + updateDto(dto); + //先删除部件保养明细记录 + eqByDetailService.deleteByById(dto.getId()); + //部件保养明细记录 + if (CollectionUtil.isNotEmpty(dto.getEqByDetailDTOList())) { + for (EqByDetailDTO eqByDetailDTO : dto.getEqByDetailDTOList()) { + EqByTemplateDetailDTO detailDTO = eqByTemplateDetailService.getByIdAs(eqByDetailDTO.getByTemplateDetailId(),EqByTemplateDetailDTO.class); + BeanUtils.copyProperties(detailDTO, eqByDetailDTO); + //调整 +// eqByDetailDTO.setId(IdWorker.getId()); + eqByDetailDTO.setById(dto.getId()); + } + eqByDetailService.saveDto(ConvertUtils.sourceToTarget(dto.getEqByDetailDTOList(), EqByDetailEntity.class)); + } + } + + @Override + public void deleteEqBy(Long[] ids) { + //删除保养记录 + batchDelete(ids); + //删除部件保养记录 + eqByDetailService.deleteByByIds(ids); + } + + @Override + public EqByDTO getEqByInfo(Long id,String relationTypeId) { + EqByDTO dto = getByIdAs(id,EqByDTO.class); + List eqByDetailDTOList = eqByDetailService.getByById(id); + //获取保养计划 + EqByPlanDTO eqByPlanDTO = eqByPlanService.getByIdAs(dto.getEqByPlanId(),EqByPlanDTO.class); + EqInfo thingsInfo = iotThingsService.getThingInfo(eqByPlanDTO.getThingsId(),relationTypeId); + dto.setEqByDetailDTOList(eqByDetailDTOList); + dto.setThingsInfo(thingsInfo); + //计划更换部件信息 + List thingPartInfos = eqPartRecordService.getByEqById(id); + dto.setThingPartInfos(thingPartInfos); + return dto; + } + + @Override + public List listExport(Map params) { + List excels = new ArrayList<>(); + + //调整 + String dictTypeStr = "byLevel"; + List dictTypeList1 = StringUtils.isNotBlank(dictTypeStr) ? Arrays.asList(dictTypeStr.split(",")) : null; + List dictTypeList = sysDictTypeService.getDictListByDictTypeList(dictTypeList1); +// Result resultResponse = dictTypeServiceFeign.getDictListByDictTypeList("byLevel"); +// if (Objects.isNull(resultResponse) && resultResponse.getCode() != 0) { +// throw new RenException(!Objects.isNull(resultResponse) ? resultResponse.getMsg() : "服务异常"); +// } +// List dictTypeList = (List) resultResponse.getData(); + + Map dictMap = null; + if (CollectionUtil.isNotEmpty(dictTypeList)) { + dictMap = dictTypeList.stream().collect(Collectors.toMap(item -> String.valueOf(item.getDictType()), item -> item.getDataList())); + } + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqBysDTOS = eqByDao.getList(params); + String relationTypeId = params.get("relationTypeId").toString(); + for (EqByDTO eqBysDTO : eqBysDTOS) { + EqByExcel excel = new EqByExcel(); + BeanUtils.copyProperties(eqBysDTO, excel); + if (StringUtils.isNotBlank(eqBysDTO.getByLevel())){ + if (dictMap != null){ + excel.setByLevel(getDictValue(dictMap,"byLevel",eqBysDTO.getByLevel())); + } + } + //获取保养计划 + EqByPlanDTO dto = eqByPlanService.getByIdAs(eqBysDTO.getEqByPlanId(),EqByPlanDTO.class); + if (dto != null && dto.getThingsId() != null) { + EqInfo thingsInfo = iotThingsService.getThingInfo(dto.getThingsId(),relationTypeId); + if (thingsInfo != null) { + excel.setEqCode(thingsInfo.getEqCode()); + excel.setName(thingsInfo.getName()); + excel.setStandard(thingsInfo.getStandard()); + if (thingsInfo.getUseDeptId() != null) { + SysDeptEntity sysDeptEntity = sysDeptService.getByIdAs(thingsInfo.getUseDeptId(),SysDeptEntity.class); + excel.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + } + } + if (StringUtils.isNotBlank(eqBysDTO.getByUser())) { + StringBuilder sb = new StringBuilder(); + String userIdStr = eqBysDTO.getByUser(); + List userIds = Arrays.stream(userIdStr.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + for (int i = 0; i < userIds.size(); i++) { + + SysUserEntity sysUserEntity = sysUserService.getByIdAs(userIds.get(i),SysUserEntity.class); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + if (i == userIds.size() - 1) { + sb.append(sysUserEntity.getRealName()); + } else { + sb.append(sysUserEntity.getRealName()); + sb.append(","); + } + } + } + excel.setByUser(sb.toString()); + } + excels.add(excel); + } + return excels; + } + + @Override + public int findByRecordCount(Long byPlanId) { + return eqByDao.findByRecordCount(byPlanId); + } + + public String getDictValue(Map dictMap, String dictType, String curValue) { + if (CollectionUtil.isEmpty(dictMap)) { + return ""; + } + //调整 + List temp = new ArrayList<>(); + Object obj = dictMap.get(dictType); + temp= JSON.parseArray(JSON.toJSONString(obj),SysDictDataEntity.class); + + if (CollectionUtil.isEmpty(temp)) { + return ""; + } + Map dictObject = new HashMap<>(); + for (SysDictDataEntity dictData:temp){ + dictObject.put(dictData.getDictValue(),dictData.getDictLabel()); + } + +// List temp = (List) dictMap.get(dictType); +// if (CollectionUtil.isEmpty(temp)) { +// return ""; +// } +// Map dictObject = temp.stream().collect(Collectors.toMap(item -> String.valueOf(item.get("dictValue")), item -> String.valueOf(item.get("dictLabel")))); + String faultType = StringUtils.isNotBlank(dictObject.get(curValue)) ? dictObject.get(curValue) : "当前字典值不存在"; + return faultType; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateDetailServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateDetailServiceImpl.java new file mode 100644 index 0000000..cf462d6 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateDetailServiceImpl.java @@ -0,0 +1,47 @@ +package com.thing.eq.eqby.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.entity.EqByTemplateDetailEntity; +import com.thing.eq.eqby.mapper.EqByTemplateDetailMapper; +import com.thing.eq.eqby.service.EqByTemplateDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 保养模板明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByTemplateDetailServiceImpl extends BaseServiceImpl implements EqByTemplateDetailService { + + @Autowired + private EqByTemplateDetailMapper eqByTemplateDetailDao; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public List getDetails(Long templateId) { + return eqByTemplateDetailDao.getDetails(templateId); + } + + @Override + public void deleteByTemplateId(Long templateId) { + eqByTemplateDetailDao.deleteByTemplateId(templateId); + } + + @Override + public void deleteByTemplateIds(Long[] ids) { + eqByTemplateDetailDao.deleteByTemplateIds(ids); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateServiceImpl.java new file mode 100644 index 0000000..f10678d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqby/service/impl/EqByTemplateServiceImpl.java @@ -0,0 +1,134 @@ +package com.thing.eq.eqby.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqby.dto.EqByTemplateDTO; +import com.thing.eq.eqby.dto.EqByTemplateDetailDTO; +import com.thing.eq.eqby.entity.EqByTemplateEntity; +import com.thing.eq.eqby.mapper.EqByTemplateMapper; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqby.service.EqByTemplateDetailService; +import com.thing.eq.eqby.service.EqByTemplateService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 保养模板 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqByTemplateServiceImpl extends BaseServiceImpl implements EqByTemplateService { + + @Autowired + private EqByTemplateMapper eqByTemplateDao; + @Autowired + private EqByTemplateDetailService eqByTemplateDetailService; + @Autowired + private EqByPlanService eqByPlanService; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + return wrapper; + } + + + @Override + public PageData page(Map params) { + Page page = getPage(params); + //查询 + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List list = eqByTemplateDao.getListData(params); + List targetList = ConvertUtils.sourceToTarget(list, EqByTemplateDTO.class); + return new PageData<>(targetList, targetList.size()); + } + + @Override + public List getList() { + return eqByTemplateDao.getList(); + } + + @Override + public void save(EqByTemplateDTO dto) { +// Long id = IdWorker.getId(); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + if (StringUtils.isNotBlank(dto.getName())){ + int count = eqByTemplateDao.getTemplateByName(dto.getName(),tenantCode); + if (count > 0){ + throw new SysException("模板名称已经存在,不可新增,请确认"); + } + } + dto.setId(dto.getId()); + super.saveDto(dto); + if (CollectionUtil.isNotEmpty(dto.getDatas())){ + for (EqByTemplateDetailDTO detailDTO:dto.getDatas()){ + EqByTemplateDTO eq = getByName(dto.getName()); + detailDTO.setByTemplateId(eq.getId()); + eqByTemplateDetailService.saveDto(detailDTO); + } + } + } + + private EqByTemplateDTO getByName(String name) { + return mapper.getByName(name); + } + + @Override + public void update(EqByTemplateDTO dto) { + int planCount = eqByPlanService.findByPlanByTempleteId(String.valueOf(dto.getId())); + if (planCount > 0){ + throw new SysException("该模板已被保养计划使用,不可修改,请确认"); + } + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + if (StringUtils.isNotBlank(dto.getName())){ + int count = eqByTemplateDao.getTemplateByNameNoId(dto.getName(),dto.getId(),tenantCode); + if (count > 0){ + throw new SysException("模板名称已经存在,不可新增,请确认"); + } + } + super.updateDto(dto); + //先删除模板明细 + eqByTemplateDetailService.deleteByTemplateId(dto.getId()); + if (CollectionUtil.isNotEmpty(dto.getDatas())){ + for (EqByTemplateDetailDTO detailDTO:dto.getDatas()){ + detailDTO.setByTemplateId(dto.getId()); + eqByTemplateDetailService.saveDto(detailDTO); + } + } + } + + @Override + public EqByTemplateDTO get(Long id) { + EqByTemplateDTO dto = super.getByIdAs(id,EqByTemplateDTO.class); + if (dto!=null){ + dto.setDatas(eqByTemplateDetailService.getDetails(id)); + } + return dto; + } + + @Override + public void delete(Long[] ids) { + for (Long id : ids){ + int count = eqByPlanService.findByPlanByTempleteId(String.valueOf(id)); + if (count > 0){ + throw new SysException("该模板已被保养计划使用,不可删除,请确认"); + } + } + super.batchDelete(ids); + //删除模板明细 + eqByTemplateDetailService.deleteByTemplateIds(ids); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckSettingController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckSettingController.java new file mode 100644 index 0000000..e783f4c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckSettingController.java @@ -0,0 +1,119 @@ +package com.thing.eq.eqcheck.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqby.excel.EqByTemplateDetailExcel; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.excel.EqCheckSettingExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 设备检查设置 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-23 +*/ +@RestController +@RequestMapping("eqchecksetting/eqchecksetting") +@Tag(name="设备-点巡检标准设置") +public class EqCheckSettingController { + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "type", description = "类型0点检 1巡检") + }) +// @RequiresPermissions("eqchecksetting:eqchecksetting:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqCheckSettingService.getPageData(params,EqCheckSettingDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqchecksetting:eqchecksetting:info") + public Result get(@PathVariable("id") Long id){ + EqCheckSettingDTO data = eqCheckSettingService.getByIdAs(id,EqCheckSettingDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("eqchecksetting:eqchecksetting:save") + public Result save(@RequestBody EqCheckSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqCheckSettingService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("eqchecksetting:eqchecksetting:update") + public Result update(@RequestBody EqCheckSettingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqCheckSettingService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("eqchecksetting:eqchecksetting:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqCheckSettingService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("eqchecksetting:eqchecksetting:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqCheckSettingService.listAs(params,EqCheckSettingDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqCheckSettingExcel.class); + ExcelUtils.exportExcel(list1,null, "设备检查设置", EqCheckSettingExcel.class,"设备检查设置.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "设备检查设置", list, EqCheckSettingExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardController.java new file mode 100644 index 0000000..5e25fed --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardController.java @@ -0,0 +1,226 @@ +package com.thing.eq.eqcheck.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqcheck.dto.EqCheckStandardDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.excel.EqCheckStandardExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import com.thing.eq.eqcheck.service.EqCheckStandardService; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping("eqcheckstandard") +@Tag(name = "设备-点巡检标准") +public class EqCheckStandardController { + @Autowired + private EqCheckStandardService eqCheckStandardService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + +// @Autowired +// private TreeService treeService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "模板名称"), + @Parameter(name = "checkPurpose", description = "检查目的"), + @Parameter(name = "keyWord", description = "关键字"), + @Parameter(name = "deviceTypeId", description = "设备类型id (设备类型不为空时,group、type也不能为空)"), + @Parameter(name = "group", description = "结构类型group"), + @Parameter(name = "type", description = "结构类型") + }) +// @RequiresPermissions("equipment:routingInspectionTemplete:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + String group = Objects.isNull(params.get("group")) ? "" : String.valueOf(params.get("group")); + String type = Objects.isNull(params.get("type")) ? "" : String.valueOf(params.get("type")); + String deviceTypeId = Objects.isNull(params.get("deviceTypeId")) ? "" : String.valueOf(params.get("deviceTypeId")); + + //todo + if (StringUtils.isNotBlank(deviceTypeId) && StringUtils.isNotBlank(group) && StringUtils.isNotBlank(type)) { + +// List relationThingId = eqCheckStandardService.getChildThingRelationByToId(group, type, deviceTypeId); +// if (CollectionUtil.isNotEmpty(relationThingId)){ +// params.put("deviceTypeIds", relationThingId); +// } +// params.put("toId", deviceTypeId); +// Result response = treeService.getChildTreeByToId(params); +// List responseData = null; +// if (!Objects.isNull(response) && response.getCode() == 0 && !Objects.isNull(response.getData())) { +// responseData = (List) response.getData(); +// params.put("deviceTypeIds", responseData); +// } + } else if (StringUtils.isNotBlank(deviceTypeId) && (StringUtils.isBlank(group) || StringUtils.isBlank(type))) { + throw new SysException("设备类型id不为空时,group、type也不能为空"); + } + PageData page = eqCheckStandardService.getPageData(params,EqCheckStandardDTO.class); + List standardDTOList = page.getList(); + if (CollectionUtil.isNotEmpty(standardDTOList)) { + standardDTOList.forEach(standardDto -> { + if (!Objects.isNull(standardDto.getId())) { + standardDto.setDetailDTOS(eqCheckStandardDetailService.getListByStandardId(standardDto.getId())); + } + }); + } + page.setList(standardDTOList); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqcheckstandard:eqcheckstandard:info") + public Result get(@PathVariable("id") Long id) { + EqCheckStandardDTO data = eqCheckStandardService.getByIdAs(id,EqCheckStandardDTO.class); + if (Objects.isNull(data)) { + throw new SysException("模板标准不存在"); + } + List detailDTOS = eqCheckStandardDetailService.getListByStandardId(id); + data.setDetailDTOS(detailDTOS); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:routingInspectionTemplete:add") + @Transactional(rollbackFor = Exception.class) + public Result save(@RequestBody EqCheckStandardDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + List detailDTOS = dto.getDetailDTOS(); + if (CollectionUtil.isEmpty(detailDTOS)) { + throw new SysException("请添加模板检查项"); + } + + dto.setTenantCode(SecurityUser.getTenantCode()); + dto.setCreateDate(new Date()); + eqCheckStandardService.saveDto(dto); +// if (Objects.isNull(dto.getId())) { +// throw new SysException("保存失败"); +// } + + + for (EqCheckStandardDetailDTO detail : detailDTOS) { + if ("0".equals(detail.getType()) && (StringUtils.isBlank(detail.getLowerLimit()) || StringUtils.isBlank(detail.getUpperLimit()))) { + throw new SysException("标准细项为上下限判定类型时,上下限值不能为空"); + } + EqCheckStandardDTO eqDto = eqCheckStandardService.getLists(dto.getName()); + detail.setStandardId(eqDto.getId()); + eqCheckStandardDetailService.saveDto(detail); + } + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:routingInspectionTemplete:update") + public Result update(@RequestBody EqCheckStandardDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + if (Objects.isNull(eqCheckStandardService.getByIdAs(dto.getId(),EqCheckStandardDTO.class))) { + throw new SysException("模板标准不存在"); + } + + String msg = eqCheckSettingService.getSettingListByStandardId(dto.getId()); + if (StringUtils.isNotBlank(msg)) { + throw new SysException("模板标准已被" + msg + "使用,无法修改"); + } + + List detailDTOS = dto.getDetailDTOS(); + if (CollectionUtil.isEmpty(detailDTOS)) { + throw new SysException("请添加模板检查项"); + } + eqCheckStandardService.updateDto(dto); + // 删除检查项 + eqCheckStandardDetailService.deleteStandardDetailByStandardId(dto.getId()); + // 重新新增新的检查项 + detailDTOS.forEach(detail -> { + detail.setStandardId(dto.getId()); + eqCheckStandardDetailService.saveDto(detail); + }); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("equipment:routingInspectionTemplete:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + for (Long id : ids) { + String msg = eqCheckSettingService.getSettingListByStandardId(id); + if (StringUtils.isNotBlank(msg)) { + throw new SysException("模板标准已被" + msg + "使用,无法删除"); + } + eqCheckStandardDetailService.deleteStandardDetailByStandardId(id); + } + eqCheckStandardService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("equipment:routingInspectionTemplete:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqCheckStandardService.listAs(params,EqCheckStandardDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqCheckStandardExcel.class); + ExcelUtils.exportExcel(list1,null, "点巡检标准", EqCheckStandardExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, null, "点巡检标准", list, EqCheckStandardExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardDetailController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardDetailController.java new file mode 100644 index 0000000..44c9eb9 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqCheckStandardDetailController.java @@ -0,0 +1,137 @@ +package com.thing.eq.eqcheck.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.excel.EqCheckStandardDetailExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** +* 设备检查标准明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping("eqcheckstandarddetail") +@Tag(name="设备-点巡检标准检查项") +public class EqCheckStandardDetailController { + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingId", description = "设备物id"), + @Parameter(name = "standardId", description = "模板id"), + @Parameter(name = "checkType", description = "模板设置类型 0:点检 1:巡检") + }) +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + List standardIdList = new ArrayList<>(); + if(!Objects.isNull(params.get("thingId")) && !Objects.isNull(params.get("checkType"))){ + // 查询设备关联的所有模板 + List eqCheckSettingDTOS = eqCheckSettingService.getSettingList(Long.parseLong(String.valueOf(params.get("thingId"))),String.valueOf(params.get("checkType"))); + eqCheckSettingDTOS.forEach(eqCheckSettingDTO -> { + standardIdList.add(eqCheckSettingDTO.getStandardId()); + }); + params.put("standardIdList",standardIdList); + } + + PageData page = eqCheckStandardDetailService.getPageData(params,EqCheckStandardDetailDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:info") + public Result get(@PathVariable("id") Long id){ + EqCheckStandardDetailDTO data = eqCheckStandardDetailService.getByIdAs(id,EqCheckStandardDetailDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:save") + public Result save(@RequestBody EqCheckStandardDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqCheckStandardDetailService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:update") + public Result update(@RequestBody EqCheckStandardDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqCheckStandardDetailService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqCheckStandardDetailService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("eqcheckstandarddetail:eqcheckstandarddetail:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqCheckStandardDetailService.listAs(params,EqCheckStandardDetailDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqCheckStandardDetailExcel.class); + ExcelUtils.exportExcel(list1,null, "标准检查项明细", EqCheckStandardDetailExcel.class,"标准检查项明细.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "标准检查项明细", list, EqCheckStandardDetailExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckPlanController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckPlanController.java new file mode 100644 index 0000000..05572d0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckPlanController.java @@ -0,0 +1,640 @@ +package com.thing.eq.eqcheck.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqcheck.dto.*; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.excel.EqPatrolCheckPlanExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqcheck.service.EqPatrolCheckRecordService; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.service.EqUserGroupService; +import com.thing.sys.biz.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 设备巡检计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping(Constant.API_BASE + "/eqpatrolcheckplan") +@Tag(name = "设备-巡检计划") +public class EqPatrolCheckPlanController { + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private CheckResultService checkResultService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private EqPatrolCheckRecordService eqPatrolCheckRecordService; + @Autowired + private EqUserGroupService eqUserGroupService; + @Autowired + private IotThingsService iotThingsService; + + + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "checkUserId", description = "巡检人员id"), + @Parameter(name = "name", description = "计划名称"), + @Parameter(name = "planStatus", description = "计划状态"), + @Parameter(name = "checkCycleUnit", description = "巡检周期类型 day:天 hour:小时"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + }) +// @RequiresPermissions("equipment:routingInspection:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = eqPatrolCheckPlanService.page(params); + + List pageData = page.getList(); + + List resultData = new ArrayList<>(); + if (CollectionUtil.isEmpty(pageData)) { + PageData result = new PageData(resultData, page.getTotal()); + return new Result>().ok(result); + } + for (EqPatrolCheckPlanDTO eqPatrolCheckPlanDTO : pageData) { + List eqInfos = new ArrayList<>(); + EqPatrolCheckPlanRes res = new EqPatrolCheckPlanRes(); + BeanUtils.copyProperties(eqPatrolCheckPlanDTO, res); + + //调整 + String userId = eqPatrolCheckPlanDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + // 查询计划巡检人员 + res.setPlanCheckUserName(name); + // 查询设备数 + List settingList = eqCheckSettingService.getSettingListByPatrolPlanId(eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(settingList)) { + Map> collect = settingList.stream().collect(Collectors.groupingBy(EqCheckSettingDTO::getThingId)); + res.setDeviceCount(CollectionUtil.isEmpty(collect) ? 0 : collect.size()); + } + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + LinkedHashSet nomalCheckIds = new LinkedHashSet<>(); + //单次 + if(StringUtils.equals("0",eqPatrolCheckPlanDTO.getType())){ + for (EqCheckSettingDTO settingDTO : settingList){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanDTO.getCheckEndTime().getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + if ((checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanDTO.getCheckEndTime().getTime()) && StringUtils.equals("2",checkResultDTO.getCheckStatus()) ){ + nomalCheckIds.add(checkResultDTO.getThingId()); + } + } + } + } + // 查询计划关联的未检记录 + res.setUnCheckCount(res.getDeviceCount()-checkIds.size()); + //异常 + res.setAbnormalCount(CollectionUtil.isEmpty(nomalCheckIds) ? 0 : nomalCheckIds.size()); + }else{ + Date newNextCheckTime = eqPatrolCheckPlanDTO.getPlanStartTime(); + Integer cycle = eqPatrolCheckPlanDTO.getCheckCycle(); + String unit = eqPatrolCheckPlanDTO.getCheckCycleUnit(); + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + for (EqCheckSettingDTO settingDTO : settingList){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + if ((checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime()) && StringUtils.equals("2",checkResultDTO.getCheckStatus()) ){ + nomalCheckIds.add(checkResultDTO.getThingId()); + } + } + } + } + // 查询计划关联的未检记录 + res.setUnCheckCount(res.getDeviceCount()-checkIds.size()); + //异常 + res.setAbnormalCount(CollectionUtil.isEmpty(nomalCheckIds) ? 0 : nomalCheckIds.size()); + } + if (res.getGroupId() !=null){ + EqUserGroupDTO data = eqUserGroupService.getById(res.getGroupId()); + if (data != null ){ + res.setGroupName(data.getName()); + } + } + if (CollectionUtil.isNotEmpty(totalCheckIds)){ + for (Long id : totalCheckIds){ + EqDTO data = iotThingsService.getInfo(id); + if (data !=null){ + ThingDTO eqInfo = new ThingDTO(); + eqInfo.setEqCode(data.getEqCode()); + eqInfo.setThingId(data.getThingId()); + eqInfo.setThingName(data.getName()); + if (checkIds.contains(id)){ + eqInfo.setCheck("1"); + }else{ + eqInfo.setCheck("0"); + } + eqInfos.add(eqInfo); + } + } + } + res.setEqInfos(eqInfos); + resultData.add(res); + } + + PageData result = new PageData(resultData, page.getTotal()); + + return new Result>().ok(result); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqpatrolcheckplan:eqpatrolcheckplan:info") + public Result get(@PathVariable("id") Long id, @RequestParam(value = "relationTypeId",required = false) String relationTypeId) { + EqPatrolCheckPlanDTO data = eqPatrolCheckPlanService.getByIdAs(id,EqPatrolCheckPlanDTO.class); + if (Objects.isNull(data)) { + throw new SysException("巡检计划不存在"); + } + if (data.getGroupId() !=null){ + EqUserGroupDTO data1 = eqUserGroupService.getById(data.getGroupId()); + if (data1 != null ){ + data.setGroupName(data.getName()); + } + } + EqPatrolCheckPlanResDTO res = new EqPatrolCheckPlanResDTO(); + BeanUtils.copyProperties(data, res); + List eqCheckSettingDTOS = eqCheckSettingService.getSettingListByPatrolPlanId(id); + if (CollectionUtil.isEmpty(eqCheckSettingDTOS)) { + return new Result().ok(res); + } + + Map> settingGroup = eqCheckSettingDTOS.stream().collect(Collectors.groupingBy(EqCheckSettingDTO::getThingId)); + + List deviceBaseInfoDTOS = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(settingGroup)) { + for (Map.Entry> entry : settingGroup.entrySet()) { + DeviceBaseInfoDTO deviceBaseInfoDTO = new DeviceBaseInfoDTO(); + //查询设备信息 + iotThingBaseInfoService.getThingInfo(deviceBaseInfoDTO, entry.getKey(), entry.getValue(), "0", relationTypeId); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(entry.getKey(),data.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (StringUtils.equals("1",res.getType())){ + Date newNextCheckTime = res.getPlanStartTime(); + Integer cycle = res.getCheckCycle(); + String unit = res.getCheckCycleUnit(); + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + if (data.getPlanStartTime()!=null && data.getCheckEndTime()!=null){ + if (checkResultDTO.getCheckStart().getTime() >= data.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO =eqPatrolCheckRecordService.getPatrolRecordById(checkResultDTO.getCheckRecordId()); + if (eqPatrolCheckRecordDTO != null && eqPatrolCheckRecordDTO.getUserId() != null) { + //调整 + String userId = eqPatrolCheckRecordDTO.getUserId().toString(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + deviceBaseInfoDTO.setUserIdStr(name); + } + deviceBaseInfoDTO.setCheckTime(checkResultDTO.getCheckStart()); + deviceBaseInfoDTO.setCheck("1"); + } + } + }else{ + if (data.getPlanStartTime()!=null && data.getCheckEndTime()!=null){ + if (checkResultDTO.getCheckStart().getTime() >= data.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < data.getCheckEndTime().getTime() ){ + EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO =eqPatrolCheckRecordService.getPatrolRecordById(checkResultDTO.getCheckRecordId()); + if (eqPatrolCheckRecordDTO != null && eqPatrolCheckRecordDTO.getUserId() != null) { + //调整 + String userId = eqPatrolCheckRecordDTO.getUserId().toString(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + deviceBaseInfoDTO.setUserIdStr(name); + } + deviceBaseInfoDTO.setCheckTime(checkResultDTO.getCheckStart()); + deviceBaseInfoDTO.setCheck("1"); + } + } + } + } + } + if (CollectionUtil.isNotEmpty(entry.getValue())) { + List details = eqCheckStandardDetailService.getCheckResultDetailByStandardIdList(deviceBaseInfoDTO.getStandardIdList()); + deviceBaseInfoDTO.setAbnormalCount(0); + deviceBaseInfoDTO.setUnCheckCount(CollectionUtil.isEmpty(details) ? 0 : details.size()); + deviceBaseInfoDTO.setTotalCount(deviceBaseInfoDTO.getUnCheckCount()); + } + deviceBaseInfoDTOS.add(deviceBaseInfoDTO); + } + } + + res.setDeviceBaseInfoDTOS(deviceBaseInfoDTOS); + + return new Result().ok(res); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:routingInspection:add") + public Result save(@RequestBody EqPatrolCheckPlanResDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + if (CollectionUtil.isEmpty(dto.getDeviceBaseInfoDTOS())) { + throw new SysException("巡检计划未选择设备"); + } + if ("1".equals(dto.getType())) { + if (Objects.isNull(dto.getCheckCycle()) || StringUtils.isBlank(dto.getCheckCycleUnit())) { + throw new SysException("巡检方式为多次时,检查周期及单位不能为空"); + } + } + if (StringUtils.isNotBlank(dto.getRemindTime()) && StringUtils.isNotBlank(dto.getRemindTimeUnit()) + && dto.getCheckCycle() !=null && StringUtils.isNotBlank(dto.getCheckCycleUnit())) { + if (StringUtils.equals(dto.getRemindTimeUnit(),dto.getCheckCycleUnit())){ + if (Integer.parseInt(dto.getRemindTime()) >= dto.getCheckCycle()) { + throw new SysException("提醒时间不可大于循环周期"); + } + }else{ + if (StringUtils.equals("day",dto.getRemindTimeUnit()) && StringUtils.equals("hour",dto.getCheckCycleUnit())){ + if (Integer.parseInt(dto.getRemindTime())*24 >= dto.getCheckCycle()){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + if (StringUtils.equals("hour",dto.getRemindTimeUnit()) && StringUtils.equals("day",dto.getCheckCycleUnit())){ + if (Integer.parseInt(dto.getRemindTime()) >= dto.getCheckCycle()*24){ + throw new SysException("提醒时间不可大于循环周期"); + } + } + } + } + dto.setExecutePlanStatus("0"); + dto.setIsRemind("0"); + + if (StringUtils.isBlank(dto.getPlanStatus())) { + dto.setPlanStatus("ACTIVE"); + } + savePatrolCheckPlan(dto); + + return new Result(); + } + + public synchronized void savePatrolCheckPlan(EqPatrolCheckPlanResDTO dto) { + eqPatrolCheckPlanService.savePatrolCheckPlan(dto); + } + + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:routingInspection:update") + public Result update(@RequestBody EqPatrolCheckPlanResDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqPatrolCheckPlanService.updatePatrolCheckPlan(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:routingInspection:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + for (Long id : ids) { + Long count = eqPatrolCheckRecordService.getRecordCountByCheckPlanId(id); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("已经存在巡检记录的巡检计划无法删除"); + } + eqCheckSettingService.deletePatrolByPatrolPlanId(id); + } + + eqPatrolCheckPlanService.batchDelete(ids); + + return new Result(); + } + + @PostMapping("updateStatus") + @Operation(summary = "修改状态") + @LogOperation("修改状态") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:routingInspection:disableOrEnable") + public Result updateStatus(@RequestBody UpdateStatusVo updateStatusVo) { + //效验数据 + ValidatorUtils.validateEntity(updateStatusVo, UpdateGroup.class, DefaultGroup.class); + + List ids = updateStatusVo.getIds(); + if (CollectionUtil.isEmpty(ids)) { + throw new SysException("未选择要修改的数据"); + } + + ids.forEach(id -> { + EqPatrolCheckPlanEntity planEntity = eqPatrolCheckPlanService.getByIdAs(Long.parseLong(id),EqPatrolCheckPlanEntity.class); + if ("2".equals(planEntity.getExecutePlanStatus())) { + throw new SysException("已完成的数据无法修改启用、停用状态"); + } + planEntity.setId(Long.parseLong(id)); + planEntity.setPlanStatus(updateStatusVo.getStatus()); + if ("ACTIVE".equals(updateStatusVo.getStatus())){ + Date date = new Date(); + Date planStartDate = planEntity.getPlanStartTime(); + String dateStr = DateTimeUtils.format(date,DateTimeUtils.DATE_PATTERN_STR); + String dateStr1 = " 00:00:00"; + if (planStartDate != null){ + dateStr1 = DateTimeUtils.format(planStartDate,DateTimeUtils.DATE_PATTERN_STR).substring(10,19); + } + Date newPlanStartDate = DateTimeUtils.stringToDate(dateStr+dateStr1,DateTimeUtils.DATE_PATTERN_STR); + planEntity.setPlanStartTime(newPlanStartDate); + } + eqPatrolCheckPlanService.updateById(planEntity); + }); + + return new Result(); + } + + @PostMapping("executePlan") + @Operation(summary = "执行计划") +// @RequiresPermissions("equipment:routingInspection:execution") + public Result executePlan(@RequestBody ExecutePlanDTO dto) { + + saveInfo(dto); + + return new Result(); + } + + public synchronized void saveInfo(ExecutePlanDTO dto) { + eqPatrolCheckPlanService.executePlanNew(dto); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("equipment:routingInspection:export") +// @Parameters({ +// @Parameter(name = "checkUserId", value = "巡检人员id", paramType = "query", dataType = "String"), +// @Parameter(name = "name", value = "计划名称", paramType = "query", dataType = "String"), +// @Parameter(name = "planStatus", value = "计划状态", paramType = "query", dataType = "String"), +// @Parameter(name = "checkCycleUnit", value = "巡检周期类型 day:天 hour:小时", paramType = "query", dataType = "String"), +// @Parameter(name = "eqIds", value = "设备id,多个用,隔开", paramType = "query", dataType = "String"), +// @Parameter(name = "patrolCheckPlanNo", value = "巡检计划单号", paramType = "query", dataType = "String") +// }) + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqPatrolCheckPlanService.exportData(params); + + List excels = new ArrayList<>(); + + if (CollectionUtil.isNotEmpty(list)) { + for (EqPatrolCheckPlanEntity eqPatrolCheckPlanDTO : list) { + EqPatrolCheckPlanExcel res = new EqPatrolCheckPlanExcel(); + BeanUtils.copyProperties(eqPatrolCheckPlanDTO, res); + if (eqPatrolCheckPlanDTO.getCheckCycle() !=null && StringUtils.isNotBlank(eqPatrolCheckPlanDTO.getCheckCycleUnit()) ){ + res.setCheckCycle(eqPatrolCheckPlanDTO.getCheckCycle() + getCheckCycleStr(eqPatrolCheckPlanDTO.getCheckCycleUnit())); + } + //调整 + // 查询计划巡检人员 + String userId = eqPatrolCheckPlanDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + res.setUser(name); + // 查询设备数 + List settingList = eqCheckSettingService.getSettingListByPatrolPlanId(eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(settingList)) { + Map> collect = settingList.stream().collect(Collectors.groupingBy(EqCheckSettingDTO::getThingId)); + res.setDeviceCount(CollectionUtil.isEmpty(collect) ? 0 : collect.size()); + } + //单次 + if(StringUtils.equals("0",eqPatrolCheckPlanDTO.getType())){ + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + LinkedHashSet nomalCheckIds = new LinkedHashSet<>(); + for (EqCheckSettingDTO settingDTO : settingList){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanDTO.getCheckEndTime().getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + if ((checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanDTO.getCheckEndTime().getTime()) && StringUtils.equals("2",checkResultDTO.getCheckStatus()) ){ + nomalCheckIds.add(checkResultDTO.getThingId()); + } + } + } + } + // 查询计划关联的未检记录 + res.setUnCheckCount(res.getDeviceCount()-checkIds.size()); + //异常 + res.setAbnormalCount(CollectionUtil.isEmpty(nomalCheckIds) ? 0 : nomalCheckIds.size()); + }else{ + Date newNextCheckTime = eqPatrolCheckPlanDTO.getPlanStartTime(); + Integer cycle = eqPatrolCheckPlanDTO.getCheckCycle(); + String unit = eqPatrolCheckPlanDTO.getCheckCycleUnit(); + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + LinkedHashSet nomalCheckIds = new LinkedHashSet<>(); + for (EqCheckSettingDTO settingDTO : settingList){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + if ((checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime()) && StringUtils.equals("2",checkResultDTO.getCheckStatus()) ){ + nomalCheckIds.add(checkResultDTO.getThingId()); + } + } + } + } + // 查询计划关联的未检记录 + res.setUnCheckCount(res.getDeviceCount()-checkIds.size()); + //异常 + res.setAbnormalCount(CollectionUtil.isEmpty(nomalCheckIds) ? 0 : nomalCheckIds.size()); + } + +// res.setDeviceCount(getDefaultValue(res.getDeviceCount())); +// res.setUnCheckCount(getDefaultValue(res.getUnCheckCount())); +// res.setAbnormalCount(getDefaultValue(res.getAbnormalCount())); + + excels.add(res); + } + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqPatrolCheckPlanExcel eqPatrolCheckPlanExcel : excels ){ + if (idList.contains(eqPatrolCheckPlanExcel.getId())){ + newList.add(eqPatrolCheckPlanExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "巡检计划", EqPatrolCheckPlanExcel.class,"巡检计划.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "巡检计划", newList, EqPatrolCheckPlanExcel.class); + } + + @PostMapping("getTbLatestData") + @Operation(summary = "获取采集数据在tb中的最新值") + public Result>> getTbLatestData(@RequestBody List tbDataDTOList) { + return new Result().ok(eqPatrolCheckPlanService.getTbLatestData(tbDataDTOList)); + } + + + + + public Integer getDefaultValue(Integer curCount) { + return Objects.isNull(curCount) ? 0 : curCount; + } + + public String getCheckCycleStr(String unit) { + String time = ""; + if ("day".equals(unit)) { + time = "天"; + } else if ("hour".equals(unit)) { + time = "小时"; + } else { + time = "未知时间单位"; + } + + return time; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckRecordController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckRecordController.java new file mode 100644 index 0000000..6288121 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqPatrolCheckRecordController.java @@ -0,0 +1,665 @@ +package com.thing.eq.eqcheck.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.entity.CheckResultEntity; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqcheck.dto.*; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.excel.EqPatrolCheckRecordDetailExcel; +import com.thing.eq.eqcheck.excel.EqPatrolCheckRecordExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqcheck.service.EqPatrolCheckRecordService; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping("eqpatrolcheckrecord") +@Tag(name = "设备-巡检记录") +@Slf4j +public class EqPatrolCheckRecordController { + @Autowired + private EqPatrolCheckRecordService eqPatrolCheckRecordService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private CheckResultService checkResultService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + + @Autowired + private EqScrapService eqScrapService; + + @Autowired + private IotThingsService iotThingsService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "计划名称"), + @Parameter(name = "thingId", description = "设备id"), + @Parameter(name = "patrolCheckRecordNo", description = "巡检记录单号") + }) +// @RequiresPermissions("equipment:routingRecords:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + + Page pageList = eqPatrolCheckRecordService.searchRecordByPage(params); + List dataList = new ArrayList<>(); + if(Objects.nonNull(pageList)){ + dataList = pageList.getRecords(); + + if (CollectionUtil.isNotEmpty(dataList)) { + StringBuilder stringBuilder = new StringBuilder(); + for (EqPatrolCheckRecordResDTO data : dataList) { + + SysUserDTO userDto = sysUserService.get(data.getUserId()); + if (!Objects.isNull(userDto)) { + data.setUserName(userDto.getUsername()); + } + + List userIdList = StringUtils.isEmpty(data.getPlanUserId()) ? new ArrayList<>() : Arrays.asList(data.getPlanUserId().split(",")); + if (CollectionUtil.isNotEmpty(userIdList)) { + userIdList.forEach(userId -> { + SysUserDTO sysUserDTO = sysUserService.get(Long.parseLong(userId)); + if (!Objects.isNull(sysUserDTO)) { + stringBuilder.append(sysUserDTO.getUsername() + ","); + } + }); + data.setPlanUserName(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + + List resultEntityList = checkResultService.searchRecordCheckResultByRecordId(data.getId()); + if (CollectionUtil.isNotEmpty(resultEntityList)) { + EqDTO eqDTO = iotThingsService.getInfo(resultEntityList.get(0).getThingId()); + if (data != null) { + data.setEqName(eqDTO.getName()); + data.setEqCode(eqDTO.getEqCode()); + } +// resultEntityList.forEach(result -> { +// if ("0".equals(result.getCheckStatus())) { +// data.setNoCheckDeviceCount((Objects.isNull(data.getNoCheckDeviceCount()) ? 0 : data.getNoCheckDeviceCount()) + 1); +// } else if ("2".equals(result.getCheckStatus())) { +// data.setAbnormalDeviceCount((Objects.isNull(data.getAbnormalDeviceCount()) ? 0 : data.getAbnormalDeviceCount()) + 1); +// } +// }); + } + } + } + } + + PageData page = new PageData(dataList, pageList.getTotalPage()); + + return new Result>().ok(page); + } + + @GetMapping("getRecordByPage") + @Operation(summary = "信息(设备列表分页)") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = "checkRecordId", description = "巡检记录id"), + @Parameter(name = "pageFlag", description = "设备列表是否分页 false:不分页 true:分页"), + @Parameter(name = "relationTypeId", description = "结构类型id") + }) +// @RequiresPermissions("eqpatrolcheckrecord:eqpatrolcheckrecord:info") + public Result get(@Parameter(hidden = true) @RequestParam Map params) { + + String recordIdStr = String.valueOf(params.get("checkRecordId")); + if (Objects.isNull(params.get("checkRecordId")) || StringUtils.isBlank(recordIdStr)) { + throw new SysException("巡检记录id不能为空"); + } + String pageFlag = Objects.isNull(params.get("pageFlag")) ? "" : String.valueOf(params.get("pageFlag")); + + Long checkRecordId = Long.parseLong(recordIdStr); + EqPatrolCheckRecordResDTO recordData = eqPatrolCheckRecordService.getPatrolRecordById(checkRecordId); + if (Objects.isNull(recordData)) { + throw new SysException("巡检记录不存在"); + } + + SysUserDTO userDto = sysUserService.get(recordData.getUserId()); + if (!Objects.isNull(userDto)) { + recordData.setUserName(userDto.getUsername()); + } + + List thingList = null; + List deviceCheckRecordVos = new ArrayList<>(); + long total = 0; + if ("true".equals(pageFlag)) { + Page recordThingList = checkResultService.searchRecordThingByPage(params); + thingList = recordThingList.getRecords(); + total = recordThingList.getTotalPage(); + } else { + thingList = checkResultService.searchRecordThing(checkRecordId); + total = CollectionUtil.isNotEmpty(thingList) ? thingList.size() : 0; + } + + + int totalUnCheckCount = 0; + int totalAbnormalCount = 0; + + if (CollectionUtil.isNotEmpty(thingList)) { + for (Long thingId : thingList) { + DeviceCheckRecordVo recordVo = new DeviceCheckRecordVo(); + recordVo.setCheckRecordId(checkRecordId); + recordVo.setPatrolCheckPlanId(recordData.getPatrolCheckPlanId()); + List settingWithStandards = eqCheckSettingService.getSettingListByPatrolPlanIdAndThingId(recordData.getPatrolCheckPlanId(), thingId); + iotThingBaseInfoService.getThingInfo(recordVo, thingId, settingWithStandards, "0", String.valueOf(params.get("relationTypeId"))); + + List checkResultList = checkResultService.searchRecordCheckResult(recordData.getId(), thingId); + CheckResultEntity checkResultEntity = null; + if (CollectionUtil.isNotEmpty(checkResultList)) { + checkResultEntity = checkResultList.get(0); + } + + if (!Objects.isNull(checkResultEntity)) { + recordVo.setId(checkResultEntity.getId()); + recordVo.setCheckStart(checkResultEntity.getCheckStart()); + recordVo.setCheckEnd(checkResultEntity.getCheckEnd()); + //计算已检项 + List resultList = null; + try { + resultList = JSON.parseArray(checkResultEntity.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + + List standardDetail = eqPatrolCheckRecordService.getStandardDetailByCheckPlan(recordData.getPatrolCheckPlanId(), thingId); + if (CollectionUtil.isEmpty(standardDetail)) { + continue; + } + recordVo.setTotal(standardDetail.size()); + + if (CollectionUtil.isNotEmpty(resultList)) { + Map checkResultMap = new HashMap<>(); + resultList.forEach(result -> { + if (!Objects.isNull(result.get("id"))) { + checkResultMap.put(String.valueOf(result.get("id")), result); + } + }); + for (EqCheckStandardDetailEntity detailEntity : standardDetail) { + Map map = checkResultMap.get(String.valueOf(detailEntity.getId())); + if (Objects.isNull(map)||(map !=null && (map.get("checkValue") == null || "".equals(map.get("checkValue"))))) { + recordVo.setUnCheckCount((Objects.isNull(recordVo.getUnCheckCount()) ? 0 : recordVo.getUnCheckCount()) + 1); + } else if ("异常".equals(map.get("checkValue"))) { + recordVo.setAbnormalCount((Objects.isNull(recordVo.getAbnormalCount()) ? 0 : recordVo.getAbnormalCount()) + 1); + } + } + } else { + recordVo.setUnCheckCount(standardDetail.size()); + recordVo.setAbnormalCount(0); + } + + if (Objects.isNull(recordVo.getUnCheckCount())) { + recordVo.setUnCheckCount(0); + } + if (Objects.isNull(recordVo.getAbnormalCount())) { + recordVo.setAbnormalCount(0); + } + + totalUnCheckCount += Objects.isNull(recordVo.getUnCheckCount()) ? 0 : recordVo.getUnCheckCount(); + totalAbnormalCount += Objects.isNull(recordVo.getAbnormalCount()) ? 0 : recordVo.getAbnormalCount(); + + } + deviceCheckRecordVos.add(recordVo); + } + } + PageData pageList = new PageData(deviceCheckRecordVos, total); + + recordData.setTotalUnCheckCount(totalUnCheckCount); + recordData.setTotalAbnormalCount(totalAbnormalCount); + recordData.setDeviceCheckRecordVos(pageList); + + return new Result().ok(recordData); + } + + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqpatrolcheckrecord:eqpatrolcheckrecord:info") + public Result get(@PathVariable("id") Long id) { + EqPatrolCheckRecordResDTO data = eqPatrolCheckRecordService.getPatrolRecordById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:routingRecords:add") + public Result save(@RequestBody EqPatrolCheckRecordDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + EqPatrolCheckPlanEntity checkPlanEntity = eqPatrolCheckPlanService.getById(dto.getPatrolCheckPlanId()); + if (Objects.isNull(checkPlanEntity)) { + throw new SysException("巡检计划不存在"); + } + if (Objects.isNull(checkPlanEntity.getPatrolCheckPlanNo())) { + throw new SysException("无效数据,巡检计划单号不存在"); + } + + savePatrolRecord(dto); + + return new Result(); + } + + public synchronized void savePatrolRecord(EqPatrolCheckRecordDTO dto) { + eqPatrolCheckRecordService.savePatrolRecord(dto); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:routingRecords:update") + public Result update(@RequestBody EqPatrolCheckRecordUpdateDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO = new EqPatrolCheckRecordDTO(); + BeanUtils.copyProperties(dto, eqPatrolCheckRecordDTO); + if (Objects.isNull(eqPatrolCheckRecordDTO.getCheckTime())) { + eqPatrolCheckRecordDTO.setCheckTime(new Date()); + } + + eqPatrolCheckRecordDTO.setCheckEndTime(eqPatrolCheckRecordDTO.getCheckTime()); + eqPatrolCheckRecordService.updateDto(eqPatrolCheckRecordDTO); + + List checkResultDTOS = dto.getCheckResultDTOS(); + if (CollectionUtil.isNotEmpty(checkResultDTOS)) { + for (CheckResultDTO checkResultDTO : checkResultDTOS) { + + if (Objects.isNull(checkResultDTO.getThingId())) { + throw new SysException("巡检结果设备物id不能为空"); + } + + eqScrapService.checkThingStatus(checkResultDTO.getThingId()); + + + if (StringUtils.isBlank(checkResultDTO.getCheckResult())) { + checkResultDTO.setCheckStatus("0"); + } else { + List resultList = null; + try { + resultList = JSON.parseArray(checkResultDTO.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + + if (CollectionUtil.isEmpty(resultList)) { + checkResultDTO.setCheckStatus("0"); + } else { + List standardDetail = eqPatrolCheckRecordService.getStandardDetailByCheckPlan(eqPatrolCheckRecordDTO.getPatrolCheckPlanId(), checkResultDTO.getThingId()); + if (CollectionUtil.isEmpty(standardDetail)) { + throw new SysException("模板标准检查项为空"); + } + if (resultList.size() != standardDetail.size()) { + checkResultDTO.setCheckStatus("2"); + } else { + for (Map map : resultList) { + if ("异常".equals(map.get("checkValue"))) { + checkResultDTO.setCheckStatus("2"); + break; + } + } + } + } + } + + if (!"0".equals(checkResultDTO.getCheckStatus()) && !"2".equals(checkResultDTO.getCheckStatus())) { + checkResultDTO.setCheckStatus("1"); + } + checkResultService.updateRecord(checkResultDTO); + } + } + + return new Result(); + } + + + @GetMapping("getCheckResult") + @Operation(summary = "巡检记录详情及检查项结果") +// @RequiresPermissions("eqpatrolcheckrecord:eqpatrolcheckrecord:update") + @Parameters({ + @Parameter(name = "recordId", description = "巡检记录id"), + @Parameter(name = "thingId", description = "设备物id"), + @Parameter(name = "checkPlanId", description = "设备物id") + }) + public Result> getCheckResult(@RequestParam(required = false) Long recordId, @RequestParam(required = false) Long thingId, @RequestParam(required = false) Long checkPlanId) { + + Long patrolCheckPlanId = 0L; + if (!Objects.isNull(checkPlanId)) { + EqPatrolCheckPlanDTO checkPlanDTO = eqPatrolCheckPlanService.getByIdAs(checkPlanId,EqPatrolCheckPlanDTO.class); + if (Objects.isNull(checkPlanDTO)) { + throw new SysException("巡检计划不存在"); + } + patrolCheckPlanId = checkPlanDTO.getId(); + } else if (!Objects.isNull(recordId)) { + EqPatrolCheckRecordDTO checkRecordDTO = eqPatrolCheckRecordService.getByIdAs(recordId,EqPatrolCheckRecordDTO.class); + if (Objects.isNull(checkRecordDTO)) { + throw new SysException("巡检记录不存在"); + } + patrolCheckPlanId = checkRecordDTO.getPatrolCheckPlanId(); + } else { + throw new SysException("未输入符合的参数"); + } + // 查询当前巡检记录关联的巡检计划的设备模板列表 + List detailDTOS = new ArrayList<>(); + List settingStandardList = eqCheckSettingService.getSettingListByPatrolPlanIdAndThingId(patrolCheckPlanId, thingId); + if (CollectionUtil.isNotEmpty(settingStandardList)) { + settingStandardList.forEach(setting -> { + if (!Objects.isNull(setting.getStandardId())) { + List standardList = eqCheckStandardDetailService.getCheckResultByStandardId(setting.getStandardId()); + detailDTOS.addAll(standardList); + } + }); + } + // 查询巡检记录巡检结果 + List checkResultList = checkResultService.searchRecordCheckResult(recordId, thingId); + if (CollectionUtil.isNotEmpty(checkResultList)) { + for (CheckResultEntity resultEntity : checkResultList) { + if (Objects.isNull(checkResultList) || StringUtils.isBlank(resultEntity.getCheckResult())) { + continue; + } + + String checkResultStr = resultEntity.getCheckResult(); + List resultList = null; + try { + resultList = JSON.parseArray(checkResultStr, Map.class); + } catch (Exception e) { + log.info("巡检结果json转换异常"); + continue; + } + if (CollectionUtil.isEmpty(resultList)) { + continue; + } + + Map resultMap = new HashMap<>(); + resultList.forEach(obj -> { + if (!Objects.isNull(obj.get("id"))) { + resultMap.put(String.valueOf(obj.get("id")), obj); + } + }); + + if (CollectionUtil.isNotEmpty(detailDTOS)) { + detailDTOS.forEach(detail -> { + Map temp = resultMap.get(String.valueOf(detail.getId())); + if (!Objects.isNull(temp)) { + detail.setActualValue(Objects.isNull(temp.get("actualValue")) ? "" : String.valueOf(temp.get("actualValue"))); + detail.setCheckValue(Objects.isNull(temp.get("checkValue")) ? "" : String.valueOf(temp.get("checkValue"))); + detail.setRemark(Objects.isNull(temp.get("remark")) ? "" : String.valueOf(temp.get("remark"))); + } + }); + } + } + } + + return new Result>().ok(detailDTOS); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:routingRecords:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqPatrolCheckRecordService.batchDelete(ids); + + checkResultService.deleteCheckResultByRecordId(Arrays.asList(ids)); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("equipment:routingRecords:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqPatrolCheckRecordService.exportData(params); + + Map storeUserName = new HashMap<>(); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + for (EqPatrolCheckRecordResDTO data : list) { + EqPatrolCheckRecordExcel eqPatrolCheckRecordExcel = new EqPatrolCheckRecordExcel(); + BeanUtils.copyProperties(data, eqPatrolCheckRecordExcel); + // 查询计划巡检人 + if (storeUserName.containsKey(data.getPlanUserId())) { + eqPatrolCheckRecordExcel.setPlanUserName(storeUserName.get(data.getPlanUserId())); + } else { + //调整 + String userId = data.getPlanUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqPatrolCheckRecordExcel.setPlanUserName(name); + storeUserName.put(data.getPlanUserId(), name); + } + + // 查询巡检人 + String userIdStr = String.valueOf(data.getUserId()); + if (storeUserName.containsKey(userIdStr)) { + eqPatrolCheckRecordExcel.setCheckUserName(storeUserName.get(userIdStr)); + } else { + SysUserDTO userDto = sysUserService.get(data.getUserId()); + if (!Objects.isNull(userDto)) { + eqPatrolCheckRecordExcel.setCheckUserName(userDto.getRealName()); + storeUserName.put(userIdStr, userDto.getRealName()); + } else { + storeUserName.put(userIdStr, ""); + } + } + + List resultEntityList = checkResultService.searchRecordCheckResultByRecordId(data.getId()); + if (CollectionUtil.isNotEmpty(resultEntityList)) { + EqDTO eqDTO = iotThingsService.getInfo(resultEntityList.get(0).getThingId()); + if (data != null) { + eqPatrolCheckRecordExcel.setEqName(eqDTO.getName()); + eqPatrolCheckRecordExcel.setEqCode(eqDTO.getEqCode()); + } + } + +// eqPatrolCheckRecordExcel.setNoCheckDeviceCount(getDefaultValue(eqPatrolCheckRecordExcel.getNoCheckDeviceCount())); +// eqPatrolCheckRecordExcel.setAbnormalDeviceCount(getDefaultValue(eqPatrolCheckRecordExcel.getAbnormalDeviceCount())); + + excels.add(eqPatrolCheckRecordExcel); + } + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqPatrolCheckRecordExcel eqPatrolCheckRecordExcel : excels ){ + if (idList.contains(eqPatrolCheckRecordExcel.getId())){ + newList.add(eqPatrolCheckRecordExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "巡检记录", EqPatrolCheckRecordExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, "巡检记录", newList, EqPatrolCheckRecordExcel.class); + } + + @PostMapping("export/details") + @Operation(summary = "导出巡检记录明细") + @LogOperation("导出巡检记录明细") +// @Parameters({ +// @Parameter(name = "name", value = "计划名称", paramType = "query", dataType = "String"), +// @Parameter(name = "thingId", value = "设备id", paramType = "query", dataType = "String"), +// @Parameter(name = "patrolCheckRecordNo", value = "巡检记录单号", paramType = "query", dataType = "String") +// }) +// @RequiresPermissions("equipment:routingRecords:export") + public void exportDetails( HttpServletResponse response,@RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqPatrolCheckRecordService.exportData(params); + + Map storeUserName = new HashMap<>(); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + for (EqPatrolCheckRecordResDTO data : list) { + // 查询计划巡检人 + String planUserName =""; + if (storeUserName.containsKey(data.getPlanUserId())) { + planUserName = storeUserName.get(data.getPlanUserId()); + } else { + //调整 + String userId = data.getPlanUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + }List planUserName1 = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName1) { + name+=s+","; + } + planUserName = name.substring(0, name.length() - 1); + storeUserName.put(data.getPlanUserId(), planUserName); + } + + // 查询巡检人 + String userIdStr = String.valueOf(data.getUserId()); + String checkUserName = ""; + if (storeUserName.containsKey(userIdStr)) { + checkUserName = storeUserName.get(userIdStr); + } else { + SysUserDTO userDto = sysUserService.get(data.getUserId()); + if (!Objects.isNull(userDto)) { + checkUserName = userDto.getRealName(); + storeUserName.put(userIdStr, userDto.getRealName()); + } + } + //查找巡检记录结果 + List resultEntityList = checkResultService.searchRecordCheckResultByRecordId(data.getId()); + resultEntityList = resultEntityList.stream().filter(item -> StringUtils.isNotBlank(item.getCheckResult())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(resultEntityList)) { + for (CheckResultEntity checkResultEntity : resultEntityList){ + //获取设备信息 + EqDTO eqDTO = iotThingsService.getInfo(checkResultEntity.getThingId()); + String eqName = ""; + String eqCode = ""; + if (eqDTO != null){ + eqName = eqDTO.getName(); + eqCode = eqDTO.getEqCode(); + } + List resultList = null; + try { + resultList = JSON.parseArray(checkResultEntity.getCheckResult(), EqCheckStandardDetailResultDTO.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + if (CollectionUtil.isNotEmpty(resultList)){ + for (EqCheckStandardDetailResultDTO result: resultList){ + EqPatrolCheckRecordDetailExcel eqPatrolCheckRecordDetailExcelExcel = new EqPatrolCheckRecordDetailExcel(); + BeanUtils.copyProperties(data, eqPatrolCheckRecordDetailExcelExcel); + eqPatrolCheckRecordDetailExcelExcel.setPlanUserName(planUserName); + eqPatrolCheckRecordDetailExcelExcel.setCheckUserName(checkUserName); + eqPatrolCheckRecordDetailExcelExcel.setEqName(eqName); + eqPatrolCheckRecordDetailExcelExcel.setEqCode(eqCode); + eqPatrolCheckRecordDetailExcelExcel.setCheckName(result.getName()); + eqPatrolCheckRecordDetailExcelExcel.setUpperLimit(result.getUpperLimit()); + eqPatrolCheckRecordDetailExcelExcel.setLowerLimit(result.getLowerLimit()); + eqPatrolCheckRecordDetailExcelExcel.setActualValue(result.getActualValue()); + eqPatrolCheckRecordDetailExcelExcel.setCheckValue(result.getCheckValue()); + excels.add(eqPatrolCheckRecordDetailExcelExcel); + } + } + } + } + } + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqPatrolCheckRecordDetailExcel eqPatrolCheckRecordDetailExcel : excels ){ + if (idList.contains(eqPatrolCheckRecordDetailExcel.getId())){ + newList.add(eqPatrolCheckRecordDetailExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "巡检记录明细", EqPatrolCheckRecordDetailExcel.class,"巡检记录明细.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "巡检记录明细", newList, EqPatrolCheckRecordDetailExcel.class); + } + + public Integer getDefaultValue(Integer curCount) { + return Objects.isNull(curCount) ? 0 : curCount; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckPlanController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckPlanController.java new file mode 100644 index 0000000..dab4899 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckPlanController.java @@ -0,0 +1,322 @@ +package com.thing.eq.eqcheck.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.paginate.Page; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqcheck.dto.*; +import com.thing.eq.eqcheck.excel.EqSpotCheckPlanExcel; +import com.thing.eq.eqcheck.service.*; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import java.util.*; + + +/** + * 点检计划 + * + * @author zy aa@aa,com + * @since 3.0 2021-10-12 + */ +@RestController +@RequestMapping("eqspotcheckplan/eqspotcheckplan") +@Tag(name = "设备-点检计划") +public class EqSpotCheckPlanController { + @Autowired + private EqSpotCheckPlanService eqSpotCheckPlanService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private EqCheckStandardService eqCheckStandardService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private EqSpotCheckRecordService eqSpotCheckRecordService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "code", description = "物编号"), + @Parameter(name = "eqCode", description = "设备编号"), + @Parameter(name = "name", description = "设备名称"), + @Parameter(name = "startTime", description = "点检开始时间"), + @Parameter(name = "endTime", description = "点检结束时间"), + @Parameter(name = "useId", description = "点检人"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + @Parameter(name = "spotCheckNo", description = "点检计划单号"), + @Parameter(name = "relationTypeId", description = "结构类型id") + }) +// @RequiresPermissions("equipment:spotCheck:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + Page pageList = eqSpotCheckPlanService.searchPlanByPage(params); + List planDTOS = new ArrayList<>(); + if(Objects.nonNull(pageList)) { + planDTOS = pageList.getRecords(); + if (CollectionUtil.isNotEmpty(planDTOS)) { + StringBuilder stringBuilder = new StringBuilder(); + planDTOS.forEach(eqSpotCheckPlanDTO -> { + + iotThingBaseInfoService.getThingInfo(eqSpotCheckPlanDTO, eqSpotCheckPlanDTO.getThingId(), null, "0", String.valueOf(params.get("relationTypeId"))); + + List standardList = new ArrayList<>(); + List settingList = eqCheckSettingService.getSettingList(eqSpotCheckPlanDTO.getThingId(), "0"); + if (CollectionUtil.isNotEmpty(settingList)) { + settingList.forEach(eqCheckSettingDTO -> { + EqCheckStandardDTO eqCheckStandardDTO = eqCheckStandardService.getByIdAs(eqCheckSettingDTO.getStandardId(),EqCheckStandardDTO.class); + if (!Objects.isNull(eqCheckStandardDTO)) { + standardList.add(eqCheckStandardDTO.getId()); + stringBuilder.append(eqCheckStandardDTO.getName() + ","); + } + }); + eqSpotCheckPlanDTO.setCheckIdList(standardList); + eqSpotCheckPlanDTO.setCheckNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + + + List useIdList = StringUtils.isNotBlank(eqSpotCheckPlanDTO.getUseId()) ? Arrays.asList(eqSpotCheckPlanDTO.getUseId().split(",")) : new ArrayList<>(); + if (CollectionUtil.isNotEmpty(useIdList)) { + useIdList.forEach(idStr -> { + SysUserDTO sysUserDTO = sysUserService.get(Long.parseLong(idStr)); + if (!Objects.isNull(sysUserDTO)) { + stringBuilder.append(sysUserDTO.getRealName() + ","); + } + }); + eqSpotCheckPlanDTO.setUseIdList(useIdList); + eqSpotCheckPlanDTO.setOperatorNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + }); + } + + } + PageData page = new PageData<>(planDTOS, CollectionUtil.isEmpty(planDTOS) ? 0 : pageList.getTotalRow()); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqspotcheckplan:eqspotcheckplan:info") + public Result get(@PathVariable("id") Long id, @RequestParam(value = "relationTypeId", required = false) String relationTypeId) { + EqSpotCheckPlanDTO data = eqSpotCheckPlanService.getByIdAs(id,EqSpotCheckPlanDTO.class); + if (Objects.isNull(data)) { + throw new SysException("点检计划不存在"); + } + iotThingBaseInfoService.getThingInfo(data, data.getThingId(), null, "0", relationTypeId); + + StringBuilder stringBuilder = new StringBuilder(); + List settingList = eqCheckSettingService.getSettingList(data.getThingId(), "0"); + + if (CollectionUtil.isNotEmpty(settingList)) { + List standardList = new ArrayList<>(); + List standardIdList = new ArrayList<>(); + settingList.forEach(eqCheckSettingDTO -> { + EqCheckStandardDTO eqCheckStandardDTO = eqCheckStandardService.getByIdAs(eqCheckSettingDTO.getStandardId(),EqCheckStandardDTO.class); + if (!Objects.isNull(eqCheckStandardDTO)) { + DataVo dataVo = new DataVo(); + dataVo.setId(eqCheckStandardDTO.getId()); + dataVo.setName(eqCheckStandardDTO.getName()); + standardIdList.add(eqCheckStandardDTO.getId()); + standardList.add(dataVo); + stringBuilder.append(eqCheckStandardDTO.getName() + ","); + } + }); + + data.setCheckIdList(standardIdList); + data.setDataList(standardList); + data.setCheckNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + + List useIdList = StringUtils.isNotBlank(data.getUseId()) ? Arrays.asList(data.getUseId().split(",")) : new ArrayList<>(); + + if (CollectionUtil.isNotEmpty(useIdList)) { + useIdList.forEach(idStr -> { + SysUserDTO sysUserDTO = sysUserService.get(Long.parseLong(idStr)); + if (!Objects.isNull(sysUserDTO)) { + stringBuilder.append(sysUserDTO.getRealName() + ","); + } + }); + data.setUseIdList(useIdList); + data.setOperatorNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + } + + List detailDTOS = eqCheckStandardDetailService.getAllStandardDetailByThingId(data.getThingId(), "0"); + data.setDetailDTOS(detailDTOS); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:spotCheck:add") + public Result save(@RequestBody EqSpotCheckPlanParamDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + if (CollectionUtil.isEmpty(dto.getThingIdList())) { + throw new SysException("未选择设备"); + } + if (CollectionUtil.isEmpty(dto.getStandardIdList())) { + throw new SysException("设备未选择巡检模板"); + } + + saveInfo(dto); + + return new Result(); + } + + public synchronized void saveInfo(EqSpotCheckPlanParamDTO dto) { + eqSpotCheckPlanService.savePlan(dto); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:spotCheck:update") + public Result update(@RequestBody EqSpotCheckPlanParamDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + + eqSpotCheckPlanService.updatePlan(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:spotCheck:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + for (Long id : ids) { + + Long count = eqSpotCheckRecordService.getRecordCountBySpotCheckPlanId(id); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("已经存在点检记录的点检计划无法删除"); + } + EqSpotCheckPlanDTO eqSpotCheckPlanDTO = eqSpotCheckPlanService.getByIdAs(id,EqSpotCheckPlanDTO.class); + if (Objects.isNull(eqSpotCheckPlanDTO)) { + continue; + } + eqCheckSettingService.deleteByThingId(eqSpotCheckPlanDTO.getThingId()); + } + eqSpotCheckPlanService.batchDelete(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "code",description = "设备编号"), + @Parameter(name = "name", description = "设备名称"), + @Parameter(name = "startTime", description = "点检开始时间"), + @Parameter(name = "endTime", description = "点检结束时间"), + @Parameter(name = "useId", description = "点检人"), + @Parameter(name = "eqIds", description = "设备id,多个用,隔开"), + @Parameter(name = "spotCheckNo", description = "点检计划单号"), + @Parameter(name = "relationTypeId", description = "结构类型id") + }) +// @RequiresPermissions("equipment:spotCheck:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqSpotCheckPlanService.searchPlan(params); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + StringBuilder stringBuilder = new StringBuilder(); + list.forEach(eqSpotCheckPlanDTO -> { + + iotThingBaseInfoService.getThingInfo(eqSpotCheckPlanDTO, eqSpotCheckPlanDTO.getThingId(), null, "0", String.valueOf(params.get("relationTypeId"))); + + List standardList = new ArrayList<>(); + List settingList = eqCheckSettingService.getSettingList(eqSpotCheckPlanDTO.getThingId(), "0"); + if (CollectionUtil.isNotEmpty(settingList)) { + settingList.forEach(eqCheckSettingDTO -> { + EqCheckStandardDTO eqCheckStandardDTO = eqCheckStandardService.getByIdAs(eqCheckSettingDTO.getStandardId(),EqCheckStandardDTO.class); + if (!Objects.isNull(eqCheckStandardDTO)) { + standardList.add(eqCheckStandardDTO.getId()); + stringBuilder.append(eqCheckStandardDTO.getName() + ","); + } + }); + eqSpotCheckPlanDTO.setCheckNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + + List useIdList = StringUtils.isNotBlank(eqSpotCheckPlanDTO.getUseId()) ? Arrays.asList(eqSpotCheckPlanDTO.getUseId().split(",")) : new ArrayList<>(); + if (CollectionUtil.isNotEmpty(useIdList)) { + useIdList.forEach(idStr -> { + SysUserDTO sysUserDTO = sysUserService.get(Long.parseLong(idStr)); + if (!Objects.isNull(sysUserDTO)) { + stringBuilder.append(sysUserDTO.getRealName() + ","); + } + }); + eqSpotCheckPlanDTO.setOperatorNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + stringBuilder.setLength(0); + } + + EqSpotCheckPlanExcel eqSpotCheckPlanExcel = new EqSpotCheckPlanExcel(); + BeanUtils.copyProperties(eqSpotCheckPlanDTO, eqSpotCheckPlanExcel); + + excels.add(eqSpotCheckPlanExcel); + }); + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqSpotCheckPlanExcel eqSpotCheckPlanExcel : excels ){ + if (idList.contains(eqSpotCheckPlanExcel.getId())){ + newList.add(eqSpotCheckPlanExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "点检计划", EqSpotCheckPlanExcel.class,"点检计划.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "点检计划", newList, EqSpotCheckPlanExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckRecordController.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckRecordController.java new file mode 100644 index 0000000..ad42493 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/controller/EqSpotCheckRecordController.java @@ -0,0 +1,358 @@ +package com.thing.eq.eqcheck.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.mybatisflex.core.paginate.Page; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqcheck.dto.*; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.excel.EqSpotCheckRecordExcel; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import com.thing.eq.eqcheck.service.EqSpotCheckRecordService; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.xml.validation.Schema; +import java.util.*; + + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@RestController +@RequestMapping("eqspotcheckrecord") +@Tag(name = "设备-点检记录") +public class EqSpotCheckRecordController { + @Autowired + private EqSpotCheckRecordService eqSpotCheckRecordService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private EqScrapService eqScrapService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "code", description = "设备编号"), + @Parameter(name = "name", description = "设备名称"), + @Parameter(name = "startTime", description = "点检开始时间"), + @Parameter(name = "endTime", description = "点检结束时间"), + @Parameter(name = "useId", description = "点检人"), + @Parameter(name = "spotPlanId", description = "点检计划id"), + @Parameter(name = "spotCheckRecordNo", description = "点检记录单号") + }) +// @RequiresPermissions("equipment:spotCheckHistory:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + Page pageList = eqSpotCheckRecordService.searchRecordByPage(params); + List recordDTOS = new ArrayList<>(); + if(Objects.nonNull(pageList)){ + recordDTOS = pageList.getRecords(); + recordDTOS.forEach(record -> { + + SysUserDTO sysUserDTO = sysUserService.get(record.getUserId()); + if (!Objects.isNull(sysUserDTO)) { + record.setOperatorName(sysUserDTO.getRealName()); + } + + List resultList = null; + try { + resultList = JSON.parseArray(record.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + + Map checkResultMap = new HashMap<>(); + if (CollectionUtil.isNotEmpty(resultList)) { + resultList.forEach(result -> { + if (!Objects.isNull(result.get("id"))) { + checkResultMap.put(String.valueOf(result.get("id")), result); + } + }); + } + + List settingList = eqCheckSettingService.getSettingList(record.getThingId(), "0"); + List standardIdList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(settingList)) { + settingList.forEach(setting -> { + if (!Objects.isNull(setting.getStandardId())) { + standardIdList.add(setting.getStandardId()); + } + }); + List standardDetail = eqCheckStandardDetailService.getCheckResultDetailByStandardIdList(standardIdList); + if (CollectionUtil.isNotEmpty(standardDetail)) { + record.setTotalCount(standardDetail.size()); + for (EqCheckStandardDetailEntity detailEntity : standardDetail) { + Map map = checkResultMap.get(String.valueOf(detailEntity.getId())); + if (Objects.isNull(map) ||(map !=null && (map.get("checkValue") == null || "".equals(map.get("checkValue"))) )) { + record.setUnCheckCount((Objects.isNull(record.getUnCheckCount()) ? 0 : record.getUnCheckCount()) + 1); + } else if ("异常".equals(map.get("checkValue"))) { + record.setAbnormalCount((Objects.isNull(record.getAbnormalCount()) ? 0 : record.getAbnormalCount()) + 1); + } + } + } + } + }); + + } + + PageData page = new PageData<>(recordDTOS, CollectionUtil.isEmpty(recordDTOS) ? 0 : pageList.getTotalRow()); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息(不包含模板检查项数据)") +// @RequiresPermissions("eqspotcheck:eqspotcheckrecord:info") + public Result get(@PathVariable("id") Long id) { + EqSpotCheckRecordDTO data = eqSpotCheckRecordService.getByIdAs(id,EqSpotCheckRecordDTO.class); + + return new Result().ok(data); + } + + @GetMapping("getRecordDetail") + @Operation(summary = "详情(包含模板检查项数据)") +// @RequiresPermissions("eqspotcheck:eqspotcheckrecord:info") + public Result getRecordDetail(@RequestParam Long recordId, @RequestParam(value = "relationTypeId", required = false) String relationTypeId) { + + EqSpotCheckRecordDTO data = eqSpotCheckRecordService.getByIdAs(recordId,EqSpotCheckRecordDTO.class); + if (Objects.isNull(data)) { + throw new SysException("点检记录不存在"); + } + iotThingBaseInfoService.getThingInfo(data, data.getThingId(), null, "0", relationTypeId); + + CheckResultRes checkResultRes = new CheckResultRes(); + checkResultRes.setEqSpotCheckRecordDTO(data); + + // 获取设备所有细项 + List detailDTOS = eqCheckStandardDetailService.getAllStandardDetailByThingId(data.getThingId(), "0"); + List standardCheckResultDTOS = ConvertUtils.sourceToTarget(detailDTOS, StandardCheckResultDTO.class); + // 根据细项匹配点检记录结果 + if (StringUtils.isNotBlank(data.getCheckResult()) && CollectionUtil.isNotEmpty(standardCheckResultDTOS)) { + List resultList = JSONArray.parseArray(data.getCheckResult(), Map.class); + Map temp = new HashMap<>(); + + if (CollectionUtil.isNotEmpty(resultList)) { + resultList.forEach(obj -> { + if (!Objects.isNull(obj.get("id"))) { + temp.put(String.valueOf(obj.get("id")), obj); + } + }); + } + if (CollectionUtil.isNotEmpty(standardCheckResultDTOS)) { + standardCheckResultDTOS.forEach(resultDTO -> { + Map res = temp.get(String.valueOf(resultDTO.getId())); + if (!Objects.isNull(res)) { + resultDTO.setActualValue(Objects.isNull(res.get("actualValue")) ? "" : String.valueOf(res.get("actualValue"))); + resultDTO.setCheckValue(Objects.isNull(res.get("checkValue")) ? "" : String.valueOf(res.get("checkValue"))); + resultDTO.setRemark(Objects.isNull(res.get("remark")) ? "" : String.valueOf(res.get("remark"))); + } + }); + } + + checkResultRes.setStandardCheckResultDTOList(standardCheckResultDTOS); + } + return new Result().ok(checkResultRes); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:spotCheck:execution") + public Result save(@RequestBody EqSpotCheckRecordDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (Objects.isNull(dto.getSpotCheckId())) { + throw new SysException("点检计划id不能为空"); + } + if(Objects.isNull(dto.getThingId())){ + throw new SysException("设备thingId不能为空"); + } + saveInfo(dto); + return new Result(); + } + + public synchronized void saveInfo(EqSpotCheckRecordDTO dto) { + eqSpotCheckRecordService.saveSpotCheckRecord(dto); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:spotCheckHistory:update") + public Result update(@RequestBody EqSpotCheckRecordDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + if(Objects.isNull(dto.getThingId())){ + throw new SysException("设备thingId不能为空"); + } + eqScrapService.checkThingStatus(dto.getThingId()); + + eqSpotCheckRecordService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("equipment:spotCheckHistory:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqSpotCheckRecordService.batchDelete(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name = "code", value = "设备编号", paramType = "query", required = true, dataType = "int"), +// @Parameter(name = "name", value = "设备名称", paramType = "query", dataType = "String"), +// @Parameter(name = "startTime", value = "点检开始时间", paramType = "query", dataType = "String"), +// @Parameter(name = "endTime", value = "点检结束时间", paramType = "query", dataType = "String"), +// @Parameter(name = "useId", value = "点检人", paramType = "query", dataType = "String"), +// @Parameter(name = "spotPlanId", value = "点检计划id", paramType = "query", dataType = "String"), +// @Parameter(name = "spotCheckRecordNo", value = "点检记录单号", paramType = "query", dataType = "String") +// }) +// @RequiresPermissions("equipment:spotCheckHistory:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqSpotCheckRecordService.searchRecord(params); + + Map storeOperator = new HashMap<>(); + + List excels = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(list)) { + list.forEach(record -> { + + if (storeOperator.containsKey(record.getUserId())) { + record.setOperatorName(storeOperator.get(record.getUserId())); + } else { + SysUserDTO sysUserDTO = sysUserService.get(record.getUserId()); + if (!Objects.isNull(sysUserDTO)) { + record.setOperatorName(sysUserDTO.getRealName()); + storeOperator.put(record.getUserId(), sysUserDTO.getRealName()); + } else { + storeOperator.put(record.getUserId(), ""); + } + } + + List resultList = null; + try { + resultList = JSON.parseArray(record.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + + Map checkResultMap = new HashMap<>(); + if (CollectionUtil.isNotEmpty(resultList)) { + resultList.forEach(result -> { + if (!Objects.isNull(result.get("id"))) { + checkResultMap.put(String.valueOf(result.get("id")), result); + } + }); + } + + List settingList = eqCheckSettingService.getSettingList(record.getThingId(), "0"); + List standardIdList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(settingList)) { + settingList.forEach(setting -> { + if (!Objects.isNull(setting.getStandardId())) { + standardIdList.add(setting.getStandardId()); + } + }); + List standardDetail = eqCheckStandardDetailService.getCheckResultDetailByStandardIdList(standardIdList); + if (CollectionUtil.isNotEmpty(standardDetail)) { + record.setTotalCount(standardDetail.size()); + for (EqCheckStandardDetailEntity detailEntity : standardDetail) { + Map map = checkResultMap.get(String.valueOf(detailEntity.getId())); + if (Objects.isNull(map) ||(map !=null && (map.get("checkValue") == null || "".equals(map.get("checkValue"))))) { + record.setUnCheckCount((Objects.isNull(record.getUnCheckCount()) ? 0 : record.getUnCheckCount()) + 1); + } else if ("异常".equals(map.get("checkValue"))) { + record.setAbnormalCount((Objects.isNull(record.getAbnormalCount()) ? 0 : record.getAbnormalCount()) + 1); + } + } + } + } + + record.setTotalCount(getDefaultValue(record.getTotalCount())); + record.setUnCheckCount(getDefaultValue(record.getUnCheckCount())); + record.setAbnormalCount(getDefaultValue(record.getAbnormalCount())); + + EqSpotCheckRecordExcel eqSpotCheckRecordExcel = new EqSpotCheckRecordExcel(); + BeanUtils.copyProperties(record, eqSpotCheckRecordExcel); + + excels.add(eqSpotCheckRecordExcel); + }); + } + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqSpotCheckRecordExcel eqSpotCheckRecordExcel : excels ){ + if (idList.contains(eqSpotCheckRecordExcel.getId())){ + newList.add(eqSpotCheckRecordExcel); + } + } + }else{ + newList.addAll(excels); + } + ExcelUtils.exportExcel(newList,null, "点检记录", EqSpotCheckRecordExcel.class,"点检记录.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "点检记录", newList, EqSpotCheckRecordExcel.class); + } + + + public Integer getDefaultValue(Integer curCount) { + return Objects.isNull(curCount) ? 0 : curCount; + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/CheckResultRes.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/CheckResultRes.java new file mode 100644 index 0000000..ded1b89 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/CheckResultRes.java @@ -0,0 +1,22 @@ +package com.thing.eq.eqcheck.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/11 0011 16:20:14 + */ +@Data +public class CheckResultRes { + + @Schema(description = "点检记录") + private EqSpotCheckRecordDTO eqSpotCheckRecordDTO; + + @Schema(description = "点检记录关联的标准检查项") + private List standardCheckResultDTOList; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DataVo.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DataVo.java new file mode 100644 index 0000000..8b00190 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DataVo.java @@ -0,0 +1,17 @@ +package com.thing.eq.eqcheck.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/15 0015 18:16:24 + */ +@Data +public class DataVo { + @Schema(description = "模板项目id") + private Long id; + @Schema(description = "模板项目名称") + private String name; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceBaseInfoDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceBaseInfoDTO.java new file mode 100644 index 0000000..e8c97c4 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceBaseInfoDTO.java @@ -0,0 +1,38 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + + +/** + * @author zy + * @version 1.0 + * @date 2021/10/13 0013 15:58:35 + */ +@Data +public class DeviceBaseInfoDTO extends ThingDTO { + @Schema(description = "合计项") + private Integer totalCount; + + @Schema(description = "未检(漏检)数") + private Integer unCheckCount; + + @Schema(description = "异常数") + private Integer abnormalCount; + + @Schema(description = "巡检人") + private String userIdStr; + + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @Schema(description = "巡检时间") + private Date checkTime; + + @Schema(description = "是否巡检 0未巡检 1已巡检") + private String check; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceCheckRecordVo.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceCheckRecordVo.java new file mode 100644 index 0000000..27c5e5b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/DeviceCheckRecordVo.java @@ -0,0 +1,35 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/15 0015 9:39:23 + */ +@Data +public class DeviceCheckRecordVo extends ThingDTO { + @Schema(description = "巡检记录结果id") + private Long id; + @Schema(description = "巡检记录id") + private Long checkRecordId; + @Schema(description = "巡检计划id") + private Long patrolCheckPlanId; + @Schema(description = "total") + private Integer total; + @Schema(description = "未检(漏检)数") + private Integer unCheckCount; + @Schema(description = "异常数") + private Integer abnormalCount; + @Schema(description = "巡检开始时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkStart; + @Schema(description = "巡检结束时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkEnd; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckSettingDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckSettingDTO.java new file mode 100644 index 0000000..2c5747e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckSettingDTO.java @@ -0,0 +1,38 @@ +package com.thing.eq.eqcheck.dto; + + +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 设备检查设置 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-23 +*/ +@Data +@Schema(description = "设备检查设置") +public class EqCheckSettingDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "设备id") + private Long thingId; + @Schema(description = "标准id") + private Long standardId; + @Schema(description = "操作人id(弃用)") + private Long userId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "类型0点检 1巡检") + private String type; + @Schema(description = "巡检计划id") + private Long patrolcheckPlanId; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDTO.java new file mode 100644 index 0000000..de43094 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDTO.java @@ -0,0 +1,56 @@ +package com.thing.eq.eqcheck.dto; + + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@Schema(description = "检查标准") +public class EqCheckStandardDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + @Schema(description = "检查标准id") + private Long id; + @Schema(description = "设备组id") + private Long groupId; + @Schema(description = "标准名称(新增时必填)", required = true) + @NotEmpty(message = "标准名称不能为空", groups = AddGroup.class) + private String name; + @Schema(description = "检查工具") + private String checkTool; + @Schema(description = "检查目的") + private String checkPurpose; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "备注") + private String tip; + @Schema(description = "设备类型id(新增时必填)", required = true) + @NotNull(message = "设备类型不能为空", groups = AddGroup.class) + private Long deviceTypeId; + @Schema(description = "模板细项") + private List detailDTOS; + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户code") + private Long tenantCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailDTO.java new file mode 100644 index 0000000..7cc5fc3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailDTO.java @@ -0,0 +1,39 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 设备检查标准明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备检查标准明细") +public class EqCheckStandardDetailDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "检查标准id") + private Long standardId; + @Schema(description = "检查名称") + private String name; + @Schema(description = "参考值(巡检记录选项)") + private String referenceValue; + @Schema(description = "上限") + private String upperLimit; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "下限") + private String lowerLimit; + @Schema(description = "判定类型 0:上下限值判定 1:布尔类型判定(单选) 2:采集结果判定 3:多选") + private String type; + @Schema(description = "备注") + private String remark; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailResultDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailResultDTO.java new file mode 100644 index 0000000..e37e1f2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqCheckStandardDetailResultDTO.java @@ -0,0 +1,43 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 设备检查标准明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备检查标准明细结果表") +public class EqCheckStandardDetailResultDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "检查标准id") + private Long standardId; + @Schema(description = "检查名称") + private String name; + @Schema(description = "参考值(巡检记录选项)") + private String referencedescription; + @Schema(description = "上限") + private String upperLimit; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "下限") + private String lowerLimit; + @Schema(description = "判定类型 0:上下限值判定 1:布尔类型判定(单选) 2:采集结果判定 3:多选") + private String type; + @Schema(description = "备注") + private String remark; + @Schema(description = "实际值") + private String actualValue; + @Schema(description = "检查结果") + private String checkValue; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanDTO.java new file mode 100644 index 0000000..27402d0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanDTO.java @@ -0,0 +1,132 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 设备巡检计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备巡检计划") +public class EqPatrolCheckPlanDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id(修改时必填)",required = true) + @NotNull(message="id不能为空", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "创建人") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新时间") + private Date updateDate; + + @Schema(description = "更新人") + private Long updater; + + @Schema(description = "巡检计划名称(新增时必填)",required = true) + @NotEmpty(message="巡检计划名称不能为空", groups = AddGroup.class) + private String name; + + @Schema(description = "检查周期") + private Integer checkCycle; + + @Schema(description = "检查周期单位") + private String checkCycleUnit; + + @Schema(description = "计划开始时间(新增时必填)",required = true) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @NotNull(message="计划开始时间不能为空", groups = AddGroup.class) + private Date planStartTime; + + @Schema(description = "") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date planEndTime; + + @Schema(description = "计划状态(启用:ACTIVE,停用:INACTIVE)") + private String planStatus; + + @Schema(description = "计划执行人(新增时必填)",required = true) + @NotEmpty(message="计划执行人不能为空", groups = AddGroup.class) + private String userId; + + @Schema(description = "提醒时间(计划开始时间前多少天提醒)") + private String remindTime; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户code") + private Long tenantCode; + + @Schema(description = "巡检计划编号") + private String planNo; + + @Schema(description = "执行范围时间参数值") + private Long planRangeParam; + + @Schema(description = "截止时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkEndTime; + + @Schema(description = "循环类型 0:单次 1:多次 (新增时必填)",required = true) + @NotEmpty(message="循环类型不能为空", groups = AddGroup.class) + private String type; + + @Schema(description = "下次记录批次时间") + private Date nextBatchTime; + + @Schema(description = "实际计划执行人") + private Long actUserId; + + @Schema(description = "实际巡检时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date actCheckTime; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "巡检计划单号") + private String patrolCheckPlanNo; + + @Schema(description = "提醒时间单位(hour/day)") + private String remindTimeUnit; + + @Schema(description = "执行状态 0:待执行 1:执行中 2:已完成(整个项目的状态)") + private String executePlanStatus; + + private String isRemind; + + @Schema(description = "设备执行状态 0:未执行 1:已执行(扫设备进来看当前计划当前设备的状态)") + private String isCheck; + + @Schema(description = "用户组id") + private Long groupId; + + @Schema(description = "用户组名称,展示用") + private String groupName; + + @Schema(description = "设备信息") + private List eqInfos; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanRes.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanRes.java new file mode 100644 index 0000000..458a768 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanRes.java @@ -0,0 +1,25 @@ +package com.thing.eq.eqcheck.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/21 0021 14:32:05 + */ +@Data +public class EqPatrolCheckPlanRes extends EqPatrolCheckPlanDTO { + @Schema(description = "计划巡检人名") + private String planCheckUserName; + + @Schema(description = "总设备数") + private Integer deviceCount; + + @Schema(description = "未检任务数") + private Integer unCheckCount; + + @Schema(description = "异常任务数") + private Integer abnormalCount; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanResDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanResDTO.java new file mode 100644 index 0000000..2797430 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckPlanResDTO.java @@ -0,0 +1,25 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import java.io.Serializable; +import java.util.List; + +/** +* 设备巡检计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备巡检计划") +public class EqPatrolCheckPlanResDTO extends EqPatrolCheckPlanDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "设备列表",required = true) + @NotEmpty(message="设备列表不能为空", groups = DefaultGroup.class) + private List deviceBaseInfoDTOS; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordDTO.java new file mode 100644 index 0000000..eaad210 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordDTO.java @@ -0,0 +1,74 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + +/** +* 设备巡检记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备巡检记录") +public class EqPatrolCheckRecordDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "巡检记录id (修改时必填)",required = true) + @NotNull(message = "id不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "巡检计划id (新增时必填)",required = true) + @NotNull(message = "巡检计划id不能为空",groups = AddGroup.class) + private Long patrolCheckPlanId; + @Schema(description = "巡检人id") + private Long userId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "未检设备数") + private Integer noCheckDeviceCount; + @Schema(description = "异常设备数") + private Integer abnormalDeviceCount; + @Schema(description = "总未检(漏检)巡检项数") + private Integer totalUnCheckCount; + @Schema(description = "总异常巡检项数") + private Integer totalAbnormalCount; + @Schema(description = "巡检开始时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkStartTime; + @Schema(description = "巡检结束时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkEndTime; + @Schema(description = "备注") + private String tip; + @Schema(description = "巡检时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date checkTime; + @Schema(description = "计划巡检时间") + private String checkPlanTime; + + @Schema(description = "巡检记录单号") + private String patrolCheckRecordNo; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户code") + private Long tenantCode; + + @Schema(description = "设备id") + private Long thingId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordResDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordResDTO.java new file mode 100644 index 0000000..2e87d77 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordResDTO.java @@ -0,0 +1,33 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.core.web.response.PageData; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import java.io.Serializable; + +/** +* 设备巡检记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备巡检记录") +public class EqPatrolCheckRecordResDTO extends EqPatrolCheckRecordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "巡检计划名称") + private String planName; + @Schema(description = "巡检人名称") + private String userName; + @Schema(description = "计划巡检人id") + private String planUserId; + @Schema(description = "计划巡检人名称") + private String planUserName; + @Schema(description = "巡检项目列表") + private PageData deviceCheckRecordVos; + @Schema(description = "设备名称") + private String eqName; + @Schema(description = "设备编号") + private String eqCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordUpdateDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordUpdateDTO.java new file mode 100644 index 0000000..b9053ea --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqPatrolCheckRecordUpdateDTO.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.eq.checkresult.dto.CheckResultDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/15 0015 17:28:37 + */ +@Data +public class EqPatrolCheckRecordUpdateDTO extends EqPatrolCheckRecordDTO { + @Schema(description = "巡检结果") + private List checkResultDTOS; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanDTO.java new file mode 100644 index 0000000..e545bd0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanDTO.java @@ -0,0 +1,62 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 点检计划 +* +* @author zy aa@aa,com +* @since 3.0 2021-10-12 +*/ +@Data +@Schema(description = "点检计划") +public class EqSpotCheckPlanDTO extends ThingDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "开机点检项") + private String checkNameList; + @Schema(description = "开机点检项id集合") + private List checkIdList; + @Schema(description = "开机点检项集合") + private List dataList; + @Schema(description = "上次点检时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date lastCheckTime; + @Schema(description = "计划点检人") + private String useId; + @Schema(description = "计划点检人集合") + private List useIdList; + @Schema(description = "计划细项") + private List detailDTOS; + @Schema(description = "计划点检人名") + private String operatorNameList; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "点检计划单号") + private String spotCheckNo; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户code") + private Long tenantCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanParamDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanParamDTO.java new file mode 100644 index 0000000..0690409 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckPlanParamDTO.java @@ -0,0 +1,48 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 点检计划 +* +* @author zy aa@aa,com +* @since 3.0 2021-10-12 +*/ +@Data +@Schema(description = "点检计划") +public class EqSpotCheckPlanParamDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "设备thingidList") + private List thingIdList; + @Schema(description = "开机点检项id集合") + private List standardIdList; + @Schema(description = "上次点检时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date lastCheckTime; + @Schema(description = "操作人") + @NotEmpty(message = "请选择操作人",groups = AddGroup.class) + @NotNull(message = "请选择操作人",groups = AddGroup.class) + private String useId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckRecordDTO.java new file mode 100644 index 0000000..902e52e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/EqSpotCheckRecordDTO.java @@ -0,0 +1,70 @@ +package com.thing.eq.eqcheck.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@Schema(description = "设备点检记录") +public class EqSpotCheckRecordDTO extends ThingDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @NotNull(message = "点检记录id不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "标准id(弃用)") + private Long standardId; + @Schema(description = "点检人id") + private Long userId; + @Schema(description = "操作人名称") + private String operatorName; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "检查项目总数") + private Integer totalCount; + @Schema(description = "异常数") + private Integer abnormalCount; + @Schema(description = "未检数") + private Integer unCheckCount; + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @Schema(description = "点检时间") + private Date checkTime; + @Schema(description = "用时(小时)") + private String useTime; + @Schema(description = "检查结果") + private String checkResult; + @Schema(description = "备注") + private String tip; + @Schema(description = "点检计划id") + @NotNull(message = "点检计划id不能为空",groups = AddGroup.class) + private Long spotCheckId; + @Schema(description = "点检记录单号") + private String spotCheckRecordNo; + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户code") + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ExecutePlanDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ExecutePlanDTO.java new file mode 100644 index 0000000..7c3eb65 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ExecutePlanDTO.java @@ -0,0 +1,35 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/8 0008 10:59:13 + */ +@Data +public class ExecutePlanDTO { + + @Schema(description = "巡检计划id",required = true) + @NotNull(message="巡检计划id不能为空", groups = DefaultGroup.class) + private Long checkPlanId; + + @Schema(description = "巡检人",required = true) + @NotNull(message="巡检人id不能为空", groups = DefaultGroup.class) + private Long checkUserId; + + @Schema(description = "巡检时间",required = true) + private String checkTime; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "巡检结果") + private List checkResultDTOS; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/SepcialTsKvParam.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/SepcialTsKvParam.java new file mode 100644 index 0000000..b1e6786 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/SepcialTsKvParam.java @@ -0,0 +1,20 @@ +package com.thing.eq.eqcheck.dto; + +import lombok.Data; + +import java.io.Serializable; + + +/** + * 取时间序列边界值的时候用 + * @author ChenYang + */ +@Data +public class SepcialTsKvParam implements Serializable { + + private static final long serialVersionUID = 1565617022057674912L; + + private String key; + + private TimeValueParam timeValue; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/StandardCheckResultDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/StandardCheckResultDTO.java new file mode 100644 index 0000000..1960d47 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/StandardCheckResultDTO.java @@ -0,0 +1,25 @@ +package com.thing.eq.eqcheck.dto; + +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/11 0011 16:21:27 + */ +@Data +public class StandardCheckResultDTO extends EqCheckStandardDetailDTO { + + /** + * 标准细项实际值 + */ + private String actualValue; + /** + * 标准细项判定结果 + */ + private String checkValue; + /** + * 标准细项判定备注 + */ + private String remark; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TbDataDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TbDataDTO.java new file mode 100644 index 0000000..71494f0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TbDataDTO.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqcheck.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zzx + * @date 2022/09/19 10:16:24 + */ +@Data +public class TbDataDTO { + @Schema(description = "物id") + private Long thingId; + @Schema(description = "属性集合") + private List keyList; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ThingDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ThingDTO.java new file mode 100644 index 0000000..6454a3f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/ThingDTO.java @@ -0,0 +1,44 @@ +package com.thing.eq.eqcheck.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/15 0015 10:09:47 + */ +@Data +public class ThingDTO { + @Schema(description = "设备物id") + private Long thingId; + @Schema(description = "物编号") + private String thingCode; + @Schema(description = "设备名称") + private String thingName; + @Schema(description = "规格型号") + private String thingStandard; + @Schema(description = "设备类型") + private String deviceType; + @Schema(description = "设备位置") + private String devicePosition; + @Schema(description = "设备状态") + private String thingStatus; + @Schema(description = "模板标准名称") + private String standardNameList; + @Schema(description = "使用部门") + private Long useDeptId; + @Schema(description = "巡检标准id") + private List standardIdList; + @Schema(description = "设备编号") + private String eqCode; + + @Schema(description = "部件库存") + private String stock; + + @Schema(description = "是否执行过 0未执行 1已执行") + private String check; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TimeValueParam.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TimeValueParam.java new file mode 100644 index 0000000..ee0e904 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/TimeValueParam.java @@ -0,0 +1,15 @@ +package com.thing.eq.eqcheck.dto; + +import lombok.Data; + +import java.io.Serializable; + +@Data +public class TimeValueParam implements Serializable { + + private static final long serialVersionUID = 2949218932491796852L; + + private String time; + + private Object value; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/UpdateStatusVo.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/UpdateStatusVo.java new file mode 100644 index 0000000..3a6cadf --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/dto/UpdateStatusVo.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqcheck.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/20 0020 14:17:56 + */ +@Data +public class UpdateStatusVo { + @Schema(description = "巡检计划id",required = true) + @NotEmpty(message = "请选择要修改的巡检计划",groups = DefaultGroup.class) + private List ids; + + @Schema(description = "状态值",required = true) + @NotNull(message = "状态值不能为空",groups = DefaultGroup.class) + @NotBlank(message = "状态值不能为空",groups = DefaultGroup.class) + private String status; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckSettingEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckSettingEntity.java new file mode 100644 index 0000000..686a473 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckSettingEntity.java @@ -0,0 +1,63 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备检查设置 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-23 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_check_setting") +public class EqCheckSettingEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 设备id + */ + private Long thingId; + /** + * 标准id + */ + private Long standardId; + /** + * 操作人id(弃用) + */ + private Long userId; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 类型0点检 1巡检 + */ + private String type; + /** + * 巡检计划id + */ + private Long patrolcheckPlanId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardDetailEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardDetailEntity.java new file mode 100644 index 0000000..1633f55 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardDetailEntity.java @@ -0,0 +1,68 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备检查标准明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_check_standard_detail") +public class EqCheckStandardDetailEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 检查标准id + */ + private Long standardId; + /** + * 检查名称 + */ + private String name; + /** + * 参考值 + */ + private String referenceValue; + /** + * 上限 + */ + private String upperLimit; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 下限 + */ + private String lowerLimit; + /** + * 判定类型 0:上下限值判定 1:布尔类型判定 2:采集结果判定 + */ + private String type; + /** + * 备注 + */ + private String remark; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardEntity.java new file mode 100644 index 0000000..fc0ca4c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqCheckStandardEntity.java @@ -0,0 +1,72 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_check_standard") +public class EqCheckStandardEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 设备组id + */ + private Long groupId; + /** + * 标准名称 + */ + private String name; + /** + * 检查工具 + */ + private String checkTool; + /** + * 检查目的 + */ + private String checkPurpose; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 备注 + */ + private String tip; + /** + * 设备类型id + */ + private Long deviceTypeId; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckPlanEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckPlanEntity.java new file mode 100644 index 0000000..d2386e8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckPlanEntity.java @@ -0,0 +1,137 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备巡检计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("eq_patrol_check_plan") +public class EqPatrolCheckPlanEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 巡检计划名称 + */ + private String name; + /** + * 检查周期 + */ + private Integer checkCycle; + /** + * 检查周期单位 + */ + private String checkCycleUnit; + /** + * 计划开始时间 + */ + private Date planStartTime; + /** + * 计划结束时间 + */ + private Date planEndTime; + /** + * 计划状态 + */ + private String planStatus; + /** + * 计划执行人 + */ + private String userId; + /** + * 提醒时间(计划开始时间前多少天提醒) + */ + private String remindTime; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; + /** + * 巡检计划编号 + */ + private String planNo; + /** + * 执行范围设置 + */ + private Long planRangeParam; + /** + * 截止时间 + */ + private Date checkEndTime; + /** + * 循环类型 0:单次 1:多次 + */ + private String type; + /** + * 下次记录批次时间 + */ + private Date nextBatchTime; + /** + * 实际巡检人 + */ + private Long actUserId; + /** + * 实际巡检时间 + */ + private Date actCheckTime; + /** + * 备注 + */ + private String remark; + /** + * 巡检计划单号 + */ + private String patrolCheckPlanNo; + /** + * 提醒时间单位(hour/day) + */ + private String remindTimeUnit; + /** + * 计划执行状态 + */ + private String executePlanStatus; + + /** + * 是否已提醒 0未提醒 1已提醒 + */ + private String isRemind; + + /** + * 用户组id + */ + private Long groupId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckRecordEntity.java new file mode 100644 index 0000000..6e417e8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqPatrolCheckRecordEntity.java @@ -0,0 +1,89 @@ +package com.thing.eq.eqcheck.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_patrol_check_record") +public class EqPatrolCheckRecordEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 巡检计划id + */ + private Long patrolCheckPlanId; + /** + * 操作人id + */ + private Long userId; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 未检设备数 + */ + private Integer noCheckDeviceCount; + /** + * 异常设备数 + */ + private Integer abnormalDeviceCount; + /** + * 检查开始时间 + */ + private Date checkStartTime; + /** + * 检查结束时间 + */ + private Date checkEndTime; + /** + * 备注 + */ + private String tip; + /** + * 巡检时间 + */ + private Date checkTime; + /** + * 计划巡检时间 + */ + private String checkPlanTime; + /** + * 巡检记录单号 + */ + private String patrolCheckRecordNo; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckPlanEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckPlanEntity.java new file mode 100644 index 0000000..94c0630 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckPlanEntity.java @@ -0,0 +1,64 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 点检计划 + * + * @author zy aa@aa,com + * @since 3.0 2021-10-12 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_spot_check_plan") +public class EqSpotCheckPlanEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 设备thingid + */ + private Long thingId; + /** + * 上次点检时间 + */ + private Date lastCheckTime; + /** + * 操作人 + */ + private String useId; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 更新时间 + */ + private String spotCheckNo; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckRecordEntity.java new file mode 100644 index 0000000..2702bcf --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/entity/EqSpotCheckRecordEntity.java @@ -0,0 +1,84 @@ +package com.thing.eq.eqcheck.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_spot_check_record") +public class EqSpotCheckRecordEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 设备id + */ + private Long thingId; + /** + * 标准id(弃用) + */ + private Long standardId; + /** + * 点检人id + */ + private Long userId; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 点检时间 + */ + private Date checkTime; + /** + * 用时(小时) + */ + private String useTime; + /** + * 检查结果 + */ + private String checkResult; + /** + * 备注 + */ + private String tip; + /** + * 点检计划id + */ + private Long spotCheckId; + /** + * 点检记录单号 + */ + private String spotCheckRecordNo; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckSettingExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckSettingExcel.java new file mode 100644 index 0000000..7748a94 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckSettingExcel.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备检查设置 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-23 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqCheckSettingExcel { + @Excel(name = "id", orderNum = "1") + private Long id; + @Excel(name = "设备id", orderNum = "2") + private Long thingId; + @Excel(name = "标准id", orderNum = "3") + private Long standardId; + @Excel(name = "操作人id", orderNum = "4") + private Long userId; + @Excel(name = "创建者", orderNum = "5") + private Long creator; + @Excel(name = "创建时间", orderNum = "6") + private Date createDate; + @Excel(name = "更新者", orderNum = "7") + private Long updater; + @Excel(name = "更新时间", orderNum = "8") + private Date updateDate; + @Excel(name = "类型0点检 1巡检", orderNum = "9") + private String type; + @Excel(name = "巡检计划id", orderNum = "10") + private Long patrolcheckPlanId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardDetailExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardDetailExcel.java new file mode 100644 index 0000000..63b997b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardDetailExcel.java @@ -0,0 +1,44 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备检查标准明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqCheckStandardDetailExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "检查标准id", orderNum= "2") + private Long standardId; + @Excel(name = "检查名称", orderNum= "3") + private String name; + @Excel(name = "参考值", orderNum= "4") + private String referenceValue; + @Excel(name = "上限", orderNum= "5") + private String upperLimit; + @Excel(name = "创建者", orderNum= "6") + private Long creator; + @Excel(name = "创建时间", orderNum= "7") + private Date createDate; + @Excel(name = "更新者", orderNum= "8") + private Long updater; + @Excel(name = "更新时间", orderNum= "9") + private Date updateDate; + @Excel(name = "下限", orderNum= "10") + private String lowerLimit; + @Excel(name = "判定类型", orderNum= "11") + private String type; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardExcel.java new file mode 100644 index 0000000..45b4ddb --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqCheckStandardExcel.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqCheckStandardExcel { + @Excel(name = "Long", orderNum= "1") + private Long id; + @Excel(name = "设备组id", orderNum= "2") + private Long groupId; + @Excel(name = "标准名称", orderNum= "3") + private String name; + @Excel(name = "检查工具", orderNum= "4") + private String checkTool; + @Excel(name = "检查目的", orderNum= "5") + private String checkPurpose; + @Excel(name = "创建者", orderNum= "6") + private Long creator; + @Excel(name = "创建时间", orderNum= "7") + private Date createDate; + @Excel(name = "更新者", orderNum= "8") + private Long updater; + @Excel(name = "更新时间", orderNum= "9") + private Date updateDate; + @Excel(name = "备注", orderNum= "10") + private String tip; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckPlanExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckPlanExcel.java new file mode 100644 index 0000000..6903327 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckPlanExcel.java @@ -0,0 +1,63 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备巡检计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqPatrolCheckPlanExcel { + @ExcelIgnore() + private Long id; + + @Excel(name= "巡检计划单号", orderNum = "1") + private String patrolCheckPlanNo; + + @Excel(name= "计划状态", orderNum = "2") + private String planStatus; + + @Excel(name= "执行状态 未执行_0,执行中_1,已完成_2", orderNum = "3") + private String executePlanStatus; + + @Excel(name= "巡检计划名称", orderNum = "4") + private String name; + +// @Excel(name= "计划所属部门") +// private String deptName; + + @Excel(name= "检查周期", orderNum = "5") + private String checkCycle; + + @Excel(name= "计划开始时间", orderNum = "6") + private Date planStartTime; + + @Excel(name= "计划截止时间", orderNum = "7") + private Date checkEndTime; + + @Excel(name= "计划执行人", orderNum = "8") + private String user; + + @Excel(name= "总设备数", orderNum = "9") + private Integer deviceCount; + + @Excel(name= "未检任务数", orderNum = "10") + private Integer unCheckCount; + + @Excel(name= "异常任务数", orderNum = "11") + private Integer abnormalCount; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordDetailExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordDetailExcel.java new file mode 100644 index 0000000..af492fb --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordDetailExcel.java @@ -0,0 +1,63 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import lombok.Data; + +import java.util.Date; + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +public class EqPatrolCheckRecordDetailExcel { + @ExcelIgnore() + private Long id; + + @Excel(name = "巡检记录单号", orderNum = "1") + private String patrolCheckRecordNo; + + @Excel(name = "设备编号", orderNum ="2") + private String eqCode; + + @Excel(name = "设备名称", orderNum = "3") + private String eqName; + + @Excel(name = "生成时间", orderNum = "4") + private Date createDate; + + @Excel(name = "计划名称", orderNum = "5") + private String planName; + + @Excel(name = "计划巡检人", orderNum = "6") + private String planUserName; + + @Excel(name = "巡检人", orderNum = "7") + private String checkUserName; + + @Excel(name = "计划巡检时间", orderNum = "8") + private String checkPlanTime; + + @Excel(name = "巡检时间", orderNum = "9") + private Date checkTime; + + @Excel(name = "检查项目", orderNum = "10") + private String checkName; + + @Excel(name = "上限", orderNum = "11") + private String upperLimit; + + @Excel(name = "下限", orderNum = "12") + private String lowerLimit; + + @Excel(name = "检查值", orderNum = "13") + private String actualValue; + + @Excel(name = "检查结果", orderNum = "14") + private String checkValue; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordExcel.java new file mode 100644 index 0000000..5991d24 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqPatrolCheckRecordExcel.java @@ -0,0 +1,48 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import lombok.Data; + +import java.util.Date; + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +public class EqPatrolCheckRecordExcel { + @ExcelIgnore() + private Long id; + + @Excel(name = "巡检记录单号", orderNum = "1") + private String patrolCheckRecordNo; + + @Excel(name = "计划名称", orderNum = "2") + private String planName; + + @Excel(name = "设备名称", orderNum = "3") + private String eqName; + @Excel(name = "设备编号", orderNum = "4") + private String eqCode; + + @Excel(name = "计划巡检人", orderNum = "5") + private String planUserName; + + @Excel(name = "巡检人", orderNum = "6") + private String checkUserName; + +// @Excel(name = "异常设备数") +// private Integer abnormalDeviceCount; +// +// @Excel(name = "未检设备数") +// private Integer noCheckDeviceCount; + + @Excel(name = "计划巡检时间", orderNum = "7") + private String checkPlanTime; + + @Excel(name = "巡检时间", orderNum = "8") + private Date checkTime; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckPlanExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckPlanExcel.java new file mode 100644 index 0000000..b26dee3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckPlanExcel.java @@ -0,0 +1,35 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import lombok.Data; + +/** + * 点检计划 + * + * @author zy aa@aa,com + * @since 3.0 2021-10-12 + */ +@Data +public class EqSpotCheckPlanExcel { + @ExcelIgnore() + private Long id; + + @Excel(name = "点检计划单号", orderNum = "1") + private String spotCheckNo; + + @Excel(name = "设备名称", orderNum = "2") + private String thingName; + + @Excel(name = "设备编号", orderNum = "3") + private String eqCode; + + @Excel(name = "规格型号", orderNum = "4") + private String thingStandard; + + @Excel(name = "点检项", orderNum = "5") + private String checkNameList; + + @Excel(name = "计划点检人", orderNum = "6") + private String operatorNameList; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckRecordExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckRecordExcel.java new file mode 100644 index 0000000..f14c2b9 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/excel/EqSpotCheckRecordExcel.java @@ -0,0 +1,43 @@ +package com.thing.eq.eqcheck.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import lombok.Data; + +import java.util.Date; + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +public class EqSpotCheckRecordExcel { + @ExcelIgnore() + private Long id; + @Excel(name= "点检记录单号", orderNum = "1") + private String spotCheckRecordNo; + + @Excel(name= "设备名称", orderNum = "2") + private String thingName; + + @Excel(name= "设备编号", orderNum = "3") + private String eqCode; + + @Excel(name= "规格型号", orderNum = "4") + private String thingStandard; + + @Excel(name= "点检时间", orderNum = "5") + private Date checkTime; + + @Excel(name= "项目总数", orderNum = "6") + private Integer totalCount; + + @Excel(name= "漏检数", orderNum = "7") + private Integer unCheckCount; + + @Excel(name= "异常数", orderNum = "8") + private Integer abnormalCount; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckSettingMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckSettingMapper.java new file mode 100644 index 0000000..5e75f96 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckSettingMapper.java @@ -0,0 +1,34 @@ +package com.thing.eq.eqcheck.mapper; + + + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.entity.EqCheckSettingEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备检查设置 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-23 +*/ +@Mapper +public interface EqCheckSettingMapper extends PowerBaseMapper { + + /** + * 分页查巡检记录设备列表模板检查项 + */ + Page searchSettingThing(Page page, @Param("params") Map params); + + /** + * 查巡检记录设备列表模板检查项 + */ + List searchSettingThing(@Param("params") Map params); + + int getPatrolPlanCountByEqId(@Param("eqId") Long eqId, @Param("type") String type); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardDetailMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardDetailMapper.java new file mode 100644 index 0000000..806f80c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardDetailMapper.java @@ -0,0 +1,16 @@ +package com.thing.eq.eqcheck.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 设备检查标准明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqCheckStandardDetailMapper extends PowerBaseMapper { + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardMapper.java new file mode 100644 index 0000000..11b741c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqCheckStandardMapper.java @@ -0,0 +1,19 @@ +package com.thing.eq.eqcheck.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.dto.EqCheckStandardDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 检查标准 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqCheckStandardMapper extends PowerBaseMapper { + + EqCheckStandardDTO getLists(String name); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckPlanMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckPlanMapper.java new file mode 100644 index 0000000..dbd539d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckPlanMapper.java @@ -0,0 +1,28 @@ +package com.thing.eq.eqcheck.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.dto.EqPatrolCheckPlanDTO; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备巡检计划 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqPatrolCheckPlanMapper extends PowerBaseMapper { + + List getEqPatrolPlanList(Map params); + + void updateIsRemind(@Param("id") Long id, @Param("isRemind") String isRemind); + + List getByGroupId(@Param("id") Long id, @Param("tenantCode") Long tenantCode); + + EqPatrolCheckPlanDTO getByName(String name); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckRecordMapper.java new file mode 100644 index 0000000..2e104f7 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqPatrolCheckRecordMapper.java @@ -0,0 +1,33 @@ +package com.thing.eq.eqcheck.mapper; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.dto.EqPatrolCheckRecordResDTO; +import com.thing.eq.eqcheck.entity.EqPatrolCheckRecordEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备巡检记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqPatrolCheckRecordMapper extends PowerBaseMapper { + /** + * 分页查巡检记录 + */ + List searchRecord(Page page, @Param("params") Map params); + /** + * 查巡检记录 + */ + List searchRecord(@Param("params") Map params); + /** + * 根据记录id查巡检记录 + */ + EqPatrolCheckRecordResDTO getPatrolRecordById(Long recordId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckPlanMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckPlanMapper.java new file mode 100644 index 0000000..58145f6 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckPlanMapper.java @@ -0,0 +1,31 @@ +package com.thing.eq.eqcheck.mapper; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.dto.EqSpotCheckPlanDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckPlanEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 点检计划 +* +* @author zy aa@aa,com +* @since 3.0 2021-10-12 +*/ +@Mapper +public interface EqSpotCheckPlanMapper extends PowerBaseMapper { + + /** + * 关联条件查询点检计划(分页) + */ + List searchPlan(Page page, @Param("params") Map params); + /** + * 关联条件查询点检计划 + */ + List searchPlan(@Param("params") Map params); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckRecordMapper.java new file mode 100644 index 0000000..6ced080 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/mapper/EqSpotCheckRecordMapper.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqcheck.mapper; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqcheck.dto.EqSpotCheckRecordDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckRecordEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备点检记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqSpotCheckRecordMapper extends PowerBaseMapper { + + Page searchRecord(Page page, @Param("params") Map params); + + List searchRecord(@Param("params") Map params); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckSettingService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckSettingService.java new file mode 100644 index 0000000..ba6ce10 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckSettingService.java @@ -0,0 +1,50 @@ +package com.thing.eq.eqcheck.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.entity.EqCheckSettingEntity; +import java.util.List; +import java.util.Map; + +/** + * 设备检查设置 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-23 + */ +public interface EqCheckSettingService extends IBaseService { + /** + * 根据设备物id获取点巡检模板 + */ + List getSettingList(Long thingId, String checkType); + + /** + * 根据设备物id删除点巡检模板 + */ + void deleteByThingId(Long thingId); + /** + * 根据巡检计划id获取点巡检模板 + */ + List getSettingListByPatrolPlanId(Long patrolPlanId); + /** + * 根据巡检计划id分页获取点巡检模板 + */ + List searchSettingThing(Map params); + /** + * 根据巡检计划id删除巡检设置 + */ + void deletePatrolByPatrolPlanId(Long patrolPlanId); + /** + * 根据巡检计划id获取点巡检模板 + */ + List getSettingListByPatrolPlanIdAndThingId(Long patrolPlanId, Long thingId); + + /** + *根据设备id检查是否存在巡检或点检计划 + */ + int getPatrolPlanCountByEqId(Long eqId, String type); + /** + *根据设备id检查是否存在巡检或点检计划 + */ + String getSettingListByStandardId(Long standardId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardDetailService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardDetailService.java new file mode 100644 index 0000000..0a88004 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardDetailService.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqcheck.service; + + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.dto.StandardCheckResultDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; + +import java.util.List; + +/** + * 设备检查标准明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqCheckStandardDetailService extends IBaseService { + /** + * 获取设备检查项 + */ + List getListByStandardId(Long standardId); + /** + * 获取设备检查项 + */ + List getCheckResultByStandardId(Long standardId); + + /** + * 获取设备所有检查项(点检专用) + */ + List getAllStandardDetailByThingId(Long thingId, String checkType); + + /** + * 根据模板id删除检查项 + */ + void deleteStandardDetailByStandardId(Long standardId); + + /** + * 获取设备详情检查项 + */ + List getCheckResultDetailByStandardIdList(List standardIdList); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardService.java new file mode 100644 index 0000000..fe8665a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqCheckStandardService.java @@ -0,0 +1,24 @@ +package com.thing.eq.eqcheck.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqCheckStandardDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardEntity; + +import java.util.List; + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqCheckStandardService extends IBaseService { + /** + * 根据standardIdList获取模板标准 + */ + List getStandardListByStandardIdList(List standardIdList); + + + EqCheckStandardDTO getLists(String name); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckPlanService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckPlanService.java new file mode 100644 index 0000000..1da591d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckPlanService.java @@ -0,0 +1,45 @@ +package com.thing.eq.eqcheck.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqPatrolCheckPlanDTO; +import com.thing.eq.eqcheck.dto.EqPatrolCheckPlanResDTO; +import com.thing.eq.eqcheck.dto.ExecutePlanDTO; +import com.thing.eq.eqcheck.dto.TbDataDTO; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; + +import java.util.List; +import java.util.Map; + +/** + * 设备巡检计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqPatrolCheckPlanService extends IBaseService { + List exportData(Map params); + + /** + * 查询状态为激活(启用)的巡检计划 + */ + List getCheckPlanByStatus(String status, String isRemind); + + + void savePatrolCheckPlan(EqPatrolCheckPlanResDTO dto); + + void updatePatrolCheckPlan(EqPatrolCheckPlanResDTO dto); + + void executePlan(ExecutePlanDTO dto); + void executePlanNew(ExecutePlanDTO dto); + void updateIsRemind(Long id, String s); + + List getCheckPlanByStatusAndType(String patrolPlanStatus, String type); + + List getByGroupId(Long id, Long tenantCode); + + PageData page(Map params); + + Map> getTbLatestData(List tbDataDTOList); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckRecordService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckRecordService.java new file mode 100644 index 0000000..9e0f432 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqPatrolCheckRecordService.java @@ -0,0 +1,55 @@ +package com.thing.eq.eqcheck.service; + + + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqPatrolCheckRecordDTO; +import com.thing.eq.eqcheck.dto.EqPatrolCheckRecordResDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckRecordEntity; + +import java.util.List; +import java.util.Map; + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqPatrolCheckRecordService extends IBaseService { + + /** + * 巡检记录保存 + */ + Long savePatrolRecord(EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO); + /** + * 巡检记录分页查询 + */ + Page searchRecordByPage(Map params); + + List exportData(Map params); + + /** + * 巡检记录保存 + */ + EqPatrolCheckRecordResDTO getPatrolRecordById(Long recordId); + + /** + * 巡检记录保存 + */ + List getStandardDetailByCheckPlan(Long checkPlanId, Long thingId); + + /** + * 巡检记录保存 + */ + List getByCheckPlanId(Long checkPlanId); + + /** + * 巡检记录保存 + */ + Long getRecordCountByCheckPlanId(Long checkPlanId); + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckPlanService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckPlanService.java new file mode 100644 index 0000000..c0ad59f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckPlanService.java @@ -0,0 +1,37 @@ +package com.thing.eq.eqcheck.service; + + + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqSpotCheckPlanDTO; +import com.thing.eq.eqcheck.dto.EqSpotCheckPlanParamDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckPlanEntity; + +import java.util.List; +import java.util.Map; + +/** + * 点检计划 + * + * @author zy aa@aa,com + * @since 3.0 2021-10-12 + */ +public interface EqSpotCheckPlanService extends IBaseService { + /** + * 保存点检计划 + */ + void savePlan(EqSpotCheckPlanParamDTO dto); + + /** + * 修改点检计划 + */ + void updatePlan(EqSpotCheckPlanParamDTO dto); + + /** + * 关联分页条件查询 + */ + Page searchPlanByPage(Map params); + + List searchPlan(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckRecordService.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckRecordService.java new file mode 100644 index 0000000..0409a1c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/EqSpotCheckRecordService.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqcheck.service; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqSpotCheckRecordDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckRecordEntity; + +import java.util.List; +import java.util.Map; + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqSpotCheckRecordService extends IBaseService { + + Page searchRecordByPage(Map params); + + List searchRecord(Map params); + + void saveSpotCheckRecord(EqSpotCheckRecordDTO dto); + + Long getRecordCountBySpotCheckPlanId(Long spotCheckPlanId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckSettingServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckSettingServiceImpl.java new file mode 100644 index 0000000..d274532 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckSettingServiceImpl.java @@ -0,0 +1,122 @@ +package com.thing.eq.eqcheck.service.impl; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.entity.EqCheckSettingEntity; +import com.thing.eq.eqcheck.mapper.EqCheckSettingMapper; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 设备检查设置 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-23 + */ +@Service +public class EqCheckSettingServiceImpl extends BaseServiceImpl implements EqCheckSettingService { + + @Autowired + private EqCheckSettingMapper eqCheckSettingDao; + + @Override + public QueryWrapper getWrapper(Map params) { + String type = Objects.isNull(params.get("type")) ? "" : String.valueOf(params.get("type")); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("type", type); + + wrapper.orderBy("update_date",false); + wrapper.orderBy("create_date",false); + return wrapper; + } + + @Override + public List getSettingList(Long thingId, String checkType) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("thing_id", thingId); + wrapper.eq("type", checkType); + List checkSettingDTOS = mapper.selectListExt(wrapper); + + return ConvertUtils.sourceToTarget(checkSettingDTOS, EqCheckSettingDTO.class); + } + + @Override + public void deleteByThingId(Long thingId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("thing_id", thingId); + wrapper.eq("type", "0"); + mapper.deleteByQuery(wrapper); + } + + @Override + public List getSettingListByPatrolPlanId(Long patrolPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("patrolcheck_plan_id", patrolPlanId); + wrapper.eq("type", "1"); + List checkSettingDTOS = mapper.selectListExt(wrapper); + + return ConvertUtils.sourceToTarget(checkSettingDTOS, EqCheckSettingDTO.class); + } + + @Override + public List searchSettingThing(Map params) { + List thingIds = eqCheckSettingDao.searchSettingThing(params); + return thingIds; + } + + @Override + public void deletePatrolByPatrolPlanId(Long patrolPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("patrolcheck_plan_id", patrolPlanId); + wrapper.eq("type", "1"); + mapper.deleteByQuery(wrapper); + } + + @Override + public List getSettingListByPatrolPlanIdAndThingId(Long patrolPlanId, Long thingId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("patrolcheck_plan_id", patrolPlanId); + if (!Objects.isNull(thingId)) { + wrapper.eq("thing_id", thingId); + } + wrapper.eq("type", "1"); + List checkSettingDTOS = mapper.selectListExt(wrapper); + return ConvertUtils.sourceToTarget(checkSettingDTOS, EqCheckSettingDTO.class); + } + + @Override + public int getPatrolPlanCountByEqId(Long eqId, String type) { + return eqCheckSettingDao.getPatrolPlanCountByEqId(eqId, type); + } + + @Override + public String getSettingListByStandardId(Long standardId) { + List settingEntities = new ArrayList<>(); + QueryWrapper wrapperSpotCheck = new QueryWrapper(); + wrapperSpotCheck.eq("standard_id", standardId); + wrapperSpotCheck.eq("type", "0"); + Long spotCount = mapper.selectCountByQuery(wrapperSpotCheck); + + StringBuilder stringBuilder = new StringBuilder(); + if (!Objects.isNull(spotCount) && spotCount > 0) { + stringBuilder.append("点检、"); + } + + QueryWrapper wrapperPatrolCheck = new QueryWrapper(); + wrapperPatrolCheck.eq("standard_id", standardId); + wrapperPatrolCheck.eq("type", "1"); + Long patrolCount = mapper.selectCountByQuery(wrapperPatrolCheck); + if (!Objects.isNull(patrolCount) && patrolCount > 0) { + stringBuilder.append("巡检"); + } + + return stringBuilder.toString(); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardDetailServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardDetailServiceImpl.java new file mode 100644 index 0000000..85e5c22 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardDetailServiceImpl.java @@ -0,0 +1,102 @@ +package com.thing.eq.eqcheck.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.dto.StandardCheckResultDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.mapper.EqCheckStandardDetailMapper; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 设备检查标准明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqCheckStandardDetailServiceImpl extends BaseServiceImpl implements EqCheckStandardDetailService { + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Override + public QueryWrapper getWrapper(Map params) { + String standardId = Objects.isNull(params.get("standardId")) ? "0" : String.valueOf(params.get("standardId")); + List standardIdList = Objects.isNull(params.get("standardIdList")) ? null : (List) params.get("standardIdList"); + QueryWrapper wrapper = new QueryWrapper(); + if (!Objects.isNull(params.get("standardId"))) { + wrapper.eq("standard_Id", Long.parseLong(standardId)); + } + if (!Objects.isNull(params.get("standardIdList"))) { + wrapper.in("standard_Id", CollectionUtil.isEmpty(standardIdList) ? Arrays.asList(0L) : standardIdList); + } + wrapper.orderBy("create_date",true); + return wrapper; + } + + + @Override + public List getListByStandardId(Long standardId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("standard_Id", standardId); + List eqCheckStandardDetailEntityList = mapper.selectListExt(wrapper); + return ConvertUtils.sourceToTarget(eqCheckStandardDetailEntityList, EqCheckStandardDetailDTO.class); + } + + @Override + public List getCheckResultByStandardId(Long standardId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("standard_Id", standardId); + List eqCheckStandardDetailEntityList = mapper.selectListExt(wrapper); + return ConvertUtils.sourceToTarget(eqCheckStandardDetailEntityList, StandardCheckResultDTO.class); + } + + @Override + public List getAllStandardDetailByThingId(Long thingId, String checkType) { + if ("1".equals(checkType)) { + throw new SysException("当前方法仅支持点检"); + } + // 查询设备关联的所有模板 + List eqCheckSettingDTOS = eqCheckSettingService.getSettingList(thingId, checkType); + + // 查询所有模板关联的细项 + List allDetailDTOS = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(eqCheckSettingDTOS)) { + eqCheckSettingDTOS.forEach(eqCheckSettingDTO -> { + List detailDTOS = eqCheckStandardDetailService.getListByStandardId(eqCheckSettingDTO.getStandardId()); + if (CollectionUtil.isNotEmpty(detailDTOS)) { + allDetailDTOS.addAll(detailDTOS); + } + }); + } + return allDetailDTOS; + } + + @Override + public void deleteStandardDetailByStandardId(Long standardId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("standard_Id", standardId); + mapper.deleteByQuery(wrapper); + } + + @Override + public List getCheckResultDetailByStandardIdList(List standardIdList) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("standard_Id", standardIdList); + List eqCheckStandardDetailEntityList = CollectionUtil.isNotEmpty(standardIdList) ? mapper.selectListExt(wrapper) : new ArrayList<>(); + return eqCheckStandardDetailEntityList; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardServiceImpl.java new file mode 100644 index 0000000..5014ac1 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqCheckStandardServiceImpl.java @@ -0,0 +1,77 @@ +package com.thing.eq.eqcheck.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqCheckStandardDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDetailDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardEntity; +import com.thing.eq.eqcheck.mapper.EqCheckStandardMapper; +import com.thing.eq.eqcheck.service.EqCheckStandardService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 检查标准 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqCheckStandardServiceImpl extends BaseServiceImpl implements EqCheckStandardService { + + @Override + public QueryWrapper getWrapper(Map params) { + String checkPurpose = Objects.isNull(params.get("checkPurpose")) ? "" : String.valueOf(params.get("checkPurpose")); + String name = Objects.isNull(params.get("name")) ? "" : String.valueOf(params.get("name")); + String keyWord = Objects.isNull(params.get("keyWord")) ? "" : String.valueOf(params.get("keyWord")); + List deviceTypeIds = Objects.isNull(params.get("deviceTypeIds")) ? new ArrayList<>() : (List) params.get("deviceTypeIds"); + QueryWrapper wrapper = new QueryWrapper(); + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + if (CollectionUtil.isNotEmpty(deviceTypeIds)) { + List deviceTypeIdList = deviceTypeIds.stream().map(id -> Long.parseLong(id)).collect(Collectors.toList()); + wrapper.in("device_type_id", deviceTypeIdList); + } + if (StringUtils.isNotBlank(keyWord)) { + wrapper.like("check_purpose", checkPurpose); + wrapper.like("name", name); + } else { + if (StringUtils.isNotBlank(checkPurpose)) { + wrapper.like("check_purpose", checkPurpose); + } + if (StringUtils.isNotBlank(name)) { + wrapper.like("name", name); + } + } + + + wrapper.orderBy("update_date",false); + + return wrapper; + } + + + @Override + public List getStandardListByStandardIdList(List standardIdList) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("id", standardIdList); + List eqCheckStandardDTOS = mapper.selectListExt(wrapper); + return ConvertUtils.sourceToTarget(eqCheckStandardDTOS, EqCheckStandardDTO.class); + } + + @Override + public EqCheckStandardDTO getLists(String name) { + return mapper.getLists(name); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckPlanServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckPlanServiceImpl.java new file mode 100644 index 0000000..c0bbfe6 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckPlanServiceImpl.java @@ -0,0 +1,677 @@ +package com.thing.eq.eqcheck.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.google.common.collect.Maps; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.eq.eqcheck.dto.*; +import com.thing.eq.eqcheck.entity.EqCheckSettingEntity; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.mapper.EqPatrolCheckPlanMapper; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqcheck.service.EqPatrolCheckRecordService; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.service.EqUserGroupService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.service.IotThingEntityService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备巡检计划 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqPatrolCheckPlanServiceImpl extends BaseServiceImpl implements EqPatrolCheckPlanService { + @Autowired + private EqPatrolCheckPlanMapper eqPatrolCheckPlanDao; + +// @Autowired +// private IotThingsServiceFeign iotThingsServiceFeign; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private EqPatrolCheckRecordService eqPatrolCheckRecordService; + + @Autowired + private CheckResultService checkResultService; + + @Autowired + private EqScrapService eqScrapService; + @Autowired + private EqUserGroupService eqUserGroupService; + + @Autowired + private IotThingEntityService iotThingEntityService; + + @Autowired + private TsKvService tsKvService; +// @Autowired +// private ThingsService thingsService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String name = Objects.isNull(params.get("name")) ? "" : String.valueOf(params.get("name")); + String checkUserId = Objects.isNull(params.get("checkUserId")) ? "" : String.valueOf(params.get("checkUserId")); + String planStatus = Objects.isNull(params.get("planStatus")) ? "" : String.valueOf(params.get("planStatus")); + String checkCycleUnit = Objects.isNull(params.get("checkCycleUnit")) ? "" : String.valueOf(params.get("checkCycleUnit")); + String patrolCheckPlanNo = Objects.isNull(params.get("patrolCheckPlanNo")) ? "" : String.valueOf(params.get("patrolCheckPlanNo")); + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + if (StringUtils.isNotBlank(name)) { + wrapper.like("name", name); + } + if (StringUtils.isNotBlank(checkUserId)) { + wrapper.like("user_id", checkUserId); + } + if (StringUtils.isNotBlank(planStatus)) { + wrapper.like("plan_status", planStatus); + } + + if (StringUtils.isNotBlank(checkCycleUnit)) { + wrapper.eq("check_cycle_unit", checkCycleUnit); + } + if (StringUtils.isNotBlank(patrolCheckPlanNo)) { + wrapper.eq("patrol_check_plan_no", patrolCheckPlanNo); + } + + wrapper.orderBy("patrol_check_plan_no",false); + + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = getPage(params); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + } + params.put("eqIds", eqthingIds); + List list = eqPatrolCheckPlanDao.getEqPatrolPlanList(params); + List targetList = ConvertUtils.sourceToTarget(list, EqPatrolCheckPlanDTO.class); + //校验设备是否执行 + if (CollectionUtil.isNotEmpty(eqthingIds)){ + for (EqPatrolCheckPlanDTO eqPatrolCheckPlanDTO :targetList){ + if (eqPatrolCheckPlanDTO.getGroupId() !=null){ + EqUserGroupDTO data = eqUserGroupService.getById(eqPatrolCheckPlanDTO.getGroupId()); + if (data != null ){ + eqPatrolCheckPlanDTO.setGroupName(data.getName()); + } + } + //检查该计划当前设备是否巡检过 + List currCount = checkResultService.getResultByThingIdAndCheckPlanId(eqthingIds.get(0),eqPatrolCheckPlanDTO.getId()); + if (StringUtils.equals("0",eqPatrolCheckPlanDTO.getType())){ + if (CollectionUtil.isNotEmpty(currCount)){ + for (CheckResultDTO checkResultDTO:currCount){ + if (checkResultDTO.getCheckStart().getTime()>=eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanDTO.getCheckEndTime().getTime() ){ + eqPatrolCheckPlanDTO.setIsCheck("1"); + } + } + } + }else{ + if (CollectionUtil.isNotEmpty(currCount)){ + Date newNextCheckTime = eqPatrolCheckPlanDTO.getPlanStartTime(); + Integer cycle = eqPatrolCheckPlanDTO.getCheckCycle(); + String unit = eqPatrolCheckPlanDTO.getCheckCycleUnit(); + if ("1".equals(eqPatrolCheckPlanDTO.getType()) && (StringUtils.isBlank(unit) || Objects.isNull(cycle))) { + continue; + } + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + for (CheckResultDTO checkResultDTO:currCount){ + if (checkResultDTO.getCheckStart().getTime()>=eqPatrolCheckPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + eqPatrolCheckPlanDTO.setIsCheck("1"); + } + } + } + } + } + } + return new PageData<>(targetList, page.getTotalRow()); + } + + @Override + public Map> getTbLatestData(List tbDataDTOList) { + Map> map = Maps.newHashMap(); + for (TbDataDTO dto : tbDataDTOList) { +// Result response = iotThingEntityService.getThingById(dto.getThingId()); + IotThingEntity iotThingEntity = iotThingEntityService.getById(dto.getThingId()); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } + if (Objects.isNull(iotThingEntity)){ + throw new SysException("物的entityId为空,请优先排除物信息情况"); + } + String time = DateTimeUtils.format(new Date(),DateTimeUtils.DATE_TIME_PATTERN_STR); + Date yearFirst = DateTimeUtils.getYearFirst(Integer.valueOf(time.substring(0, 4))); + String yearFirsts = DateTimeUtils.format(yearFirst,DateTimeUtils.DATE_TIME_PATTERN_STR); + Long yearStartTime = DateTimeUtils.dateToStamp(yearFirsts); + Date yearLast = DateTimeUtils.getYearLast(Integer.valueOf(time.substring(0, 4))); + String yearLasts = DateTimeUtils.format(yearLast,DateTimeUtils.DATE_TIME_PATTERN_STR); + Long yearEndTime = DateTimeUtils.dateToStamp(yearLasts); +// long yearStartTime = DateUtils.getBeginTime(time, "1"); +// long yearEndTime = DateUtils.getEndTime(time, "1"); + +// Map responseData = (Map) response.getData(); +// String entityId = Objects.isNull(responseData.get("entityId")) ? "" : String.valueOf(responseData.get("entityId")); +// if (StringUtils.isBlank(code)) throw new RenException("物的entityId为空,请优先排除物信息情况"); + Map> codeAndKeyMap1 = new HashMap<>(); + codeAndKeyMap1.put(iotThingEntity.getCode(), dto.getKeyList()); + + //获取区域企业数据,并以属性分组合计 + List dataList1 = tsKvService.findTsKvByMultiMap(codeAndKeyMap1, yearStartTime - 1000L, yearEndTime + 1000L, Boolean.FALSE); + map.put(dto.getThingId(), dataList1); + } + return map; + } + + @Override + public List exportData(Map params) { + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + } + params.put("eqIds", eqthingIds); + List list = eqPatrolCheckPlanDao.getEqPatrolPlanList(params); + return list; + } + + + @Override + public List getCheckPlanByStatus(String status,String isRemind) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("plan_status", status); + wrapper.eq("is_remind", isRemind); + + return mapper.selectListExt(wrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePatrolCheckPlan(EqPatrolCheckPlanResDTO dto) { + if (CollectionUtil.isEmpty(dto.getDeviceBaseInfoDTOS())) { + throw new SysException("巡检计划未选择设备"); + } + EqPatrolCheckPlanDTO planDTO = getByIdAs(dto.getId(),EqPatrolCheckPlanDTO.class); + if (Objects.isNull(planDTO)) { + throw new SysException("巡检计划不存在"); + } + if ("2".equals(planDTO.getExecutePlanStatus())) { + throw new SysException("巡检计划已完成"); + } + + Long count = eqPatrolCheckRecordService.getRecordCountByCheckPlanId(dto.getId()); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("巡检计划已存在巡检记录无法修改"); + } + + updateDto(dto); + + eqCheckSettingService.deletePatrolByPatrolPlanId(dto.getId()); + + saveDeviceList(dto.getId(), dto.getDeviceBaseInfoDTOS()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void executePlan(ExecutePlanDTO dto) { + + EqPatrolCheckPlanDTO checkPlanDTO = getByIdAs(dto.getCheckPlanId(),EqPatrolCheckPlanDTO.class); + if (Objects.isNull(checkPlanDTO)) { + throw new SysException("巡检计划不存在"); + } + if ("2".equals(checkPlanDTO.getExecutePlanStatus())) { + throw new SysException("巡检计划已完成无法修改"); + } + if (!"ACTIVE".equals(checkPlanDTO.getPlanStatus())) { + throw new SysException("巡检计划未启用"); + } + // 如果是循环则重新生成时间,覆盖上次时间 + Date newNextCheckTime = checkPlanDTO.getPlanStartTime(); + Date newNextChecnEndTime = null; + if ("0".equals(checkPlanDTO.getType())) { + Long count = eqPatrolCheckRecordService.getRecordCountByCheckPlanId(dto.getCheckPlanId()); + if (!Objects.isNull(count) && count > 0) { + throw new SysException("当前计划为单次,并且已存在记录"); + } + checkPlanDTO.setPlanStatus("INACTIVE"); + checkPlanDTO.setExecutePlanStatus("2"); + } else { + Integer cycle = checkPlanDTO.getCheckCycle(); + String unit = checkPlanDTO.getCheckCycleUnit(); + if ("1".equals(checkPlanDTO.getType()) && (StringUtils.isBlank(unit) || Objects.isNull(cycle))) { + throw new SysException("巡检计划周期及单位不能为空"); + } + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + String newNextChecnEndTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextChecnEndTime = DateTimeUtils.stringToDate(newNextChecnEndTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + String newNextChecnEndTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextChecnEndTime = DateTimeUtils.stringToDate(newNextChecnEndTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + // 下次开始时间需小于截止时间 + if (!Objects.isNull(checkPlanDTO.getCheckEndTime()) && newNextCheckTime.getTime() > checkPlanDTO.getCheckEndTime().getTime()) { + throw new SysException("巡检计划开始时间超过截止时间"); + } + + if (!Objects.isNull(checkPlanDTO.getCheckEndTime()) && newNextChecnEndTime.getTime() > checkPlanDTO.getCheckEndTime().getTime()) { + checkPlanDTO.setExecutePlanStatus("2"); + checkPlanDTO.setPlanStatus("INACTIVE"); + } else { + checkPlanDTO.setExecutePlanStatus("0"); + } + //更新为未提醒 + checkPlanDTO.setIsRemind("0"); + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateTimeUtils.DATE_TIME_PATTERN_STR); + Date actCheckTime = null; + if (StringUtils.isNotBlank(dto.getCheckTime())) { + try { + actCheckTime = simpleDateFormat.parse(dto.getCheckTime()); + } catch (ParseException e) { + throw new SysException("巡检时间格式不正确 yyyy-MM-dd hh:mm:ss"); + } + } + // 先新增本次时间的巡检记录 + EqPatrolCheckRecordDTO recordDTO = new EqPatrolCheckRecordDTO(); + recordDTO.setUserId(dto.getCheckUserId()); + recordDTO.setTip(dto.getRemark()); + recordDTO.setCheckTime(actCheckTime); + recordDTO.setPatrolCheckPlanId(checkPlanDTO.getId()); + recordDTO.setCheckPlanTime(DateTimeUtils.format(checkPlanDTO.getPlanStartTime(), DateTimeUtils.DATE_TIME_PATTERN_STR + " 至 " + DateTimeUtils.format(newNextCheckTime, DateTimeUtils.DATE_TIME_PATTERN_STR))); + Long recordId = eqPatrolCheckRecordService.savePatrolRecord(recordDTO); + + checkPlanDTO.setActUserId(dto.getCheckUserId()); + checkPlanDTO.setActCheckTime(actCheckTime); + checkPlanDTO.setRemark(dto.getRemark()); + // 更新为下次的巡检记录的计划时间 + checkPlanDTO.setPlanStartTime(newNextCheckTime); + checkPlanDTO.setNextBatchTime(newNextCheckTime); + checkPlanDTO.setPlanEndTime(newNextChecnEndTime); + updateDto(checkPlanDTO); + + if (Objects.isNull(recordId)) { + throw new SysException("巡检计划执行失败,新增巡检记录未成功"); + } + // 更新巡检记录结果 + List checkResultDTOS = dto.getCheckResultDTOS(); + if (CollectionUtil.isEmpty(checkResultDTOS)) { + return; + } + for (CheckResultDTO checkResultDTO : checkResultDTOS) { + checkResultDTO.setCheckRecordId(recordId); + if (Objects.isNull(checkResultDTO.getThingId())) { + throw new SysException("巡检结果设备物id不能为空"); + } + eqScrapService.checkThingStatus(checkResultDTO.getThingId()); + + // 无巡检结果跳过 + if (StringUtils.isNotBlank(checkResultDTO.getCheckResult())) { + // 巡检结果json异常 + List resultList = null; + try { + resultList = JSON.parseArray(checkResultDTO.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + // 巡检结果json解析为空集合跳过 + if (CollectionUtil.isNotEmpty(resultList)) { + List standardDetail = eqPatrolCheckRecordService.getStandardDetailByCheckPlan(dto.getCheckPlanId(), checkResultDTO.getThingId()); + if (CollectionUtil.isEmpty(standardDetail)) { + throw new SysException("模板标准检查项为空"); + } + if (resultList.size() != standardDetail.size()) { + checkResultDTO.setCheckStatus("2"); + } else { + for (Map map : resultList) { + if ("异常".equals(map.get("checkValue"))) { + checkResultDTO.setCheckStatus("2"); + break; + } + } + } + } else { + checkResultDTO.setCheckStatus("0"); + } + } else { + checkResultDTO.setCheckStatus("0"); + } + + if (!"0".equals(checkResultDTO.getCheckStatus()) && !"2".equals(checkResultDTO.getCheckStatus())) { + checkResultDTO.setCheckStatus("1"); + } + checkResultService.updateRecord(checkResultDTO); + } + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void executePlanNew(ExecutePlanDTO dto) { + //逻辑改为单设备执行2022-06-13 + EqPatrolCheckPlanDTO checkPlanDTO = getByIdAs(dto.getCheckPlanId(),EqPatrolCheckPlanDTO.class); + if (Objects.isNull(checkPlanDTO)) { + throw new SysException("巡检计划不存在"); + } + if (StringUtils.isBlank(checkPlanDTO.getPatrolCheckPlanNo())) { + throw new SysException("巡检计划单号不能为空"); + } + if (StringUtils.isBlank(checkPlanDTO.getType())) { + throw new SysException("循环类型不能为空"); + } + if ("2".equals(checkPlanDTO.getExecutePlanStatus())) { + throw new SysException("巡检计划已完成无法修改"); + } + if (!"ACTIVE".equals(checkPlanDTO.getPlanStatus())) { + throw new SysException("巡检计划未启用"); + } + //下次计划开始时间 + Date newNextCheckTime = checkPlanDTO.getPlanStartTime(); + CheckResultDTO checkDTO = dto.getCheckResultDTOS().get(0); + Long executeTime = DateTimeUtils.parse(dto.getCheckTime(),DateTimeUtils.DATE_TIME_PATTERN_STR).getTime(); + if(executeTime >= checkPlanDTO.getCheckEndTime().getTime() ){ + throw new SysException("计划已经截止,不可执行"); + } + List eqCheckSettingDTOS = eqCheckSettingService.getSettingListByPatrolPlanId(dto.getCheckPlanId()); + if ("0".equals(checkPlanDTO.getType())) { + //校验执行计划时间是否在开始结束时间范围内 + if(executeTime < checkPlanDTO.getPlanStartTime().getTime() + || executeTime >= checkPlanDTO.getCheckEndTime().getTime() ){ + throw new SysException("执行计划时间不在计划时间内,不可执行"); + } + //检查该计划当前设备是否巡检过 + List checkResultDTOCount = checkResultService.getResultByThingIdAndCheckPlanId(checkDTO.getThingId(),dto.getCheckPlanId()); + if (CollectionUtil.isNotEmpty(checkResultDTOCount) && checkResultDTOCount.size() > 0) { + throw new SysException("当前计划为单次,并且该设备已存在记录"); + } + + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + for (EqCheckSettingDTO settingDTO : eqCheckSettingDTOS){ + if (!settingDTO.getThingId().equals(checkDTO.getThingId())){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),checkPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= checkPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < checkPlanDTO.getCheckEndTime().getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + } + } + } + } + totalCheckIds.removeAll(checkIds); + LinkedHashSet noCheckIds = new LinkedHashSet<>(); + noCheckIds.addAll(totalCheckIds); + //如果其他设备全部巡检过,更新计划状态为完成,并禁用该计划 + if (noCheckIds.size()==0){ + checkPlanDTO.setPlanStatus("INACTIVE"); + checkPlanDTO.setExecutePlanStatus("2"); + }else{ + checkPlanDTO.setExecutePlanStatus("1"); + } + } else { + Integer cycle = checkPlanDTO.getCheckCycle(); + String unit = checkPlanDTO.getCheckCycleUnit(); + if ("1".equals(checkPlanDTO.getType()) && (StringUtils.isBlank(unit) || Objects.isNull(cycle))) { + throw new SysException("巡检计划周期及单位不能为空"); + } + if ("day".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateDays(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String newNextCheckTimes = DateTimeUtils.addDateHours(newNextCheckTime, cycle); + newNextCheckTime = DateTimeUtils.stringToDate(newNextCheckTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + // 校验本次计划是否在计划时间内 + if (executeTime < checkPlanDTO.getPlanStartTime().getTime() || executeTime >= newNextCheckTime.getTime()) { + throw new SysException("当次执行计划时间不在计划时间内,不可执行"); + } + //校验本次计划该设备是否执行过 + List currCount = checkResultService.getResultByThingIdAndCheckPlanId(checkDTO.getThingId(),dto.getCheckPlanId()); + if (CollectionUtil.isNotEmpty(currCount)){ + for (CheckResultDTO checkResultDTO:currCount){ + if (checkResultDTO.getCheckStart().getTime()>=checkPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + throw new SysException("本次巡检计划该设备已存在记录"); + } + } + } + + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + for (EqCheckSettingDTO settingDTO : eqCheckSettingDTOS){ + if (!settingDTO.getThingId().equals(checkDTO.getThingId())){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),checkPlanDTO.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= checkPlanDTO.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < newNextCheckTime.getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + } + } + } + } + totalCheckIds.removeAll(checkIds); + LinkedHashSet noCheckIds = new LinkedHashSet<>(); + noCheckIds.addAll(totalCheckIds); + //如果其他设备全部巡检过,更新计划状态为完成 + if (noCheckIds.size()==0){ + checkPlanDTO.setExecutePlanStatus("2"); + }else{ + checkPlanDTO.setExecutePlanStatus("1"); + } + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateTimeUtils.DATE_TIME_PATTERN_STR); + Date actCheckTime = null; + if (StringUtils.isNotBlank(dto.getCheckTime())) { + try { + actCheckTime = simpleDateFormat.parse(dto.getCheckTime()); + } catch (ParseException e) { + throw new SysException("巡检时间格式不正确 yyyy-MM-dd hh:mm:ss"); + } + } + // 先新增本次时间的巡检记录 + EqPatrolCheckRecordDTO recordDTO = new EqPatrolCheckRecordDTO(); + recordDTO.setUserId(dto.getCheckUserId()); + recordDTO.setTip(dto.getRemark()); + recordDTO.setCheckTime(actCheckTime); + recordDTO.setPatrolCheckPlanId(checkPlanDTO.getId()); + if (StringUtils.equals("0",checkPlanDTO.getType())){ + recordDTO.setCheckPlanTime(DateTimeUtils.format(checkPlanDTO.getPlanStartTime(), DateTimeUtils.DATE_TIME_PATTERN_STR + " 至 " + DateTimeUtils.format(checkPlanDTO.getCheckEndTime(), DateTimeUtils.DATE_TIME_PATTERN_STR))); + }else{ + recordDTO.setCheckPlanTime(DateTimeUtils.format(checkPlanDTO.getPlanStartTime(), DateTimeUtils.DATE_TIME_PATTERN_STR + " 至 " + DateTimeUtils.format(newNextCheckTime, DateTimeUtils.DATE_TIME_PATTERN_STR))); + } + //只存当前设备结果记录 + recordDTO.setThingId(dto.getCheckResultDTOS().get(0).getThingId()); + Long recordId = eqPatrolCheckRecordService.savePatrolRecord(recordDTO); + + checkPlanDTO.setActUserId(dto.getCheckUserId()); + checkPlanDTO.setActCheckTime(actCheckTime); + checkPlanDTO.setRemark(dto.getRemark()); + updateDto(checkPlanDTO); + + if (Objects.isNull(recordId)) { + throw new SysException("巡检计划执行失败,新增巡检记录未成功"); + } + // 更新巡检记录结果 + List checkResultDTOS = dto.getCheckResultDTOS(); + if (CollectionUtil.isEmpty(checkResultDTOS)) { + return; + } + for (CheckResultDTO checkResultDTO : checkResultDTOS) { + checkResultDTO.setCheckRecordId(recordId); + if (Objects.isNull(checkResultDTO.getThingId())) { + throw new SysException("巡检结果设备物id不能为空"); + } + eqScrapService.checkThingStatus(checkResultDTO.getThingId()); + + // 无巡检结果跳过 + if (StringUtils.isNotBlank(checkResultDTO.getCheckResult())) { + // 巡检结果json异常 + List resultList = null; + try { + resultList = JSON.parseArray(checkResultDTO.getCheckResult(), Map.class); + } catch (Exception e) { + throw new SysException("检查结果json格式不正确"); + } + // 巡检结果json解析为空集合跳过 + if (CollectionUtil.isNotEmpty(resultList)) { + List standardDetail = eqPatrolCheckRecordService.getStandardDetailByCheckPlan(dto.getCheckPlanId(), checkResultDTO.getThingId()); + if (CollectionUtil.isEmpty(standardDetail)) { + throw new SysException("模板标准检查项为空"); + } + if (resultList.size() != standardDetail.size()) { + checkResultDTO.setCheckStatus("2"); + } else { + for (Map map : resultList) { + if ("异常".equals(map.get("checkValue"))) { + checkResultDTO.setCheckStatus("2"); + break; + } + } + } + } else { + checkResultDTO.setCheckStatus("0"); + } + } else { + checkResultDTO.setCheckStatus("0"); + } + + if (!"0".equals(checkResultDTO.getCheckStatus()) && !"2".equals(checkResultDTO.getCheckStatus())) { + checkResultDTO.setCheckStatus("1"); + } + checkResultService.updateRecord(checkResultDTO); + } + } + + @Override + public void updateIsRemind(Long id, String isRemind) { + eqPatrolCheckPlanDao.updateIsRemind(id,isRemind); + } + + @Override + public List getCheckPlanByStatusAndType(String patrolPlanStatus, String type) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("plan_status", patrolPlanStatus); +// wrapper.eq("type", type); + + return mapper.selectListExt(wrapper); + } + + @Override + public List getByGroupId(Long id, Long tenantCode) { + return eqPatrolCheckPlanDao.getByGroupId(id,tenantCode); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void savePatrolCheckPlan(EqPatrolCheckPlanResDTO dto) { + saveInfo(dto); + } + + public void saveInfo(EqPatrolCheckPlanResDTO dto) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.orderBy("patrol_check_plan_no",false); + EqPatrolCheckPlanEntity planEntity = mapper.selectOneExt(queryWrapper); + + if (Objects.isNull(planEntity)) { + planEntity = new EqPatrolCheckPlanEntity(); + } + + String lastNo = SerialNumberUnit.generateNumber("XJ", planEntity.getPatrolCheckPlanNo()); + dto.setPatrolCheckPlanNo(lastNo); + saveDto(dto); + if (Objects.isNull(dto.getId())) { + throw new SysException("巡检计划保存失败"); + } + + saveDeviceList(dto.getId(), dto.getDeviceBaseInfoDTOS()); + } + + public void saveDeviceList(Long checkPlanId, List deviceBaseInfoDTOS) { + for (DeviceBaseInfoDTO deviceInfo : deviceBaseInfoDTOS) { + + eqScrapService.checkThingStatus(deviceInfo.getThingId()); + + List standardDTOS = deviceInfo.getStandardIdList(); + //todo + if (CollectionUtil.isEmpty(standardDTOS)) { + IotThingEntityDTO data1 = iotThingEntityService.getByIdAs(deviceInfo.getThingId(),IotThingEntityDTO.class); +// Result response = iotThingsServiceFeign.getThingById(deviceInfo.getThingId()); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } +// Map responseData = (Map) response.getData(); + String thingName = Objects.isNull(data1.getName()) ? "" : data1.getName(); + throw new SysException("设备:" + thingName + "未选择检查项"); + } + + for (Long standardId : standardDTOS) { + EqCheckSettingEntity eqCheckSettingDTO = new EqCheckSettingEntity(); + eqCheckSettingDTO.setThingId(deviceInfo.getThingId()); + eqCheckSettingDTO.setStandardId(standardId); + eqCheckSettingDTO.setType("1"); + eqCheckSettingDTO.setPatrolcheckPlanId(checkPlanId); + eqCheckSettingService.saveDto(eqCheckSettingDTO); + } + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckRecordServiceImpl.java new file mode 100644 index 0000000..1b71d14 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqPatrolCheckRecordServiceImpl.java @@ -0,0 +1,220 @@ +package com.thing.eq.eqcheck.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.EqPatrolCheckRecordDTO; +import com.thing.eq.eqcheck.dto.EqPatrolCheckRecordResDTO; +import com.thing.eq.eqcheck.entity.EqCheckStandardDetailEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.entity.EqPatrolCheckRecordEntity; +import com.thing.eq.eqcheck.mapper.EqPatrolCheckRecordMapper; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqCheckStandardDetailService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqcheck.service.EqPatrolCheckRecordService; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 设备巡检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqPatrolCheckRecordServiceImpl extends BaseServiceImpl implements EqPatrolCheckRecordService { + + @Autowired + private CheckResultService checkResultService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private EqPatrolCheckRecordMapper eqPatrolCheckRecordDao; + + @Autowired + private EqCheckStandardDetailService eqCheckStandardDetailService; + + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + + @Autowired + private EqScrapService eqScrapService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public Long savePatrolRecord(EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO) { + EqPatrolCheckPlanEntity checkPlanEntity = eqPatrolCheckPlanService.getByIdAs(eqPatrolCheckRecordDTO.getPatrolCheckPlanId(),EqPatrolCheckPlanEntity.class); + if (Objects.isNull(checkPlanEntity)) { + throw new SysException("巡检计划不存在"); + } + + if (Objects.isNull(checkPlanEntity.getPatrolCheckPlanNo())) { + throw new SysException("无效数据,巡检计划单号不存在"); + } + + Long recordId = saveInfo(eqPatrolCheckRecordDTO, checkPlanEntity); + + return recordId; + } + + public Long saveInfo(EqPatrolCheckRecordDTO eqPatrolCheckRecordDTO, EqPatrolCheckPlanEntity checkPlanEntity) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("patrol_check_plan_id",eqPatrolCheckRecordDTO.getPatrolCheckPlanId()); + queryWrapper.orderBy("LENGTH(patrol_check_record_no)",false); + queryWrapper.orderBy("patrol_check_record_no",false); +// queryWrapper.last("limit 1"); + EqPatrolCheckRecordEntity recordEntity = mapper.selectOneExt(queryWrapper); + if (Objects.isNull(recordEntity)) { + recordEntity = new EqPatrolCheckRecordEntity(); + } + String lastNo = SerialNumberUnit.generateChildNumber(checkPlanEntity.getPatrolCheckPlanNo(), recordEntity.getPatrolCheckRecordNo()); + eqPatrolCheckRecordDTO.setPatrolCheckRecordNo(lastNo); + saveDto(eqPatrolCheckRecordDTO); + + if (Objects.isNull(eqPatrolCheckRecordDTO)) { + throw new SysException("巡检结果保存失败"); + } + if (eqPatrolCheckRecordDTO.getThingId() !=null){ + eqScrapService.checkThingStatus(eqPatrolCheckRecordDTO.getThingId()); + CheckResultDTO checkResult = new CheckResultDTO(); + checkResult.setCheckRecordId(eqPatrolCheckRecordDTO.getId()); + checkResult.setType("1"); + checkResult.setThingId(eqPatrolCheckRecordDTO.getThingId()); + checkResult.setCheckPlanId(eqPatrolCheckRecordDTO.getPatrolCheckPlanId()); + checkResultService.saveDto(checkResult); + }else{ + List settingDTOS = eqCheckSettingService.getSettingListByPatrolPlanId(eqPatrolCheckRecordDTO.getPatrolCheckPlanId()); + if (CollectionUtil.isNotEmpty(settingDTOS)) { + Map> map = settingDTOS.stream().collect(Collectors.groupingBy(EqCheckSettingDTO::getThingId)); + for (Long thingId : map.keySet()) { + + eqScrapService.checkThingStatus(thingId); + + CheckResultDTO checkResult = new CheckResultDTO(); + checkResult.setCheckRecordId(eqPatrolCheckRecordDTO.getId()); + checkResult.setType("1"); + checkResult.setThingId(thingId); + checkResult.setCheckPlanId(eqPatrolCheckRecordDTO.getPatrolCheckPlanId()); + checkResultService.saveDto(checkResult); + } + } + } + + return eqPatrolCheckRecordDTO.getId(); + } + + @Override + public Page searchRecordByPage(Map params) { + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("page、limit不能为空"); + } + int page = Integer.parseInt(String.valueOf(params.get("page"))); + int limit = Integer.parseInt(String.valueOf(params.get("limit"))); + + getParam(params); + + List eqPatrolCheckRecordDTOPage = eqPatrolCheckRecordDao.searchRecord(new Page<>(page, limit), params); + Page page2 = new Page<>(); + page2.setRecords(eqPatrolCheckRecordDTOPage); + page2.setTotalPage(eqPatrolCheckRecordDTOPage.size()); + + return page2; + } + + + @Override + public List exportData(Map params) { + + getParam(params); + + List eqPatrolCheckRecordDTOPage = eqPatrolCheckRecordDao.searchRecord(params); + + return eqPatrolCheckRecordDTOPage; + } + + public Map getParam(Map params){ + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + + String thingId = Objects.isNull(params.get("thingId")) ? "" : String.valueOf(params.get("thingId")); + if (StringUtils.isBlank(thingId)) { + params.remove("thingId"); + } else { + params.put("thingId", Long.parseLong(thingId)); + } + + return params; + } + + @Override + public EqPatrolCheckRecordResDTO getPatrolRecordById(Long recordId) { + if (Objects.isNull(recordId)) { + throw new SysException("巡检记录id不能为空"); + } + EqPatrolCheckRecordResDTO patrolRecord = eqPatrolCheckRecordDao.getPatrolRecordById(recordId); + return patrolRecord; + } + + @Override + public List getStandardDetailByCheckPlan(Long checkPlanId, Long thingId) { + + List settingList = eqCheckSettingService.getSettingListByPatrolPlanIdAndThingId(checkPlanId, thingId); + + List standardId = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(settingList)) { + settingList.forEach(setting -> { + standardId.add(setting.getStandardId()); + }); + } + + List checkResultDetail = eqCheckStandardDetailService.getCheckResultDetailByStandardIdList(standardId); + + return checkResultDetail; + } + + @Override + public List getByCheckPlanId(Long checkPlanId) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("patrol_check_plan_id", checkPlanId); + + List recordEntities = mapper.selectListExt(wrapper); + + return ConvertUtils.sourceToTarget(recordEntities, EqPatrolCheckRecordDTO.class); + } + + @Override + public Long getRecordCountByCheckPlanId(Long checkPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("patrol_check_plan_id", checkPlanId); + + return mapper.selectCountByQuery(wrapper); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckPlanServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckPlanServiceImpl.java new file mode 100644 index 0000000..2b4d734 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckPlanServiceImpl.java @@ -0,0 +1,241 @@ +package com.thing.eq.eqcheck.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.EqSpotCheckPlanDTO; +import com.thing.eq.eqcheck.dto.EqSpotCheckPlanParamDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckPlanEntity; +import com.thing.eq.eqcheck.mapper.EqSpotCheckPlanMapper; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqSpotCheckPlanService; +import com.thing.eq.eqcheck.service.EqSpotCheckRecordService; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import org.apache.commons.lang3.StringUtils; +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.util.*; +import java.util.stream.Collectors; + +/** + * 点检计划 + * + * @author zy aa@aa,com + * @since 3.0 2021-10-12 + */ +@Service +public class EqSpotCheckPlanServiceImpl extends BaseServiceImpl implements EqSpotCheckPlanService { + + @Autowired + private EqCheckSettingService eqCheckSettingService; + +// @Autowired +// private IotThingsServiceFeign iotThingsServiceFeign; + + @Autowired + private EqSpotCheckPlanMapper eqSpotCheckPlanDao; + + @Autowired + private EqSpotCheckRecordService eqSpotCheckRecordService; + + @Autowired + private EqScrapService eqScrapService; + + @Autowired + private IotThingEntityService iotThingEntityService; + +// @Autowired +// private ThingsService thingsService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void savePlan(EqSpotCheckPlanParamDTO dto) { + saveInfo(dto); + } + + public void saveInfo(EqSpotCheckPlanParamDTO dto) { + QueryWrapper lastWrapper = new QueryWrapper(); + lastWrapper.orderBy("spot_check_no",false); + EqSpotCheckPlanEntity checkPlanEntity = mapper.selectOneExt(lastWrapper); + + String lastNo = ""; + for (String thingIdStr : dto.getThingIdList()) { + Long thingId = Long.parseLong(thingIdStr); + + eqScrapService.checkThingStatus(thingId); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("thing_id", thingId); + //调整 + if (mapper.selectCountByQuery(wrapper) > 0) { + IotThingEntityDTO data1 = iotThingEntityService.getByIdAs(thingId,IotThingEntityDTO.class); + String thingName = Objects.isNull(data1.getName()) ? "" : data1.getName(); +// Result response = iotThingsServiceFeign.getThingById(thingId); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } +// Map responseData = (Map) response.getData(); +// String thingName = Objects.isNull(responseData.get("name")) ? "" : String.valueOf(responseData.get("name")); + throw new SysException("设备:" + thingName + "已存在巡检项"); + } + + // 第一个单号用lastNo + if (Objects.isNull(checkPlanEntity)) { + checkPlanEntity = new EqSpotCheckPlanEntity(); + } + lastNo = SerialNumberUnit.generateNumber("DJ", checkPlanEntity.getSpotCheckNo()); + + EqSpotCheckPlanEntity eqSpotCheckPlanEntity = new EqSpotCheckPlanEntity(); + BeanUtils.copyProperties(dto, eqSpotCheckPlanEntity); + eqSpotCheckPlanEntity.setThingId(thingId); + eqSpotCheckPlanEntity.setTenantCode(SecurityUser.getTenantCode()); + eqSpotCheckPlanEntity.setSpotCheckNo(lastNo); + saveDto(eqSpotCheckPlanEntity); +// if (Objects.isNull(eqSpotCheckPlanEntity.getId())) { +// throw new SysException("记录保存失败"); +// } + BeanUtils.copyProperties(eqSpotCheckPlanEntity, checkPlanEntity); + + dto.getStandardIdList().forEach(standardId -> { + EqCheckSettingDTO settingDTO = new EqCheckSettingDTO(); + settingDTO.setThingId(thingId); + settingDTO.setStandardId(Long.parseLong(standardId)); + settingDTO.setType("0"); + + eqCheckSettingService.saveDto(settingDTO); + }); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updatePlan(EqSpotCheckPlanParamDTO dto) { + EqSpotCheckPlanDTO eqSpotCheckPlanDTO = this.getByIdAs(dto.getId(),EqSpotCheckPlanDTO.class); + if (Objects.isNull(eqSpotCheckPlanDTO)) { + throw new SysException("点检计划不存在"); + } + + List standardIdList = dto.getStandardIdList(); + if (CollectionUtil.isEmpty(standardIdList)) { + throw new SysException("设备未选择巡检模板"); + } + + Long count = eqSpotCheckRecordService.getRecordCountBySpotCheckPlanId(dto.getId()); + if(!Objects.isNull(count) && count > 0){ + throw new SysException("已经存在点检记录的点检计划无法修改"); + } + + Long thingId = Long.parseLong(dto.getThingIdList().get(0)); + + eqScrapService.checkThingStatus(thingId); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("thing_id", thingId); + List planEntityList = this.mapper.selectListExt(wrapper); + if (CollectionUtil.isNotEmpty(planEntityList) && planEntityList.get(0).getId().compareTo(dto.getId()) != 0) { + + //调整 + IotThingEntityDTO data1 = iotThingEntityService.getByIdAs(thingId,IotThingEntityDTO.class); + String thingName = Objects.isNull(data1.getName()) ? "" : data1.getName(); +// Result response = iotThingsServiceFeign.getThingById(thingId); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } +// Map responseData = (Map) response.getData(); +// String thingName = Objects.isNull(responseData.get("name")) ? "" : String.valueOf(responseData.get("name")); + throw new SysException("设备:" + thingName + "已存在巡检项"); + } + + eqSpotCheckPlanDTO.setUseId(dto.getUseId()); + if (CollectionUtil.isNotEmpty(dto.getThingIdList())) { + eqCheckSettingService.deleteByThingId(eqSpotCheckPlanDTO.getThingId()); + eqSpotCheckPlanDTO.setThingId(Long.parseLong(dto.getThingIdList().get(0))); + } + this.updateDto(eqSpotCheckPlanDTO); + + standardIdList.forEach(standardId -> { + EqCheckSettingDTO settingDTO = new EqCheckSettingDTO(); + settingDTO.setThingId(eqSpotCheckPlanDTO.getThingId()); + settingDTO.setStandardId(Long.parseLong(standardId)); + settingDTO.setType("0"); + + eqCheckSettingService.saveDto(settingDTO); + }); + + } + + @Override + public Page searchPlanByPage(Map params) { + + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("page、limit不能为空"); + } + int page = Integer.parseInt(String.valueOf(params.get("page"))); + int limit = Integer.parseInt(String.valueOf(params.get("limit"))); + + getParam(params); + + List eqSpotCheckPlanList = eqSpotCheckPlanDao.searchPlan(new Page<>(page, limit), params); + + Page objectPage = new Page<>(); + objectPage.setRecords(eqSpotCheckPlanList); + objectPage.setTotalRow(eqSpotCheckPlanList.size()); + return objectPage; + } + + @Override + public List searchPlan(Map params) { + + getParam(params); + + List eqSpotCheckPlanDTOS = eqSpotCheckPlanDao.searchPlan(params); + + return eqSpotCheckPlanDTOS; + } + + + public Map getParam(Map params) { + + if (!Objects.isNull(params.get("startTime")) && StringUtils.isNotBlank(String.valueOf(params.get("startTime"))) + && !Objects.isNull("endTime") && StringUtils.isNotBlank(String.valueOf(params.get("endTime")))) { + Date start = DateTimeUtils.parse(String.valueOf(params.get("startTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + Date end = DateTimeUtils.parse(String.valueOf(params.get("endTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + + params.put("startTime", start); + params.put("endTime", end); + } else { + params.remove("startTime"); + params.remove("endTime"); + } + + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + } + params.put("eqIds", eqthingIds); + + return params; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckRecordServiceImpl.java new file mode 100644 index 0000000..91719e8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqcheck/service/impl/EqSpotCheckRecordServiceImpl.java @@ -0,0 +1,154 @@ +package com.thing.eq.eqcheck.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqSpotCheckRecordDTO; +import com.thing.eq.eqcheck.entity.EqSpotCheckPlanEntity; +import com.thing.eq.eqcheck.entity.EqSpotCheckRecordEntity; +import com.thing.eq.eqcheck.mapper.EqSpotCheckRecordMapper; +import com.thing.eq.eqcheck.service.EqSpotCheckPlanService; +import com.thing.eq.eqcheck.service.EqSpotCheckRecordService; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.utils.SerialNumberUnit; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 设备点检记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqSpotCheckRecordServiceImpl extends BaseServiceImpl implements EqSpotCheckRecordService { + + @Autowired + private EqSpotCheckRecordMapper eqSpotCheckRecordDao; + + @Autowired + private EqSpotCheckPlanService eqSpotCheckPlanService; + + @Autowired + private EqScrapService eqScrapService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public Page searchRecordByPage(Map params) { + + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("page、limit不能为空"); + } + int page = Integer.parseInt(String.valueOf(params.get("page"))); + int limit = Integer.parseInt(String.valueOf(params.get("limit"))); + + getParam(params); + + Page eqSpotCheckPlanList = eqSpotCheckRecordDao.searchRecord(new Page<>(page, limit), params); + + return eqSpotCheckPlanList; + } + + + public Map getParam(Map params) { + if (!Objects.isNull(params.get("startTime")) && !Objects.isNull("endTime") + && StringUtils.isNotBlank(String.valueOf(params.get("startTime"))) + && StringUtils.isNotBlank(String.valueOf(params.get("endTime")))) { + Date start = DateTimeUtils.parse(String.valueOf(params.get("startTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + Date end = DateTimeUtils.parse(String.valueOf(params.get("endTime")), DateTimeUtils.DATE_TIME_PATTERN_STR); + + params.put("startTime", start); + params.put("endTime", end); + } else { + params.remove("startTime"); + params.remove("endTime"); + } + + if (!Objects.isNull(params.get("spotPlanId")) && StringUtils.isNotBlank(String.valueOf(params.get("spotPlanId")))) { + Long spotPlanId = Long.parseLong(String.valueOf(params.get("spotPlanId"))); + params.put("spotPlanId", spotPlanId); + } else { + params.remove("spotPlanId"); + } + + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + + if (!Objects.isNull(params.get("useId")) && StringUtils.isNotBlank(String.valueOf(params.get("useId")))) { + Long useId = Long.parseLong(String.valueOf(params.get("useId"))); + params.put("useId", useId); + } else { + params.remove("useId"); + } + + return params; + } + + @Override + public List searchRecord(Map params) { + + getParam(params); + + List recordDTOS = eqSpotCheckRecordDao.searchRecord(params); + + return recordDTOS; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveSpotCheckRecord(EqSpotCheckRecordDTO dto) { + EqSpotCheckPlanEntity checkPlanEntity = eqSpotCheckPlanService.getByIdAs(dto.getSpotCheckId(),EqSpotCheckPlanEntity.class); + if (Objects.isNull(checkPlanEntity)) { + throw new SysException("点检计划不存在"); + } + + eqScrapService.checkThingStatus(dto.getThingId()); + + saveInfo(dto, checkPlanEntity); + } + + @Override + public Long getRecordCountBySpotCheckPlanId(Long spotCheckPlanId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("spot_check_id", spotCheckPlanId); + + return eqSpotCheckRecordDao.selectCountByQuery(wrapper); + } + + public void saveInfo(EqSpotCheckRecordDTO dto, EqSpotCheckPlanEntity checkPlanEntity) { + + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("spot_check_id", dto.getSpotCheckId()); + queryWrapper.orderBy("LENGTH(spot_check_record_no)",false); + queryWrapper.orderBy("spot_check_record_no",false); + EqSpotCheckRecordEntity spotCheckRecordEntity = mapper.selectOneExt(queryWrapper); + + if (Objects.isNull(spotCheckRecordEntity)) { + spotCheckRecordEntity = new EqSpotCheckRecordEntity(); + } + + String lastNo = SerialNumberUnit.generateChildNumber(checkPlanEntity.getSpotCheckNo(), spotCheckRecordEntity.getSpotCheckRecordNo()); + dto.setSpotCheckRecordNo(lastNo); + + saveDto(dto); + + checkPlanEntity.setLastCheckTime(dto.getCheckTime()); + eqSpotCheckPlanService.updateById(checkPlanEntity); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/controller/EqFileManageController.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/controller/EqFileManageController.java new file mode 100644 index 0000000..e9e2d4a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/controller/EqFileManageController.java @@ -0,0 +1,386 @@ +package com.thing.eq.eqfilemanage.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqfilemanage.dto.EqFileManageDTO; +import com.thing.eq.eqfilemanage.dto.EqFileManageResDTO; +import com.thing.eq.eqfilemanage.dto.EqFileManageTreeDTO; +import com.thing.eq.eqfilemanage.dto.MoveFileParams; +import com.thing.eq.eqfilemanage.entity.EqFileDeleteEntity; +import com.thing.eq.eqfilemanage.entity.EqFileManageEntity; +import com.thing.eq.eqfilemanage.service.EqFileDeleteService; +import com.thing.eq.eqfilemanage.service.EqFileManageService; +import com.thing.eq.task.DeleteFileTask; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.oss.entity.SysOssEntity; +import com.thing.sys.oss.service.SysOssService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import java.io.IOException; +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * 设备文件管理 + * + * @author zy zy@xx,com + * @since 3.0 2021-10-28 + */ +@RestController +@RequestMapping("eqfilemanage/eqfilemanage") +@Tag(name = "设备-设备文件管理") +public class EqFileManageController { + @Autowired + private EqFileManageService eqFileManageService; + +// @Autowired +// private FileServiceFeign fileServiceFeign; + + @Autowired + private EqFileDeleteService eqFileDeleteService; + + @Autowired + private DeleteFileTask deleteFileTask; + + @Autowired + private SysOssService sysOssService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "fileName", description = "文件名"), + @Parameter(name = "parentId", description = "父目录id") + }) +// @RequiresPermissions("eqfilemanage:eqfilemanage:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + + PageData page = eqFileManageService.getPageData(params,EqFileManageDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("queryAll") + @Operation(summary = "查询目录下的文件") +// @RequiresPermissions("eqfilemanage:eqfilemanage:info") + public Result queryAll(@RequestParam(value = "fileId", required = false) Long fileId,@RequestParam(value = "fileName", required = false) String fileName) { + + if (Objects.isNull(fileId)) { + fileId = 0L; + } + List eqFileManageEntityList = eqFileManageService.selectFileByParentId(fileId, "0",fileName); + + if (CollectionUtil.isNotEmpty(eqFileManageEntityList)) { + for (EqFileManageEntity file : eqFileManageEntityList) { + if (Objects.isNull(file)) { + continue; + } + if ("0".equals(file.getFileType())) { + findFileSize(file); + } + } + } + + EqFileManageTreeDTO parentFile = getParentFile(fileId, null); + EqFileManageResDTO eqFileManageResDTO = new EqFileManageResDTO(); + eqFileManageResDTO.setParentFileInfo(parentFile); + + List eqFileManageDTOS = ConvertUtils.sourceToTarget(eqFileManageEntityList,EqFileManageDTO.class); + eqFileManageResDTO.setChildFileInfos(eqFileManageDTOS); + + return new Result().ok(eqFileManageResDTO); + } + + public void findFileSize(EqFileManageEntity curDir) { + + BigDecimal curTotal = Objects.isNull(curDir.getFileSize()) ? new BigDecimal("0") : curDir.getFileSize(); + List allChild = eqFileManageService.selectAllChildFileByParentId(curDir.getId(), "0", "0"); + + for (EqFileManageEntity file : allChild) { + if (Objects.isNull(file) || Objects.isNull(file.getFileSize())) { + continue; + } + curTotal = curTotal.add(file.getFileSize()); + } + + curDir.setFileSize(curTotal); + } + + // 查询当前目录下的所有直接父目录 + public EqFileManageTreeDTO getParentFile(Long parentId, EqFileManageTreeDTO child) { + EqFileManageTreeDTO parentTreeDTO = new EqFileManageTreeDTO(); + EqFileManageDTO eqFileManageDTO = eqFileManageService.getByIdAs(parentId,EqFileManageDTO.class); + + if (!Objects.isNull(eqFileManageDTO)) { + BeanUtils.copyProperties(eqFileManageDTO, parentTreeDTO); + parentTreeDTO.setChilds(Arrays.asList(child)); + if (eqFileManageDTO.getParentId() > 0) { + return getParentFile(eqFileManageDTO.getParentId(), parentTreeDTO); + } + } + + return parentTreeDTO; + } + + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqfilemanage:eqfilemanage:info") + public Result get(@PathVariable("id") Long id) { + EqFileManageDTO data = eqFileManageService.getByIdAs(id,EqFileManageDTO.class); + + return new Result().ok(data); + } + + @GetMapping("deleteFileTask") + @Operation(summary = "文件删除定时任务手动触发") +// @RequiresPermissions("equipment:fileManager:delete") + public Result deleteFileTask() { + deleteFileTask.run(); + return new Result().ok(null); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:fileManager:add") + public Result save(EqFileManageDTO dto, + @RequestParam(value = "file", required = false) MultipartFile file) throws IOException { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + if (Objects.isNull(dto.getParentId())) { + dto.setParentId(0L); + } + EqFileManageEntity eqFileManageEntity = eqFileManageService.selectFileByName(dto.getFileName(), dto.getParentId(), dto.getFileType()); + if (!Objects.isNull(eqFileManageEntity)) { + throw new SysException("文件或目录已存在"); + } + if (dto.getParentId() > 0) { + EqFileManageDTO eqFileManageDTO = eqFileManageService.getByIdAs(dto.getParentId(),EqFileManageDTO.class); + if (Objects.isNull(eqFileManageDTO)) { + throw new SysException("所选文件夹不存在"); + } + if ("1".equals(eqFileManageDTO.getFileType())) { + throw new SysException("不能在非文件夹类型中创建文件"); + } + } + if (dto.getParentId() == 0) { + dto.setFilePath("/"); + dto.setFileIdPath("/"); + } else { + EqFileManageEntity parentFile = eqFileManageService.getById(dto.getParentId()); + if (!Objects.isNull(parentFile)) { + dto.setFilePath(parentFile.getFilePath() + parentFile.getFileName() + "/"); + dto.setFileIdPath(parentFile.getFileIdPath() + parentFile.getId() + "/"); + } + } + if ("1".equals(dto.getFileType())) { + Map data = null; + if (!Objects.isNull(file) && StringUtils.isNotBlank(file.getOriginalFilename())) { + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + + //上传文件 + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + String url = OSSFactory.build().uploadSuffix(file.getBytes(), extension); + + //保存文件信息 + SysOssEntity ossEntity = new SysOssEntity(); + ossEntity.setUrl(url); + ossEntity.setCreateDate(DateTimeUtils.dateToTimestamp(new Date())); + sysOssService.save(ossEntity); + +// Map data = new HashMap<>(1); + data.put("src", url); + data.put("id", ossEntity.getId()); +// Result response = fileServiceFeign.uploadFile(file); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException("文件上传失败:" + (Objects.isNull(response) ? "" : response.getMsg())); +// } +// data = (Map) response.getData(); + if (Objects.isNull(data)) { + throw new SysException("文件上传失败"); + } + +// url = String.valueOf(data.get("src")); + dto.setFileStorePath(url); + } + } + + + eqFileManageService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:fileManager:update") + public Result update(@RequestBody EqFileManageDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + EqFileManageDTO manageDTO = eqFileManageService.getByIdAs(dto.getId(),EqFileManageDTO.class); + + if (Objects.isNull(manageDTO)) { + throw new SysException("文件或文件夹不存在"); + } + if (!manageDTO.getFileType().equals(dto.getFileType())) { + throw new SysException("文件类型不能修改"); + } + + if ("0".equals(dto.getFileType())) { + dto.setFileSize(null); + } + + eqFileManageService.updateDto(dto); + + return new Result(); + } + + @PostMapping("moveFile") + @Operation(summary = "粘贴(移动)") + @LogOperation("粘贴(移动)") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:fileManager:move") + public Result moveFile(@RequestBody MoveFileParams params) { + //效验数据 + ValidatorUtils.validateEntity(params, UpdateGroup.class, DefaultGroup.class); + + List fileIds = params.getSrcFileId().stream().map(item -> Long.parseLong(item)).collect(Collectors.toList()); + + List entityList = eqFileManageService.selectChildFileByParentId(fileIds, "1"); + if (CollectionUtil.isNotEmpty(entityList)) { + List fileList = entityList.stream().map(item -> item.getId()).collect(Collectors.toList()); + if (fileList.contains(params.getDisDirFileId())) { + throw new SysException("待移动的目标目录,不能是当前所移动的文件的子目录"); + } + } + + if ("0".equals(params.getSrcDirFileId()) && "0".equals(params.getDisDirFileId())) { + throw new SysException("文件已经在当前目录下"); + } + + // 源目录 + EqFileManageDTO srcDirFile = null; + + if (params.getSrcDirFileId() != 0) { + srcDirFile = eqFileManageService.getByIdAs(params.getSrcDirFileId(),EqFileManageDTO.class); + if (Objects.isNull(srcDirFile)) { + throw new SysException("源目录不存在"); + } + if ("1".equals(srcDirFile.getFileType())) { + throw new SysException("源目录是文件,无法粘贴(移动)"); + } + } + EqFileManageDTO disDirFile = null; + if(params.getDisDirFileId() != 0){ + disDirFile = eqFileManageService.getByIdAs(params.getDisDirFileId(),EqFileManageDTO.class); + if (Objects.isNull(disDirFile)) { + throw new SysException("目标文件目录不存在"); + } + if ("1".equals(disDirFile.getFileType())) { + throw new SysException("目标文件不是目录无法粘贴(移动)"); + } + } + // 目标目录 + if ("1".equals(params.getOperateType())) { + eqFileManageService.cutMove(srcDirFile, disDirFile, params.getSrcFileId(), params.getSrcDirFileId(), params.getDisDirFileId()); + } else if ("0".equals(params.getOperateType())) { + eqFileManageService.copyPaste(disDirFile, params.getSrcFileId(), params.getDisDirFileId()); + } else { + throw new SysException("不支持除复制粘贴、剪切粘贴(移动)之外的其它类型的操作"); + } + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:fileManager:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + List entityList = eqFileManageService.selectChildFileByParentId(Arrays.asList(ids), "1"); + if (CollectionUtil.isEmpty(entityList)) { + return new Result(); + } + List deleteFileStorePath = entityList.stream().map(item -> item.getFileStorePath()).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(deleteFileStorePath)) { + List deleteEntityList = new ArrayList<>(); + deleteFileStorePath.forEach(item -> { + EqFileDeleteEntity deleteEntity = new EqFileDeleteEntity(); + deleteEntity.setUrl(item); + + deleteEntityList.add(deleteEntity); + }); + eqFileDeleteService.batchHandle(deleteEntityList, null); + } + + List deleteFileIdList = entityList.stream().map(item -> item.getId()).collect(Collectors.toList()); + eqFileManageService.removeByIds(deleteFileIdList); + return new Result(); + } + + + @DeleteMapping("brandUseFileDelete") + @Operation(summary = "通用未使用的文件删除") + @LogOperation("通用未使用的文件删除") + @Parameters({ + @Parameter(name = "url", description = "文件路径"), + @Parameter(name = "type", description = "删除方式 0:由定时任务删除 1:立即删除") + }) + @Transactional(rollbackFor = Exception.class) +// @RequiresPermissions("equipment:fileManager:delete") + public Result brandUseFileDelete(@RequestParam String url, @RequestParam String type) { + //效验数据 + if (StringUtils.isBlank(url)) { + throw new SysException("文件路径不能为空"); + } + // 0:由定时任务删除 1:立即删除 + List urlList = Arrays.asList(url); + if ("0".equals(type)) { + eqFileDeleteService.saveToDeletingFile(urlList); + } else if ("1".equals(type)) { + eqFileDeleteService.deleteFile(urlList); + } else { + throw new SysException("0:由定时任务删除 1:立即删除,不支持其它类型的删除"); + } + return new Result(); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/DeleteFileParam.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/DeleteFileParam.java new file mode 100644 index 0000000..ce0c0ac --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/DeleteFileParam.java @@ -0,0 +1,22 @@ +package com.thing.eq.eqfilemanage.dto; + +import lombok.Data; + +import java.util.List; + +/** + * 文件上传 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class DeleteFileParam { + + /** + * URL地址 + */ + private List urlList; + + private String deleteType; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileDeleteDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileDeleteDTO.java new file mode 100644 index 0000000..3667c3d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileDeleteDTO.java @@ -0,0 +1,24 @@ +package com.thing.eq.eqfilemanage.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 文件删除 +* +* @author zy zy@aa.com +* @since 3.0 2021-11-03 +*/ +@Data +@Schema(description = "文件删除") +public class EqFileDeleteDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "oss表id") + private Long id; + @Schema(description = "文件路径") + private String url; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageDTO.java new file mode 100644 index 0000000..b5f4ce8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageDTO.java @@ -0,0 +1,57 @@ +package com.thing.eq.eqfilemanage.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 设备文件管理 +* +* @author zy zy@xx,com +* @since 3.0 2021-10-28 +*/ +@Data +@Schema(description = "设备文件管理") +public class EqFileManageDTO extends BaseDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "文件id(修改时必填)",required = true) + @NotNull(message = "文件id不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "文件名",required = true) + @NotEmpty(message = "文件名不能为空",groups = DefaultGroup.class) + private String fileName; + @Schema(description = "文件类型 0:目录 1:文件 (新增时不能为空)",required = true) + @NotEmpty(message = "文件类型不能为空",groups = AddGroup.class) + private String fileType; + @Schema(description = "文件后缀 文件类型是1的时候才有值") + private String fileSuffix; + @Schema(description = "文件路径") + private String filePath; + @Schema(description = "文件父id") + private Long parentId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "文件大小") + private BigDecimal fileSize; + @Schema(description = "文件实际存储路径") + private String fileStorePath; + @Schema(description = "文件父级id集合") + private String fileIdPath; + @Schema(description = "文件描述") + private String fileDesc; + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "租户编码") + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageResDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageResDTO.java new file mode 100644 index 0000000..e95e88c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageResDTO.java @@ -0,0 +1,23 @@ +package com.thing.eq.eqfilemanage.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** +* 设备文件管理 +* +* @author zy zy@xx,com +* @since 3.0 2021-10-28 +*/ +@Data +@Schema(description = "设备文件管理") +public class EqFileManageResDTO { + @Schema(description = "父文件") + private EqFileManageTreeDTO parentFileInfo; + + @Schema(description = "子文件列表") + private List childFileInfos; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageTreeDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageTreeDTO.java new file mode 100644 index 0000000..0830c97 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/EqFileManageTreeDTO.java @@ -0,0 +1,20 @@ +package com.thing.eq.eqfilemanage.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** +* 设备文件管理 +* +* @author zy zy@xx,com +* @since 3.0 2021-10-28 +*/ +@Data +@Schema(description = "设备文件管理") +public class EqFileManageTreeDTO extends EqFileManageDTO { + + @Schema(description = "子文件") + private List childs; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/MoveFileParams.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/MoveFileParams.java new file mode 100644 index 0000000..43cdd26 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/dto/MoveFileParams.java @@ -0,0 +1,34 @@ +package com.thing.eq.eqfilemanage.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/1 0001 14:10:59 + */ +@Data +public class MoveFileParams { + + @Schema(description = "源目录文件id",required = true) + @NotNull(message = "源目录文件id不能为空",groups = DefaultGroup.class) + private Long srcDirFileId; + + @Schema(description = "复制的文件id集合",required = true) + @NotEmpty(message = "复制的文件id集合不能为空",groups = DefaultGroup.class) + @NotNull(message = "复制的文件id集合不能为空",groups = DefaultGroup.class) + private List srcFileId; + + @Schema(description = "粘贴(移动)的文件id",required = true) + @NotNull(message = "粘贴(移动)的文件id不能为空",groups = DefaultGroup.class) + private Long disDirFileId; + + @Schema(description = "操作类型",required = true) + @NotNull(message = "操作类型不能为空 1:剪切粘贴(移动)---- (源目录更新为新的目录)",groups = DefaultGroup.class) + private String operateType; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileDeleteEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileDeleteEntity.java new file mode 100644 index 0000000..2d9cbae --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileDeleteEntity.java @@ -0,0 +1,30 @@ +package com.thing.eq.eqfilemanage.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 文件删除 + * + * @author zy zy@aa.com + * @since 3.0 2021-11-03 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_file_delete") +public class EqFileDeleteEntity { + private static final long serialVersionUID = 1L; + + /** + * oss表id + */ + @Id + private Long id; + /** + * 文件路径 + */ + private String url; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileManageEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileManageEntity.java new file mode 100644 index 0000000..e514822 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/EqFileManageEntity.java @@ -0,0 +1,84 @@ +package com.thing.eq.eqfilemanage.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 设备文件管理 + * + * @author zy zy@xx,com + * @since 3.0 2021-10-28 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_file_manage") +public class EqFileManageEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 文件名 + */ + private String fileName; + /** + * 文件类型 0:目录 1:文件 + */ + private String fileType; + /** + * 文件后缀 文件类型是1的时候才有值 + */ + private String fileSuffix; + /** + * 文件路径 + */ + private String filePath; + /** + * 文件父id + */ + private Long parentId; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + + /** + * 文件大小 + */ + private BigDecimal fileSize; + /** + * 文件实际存储路径 + */ + private String fileStorePath; + /** + * 文件父级id集合 + */ + private String fileIdPath; + /** + * 文件描述 + */ + private String fileDesc; + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/SysOssEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/SysOssEntity.java new file mode 100644 index 0000000..57ab8d7 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/entity/SysOssEntity.java @@ -0,0 +1,31 @@ +package com.thing.eq.eqfilemanage.entity; + +import lombok.Data; + +import java.util.Date; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/3 0003 13:26:15 + */ +@Data +public class SysOssEntity { + /** + * id + */ + private Long id; + /** + * URL地址 + */ + private String url; + + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileDeleteExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileDeleteExcel.java new file mode 100644 index 0000000..fffad6a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileDeleteExcel.java @@ -0,0 +1,24 @@ +package com.thing.eq.eqfilemanage.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +/** + * 文件删除 + * + * @author zy zy@aa.com + * @since 3.0 2021-11-03 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqFileDeleteExcel { + @Excel(name = "oss表id", orderNum = "1") + private Long id; + @Excel(name = "文件路径", orderNum = "2") + private String url; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileManageExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileManageExcel.java new file mode 100644 index 0000000..c77d27d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/excel/EqFileManageExcel.java @@ -0,0 +1,43 @@ +package com.thing.eq.eqfilemanage.excel; + + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备文件管理 + * + * @author zy zy@xx,com + * @since 3.0 2021-10-28 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqFileManageExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "文件名", orderNum = "2") + private String fileName; + @Excel(name = "文件类型 0:目录 1:文件", orderNum = "3") + private String fileType; + @Excel(name = "文件后缀 文件类型是1的时候才有值", orderNum = "4") + private String fileSuffix; + @Excel(name = "文件路径", orderNum = "5") + private String filePath; + @Excel(name = "文件父id", orderNum = "6") + private Long pFileId; + @Excel(name = "创建人", orderNum = "7") + private Long creator; + @Excel(name = "创建时间", orderNum = "8") + private Date createDate; + @Excel(name = "更新人", orderNum = "9") + private Long updater; + @Excel(name = "更新时间", orderNum = "10") + private Date updateDate; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileDeleteMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileDeleteMapper.java new file mode 100644 index 0000000..87feb0b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileDeleteMapper.java @@ -0,0 +1,18 @@ +package com.thing.eq.eqfilemanage.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqfilemanage.entity.EqFileDeleteEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** +* 文件删除 +* +* @author zy zy@aa.com +* @since 3.0 2021-11-03 +*/ +@Mapper +public interface EqFileDeleteMapper extends PowerBaseMapper { + void deleteFile(@Param("url") String url); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileManageMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileManageMapper.java new file mode 100644 index 0000000..2eb76f5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/mapper/EqFileManageMapper.java @@ -0,0 +1,21 @@ +package com.thing.eq.eqfilemanage.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqfilemanage.entity.EqFileManageEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 设备文件管理 +* +* @author zy zy@xx,com +* @since 3.0 2021-10-28 +*/ +@Mapper +public interface EqFileManageMapper extends PowerBaseMapper { + + List queryAll(Long parentId); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileDeleteService.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileDeleteService.java new file mode 100644 index 0000000..f01c8ae --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileDeleteService.java @@ -0,0 +1,33 @@ +package com.thing.eq.eqfilemanage.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqfilemanage.dto.EqFileDeleteDTO; +import com.thing.eq.eqfilemanage.entity.EqFileDeleteEntity; + +import java.util.List; + +/** + * 文件删除 + * + * @author zy zy@aa.com + * @since 3.0 2021-11-03 + */ +public interface EqFileDeleteService extends IBaseService { + /** + * 查询所欲待删除的本地文件 + */ + List selectAll(); + /** + * 立即删除本地文件 + */ + void deleteFile(List urlList); + /** + * 加入待删除文件列表,等待定时任务清除本地文件 + */ + void saveToDeletingFile(List urlList); + + /** + * 批量加入待删除文件列表 + */ + void batchHandle(List listData, Integer batchSize); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileManageService.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileManageService.java new file mode 100644 index 0000000..98f8881 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/EqFileManageService.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqfilemanage.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqfilemanage.dto.EqFileManageDTO; +import com.thing.eq.eqfilemanage.entity.EqFileManageEntity; + +import java.util.List; + +/** + * 设备文件管理 + * + * @author zy zy@xx,com + * @since 3.0 2021-10-28 + */ +public interface EqFileManageService extends IBaseService { + /** + * 文件名查询 + */ + EqFileManageEntity selectFileByName(String fileName, Long parentId, String fileType); + /** + * 根据文件父id查询直接子集 + */ + List selectFileByParentId(Long parentId, String needFileSize, String fileName); + /** + * 根据文件父id查询所有层级子集 + */ + List selectAllChildFileByParentId(Long parentId, String containDir, String containParentFile); + + List selectChildFileByParentId(List srcFileIds, String containParentFile); + /** + * 根据文件父id查询 + */ + void deleteFileByParentId(Long parentId); + + // 剪切移动 + void cutMove(EqFileManageDTO srcDirFile, EqFileManageDTO disDirFile, List srcFileId, Long srcDirFileId, Long disDirFileId); + + // 复制粘贴 + void copyPaste(EqFileManageDTO disDirFile, List srcFileId, Long disDirFileId); + + boolean checkFileExist(Long disDirFileId, List srcFileId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileDeleteServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileDeleteServiceImpl.java new file mode 100644 index 0000000..931df90 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileDeleteServiceImpl.java @@ -0,0 +1,109 @@ +package com.thing.eq.eqfilemanage.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqfilemanage.dto.DeleteFileParam; +import com.thing.eq.eqfilemanage.entity.EqFileDeleteEntity; +import com.thing.eq.eqfilemanage.mapper.EqFileDeleteMapper; +import com.thing.eq.eqfilemanage.service.EqFileDeleteService; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.oss.service.SysOssService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 文件删除 + * + * @author zy zy@aa.com + * @since 3.0 2021-11-03 + */ +@Service +@Slf4j +public class EqFileDeleteServiceImpl extends BaseServiceImpl implements EqFileDeleteService { + @Autowired + private SysParamsService sysParamsService; + +// @Autowired +// private FileServiceFeign fileServiceFeign; + + @Autowired + private SysOssService sysOssService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public List selectAll() { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.isNotNull("url"); + return mapper.selectListExt(wrapper); + } + + @Override + public void deleteFile(List urlList) { + + DeleteFileParam deleteFileParam = new DeleteFileParam(); + deleteFileParam.setUrlList(urlList); + deleteFileParam.setDeleteType("0"); + //todo +// sysOssService.deleteByUrlList(deleteFileParam.getUrlList(), deleteFileParam.getDeleteType()); +// Result resultResponse = fileServiceFeign.deleteFile(deleteFileParam); +// if(Objects.isNull(resultResponse) || resultResponse.getCode() != 0 ){ +// throw new RenException(!Objects.isNull(resultResponse) ? resultResponse.getMsg() : "服务异常"); +// } + } + + @Override + public void saveToDeletingFile(List urlList) { + + if (CollectionUtil.isEmpty(urlList)) { + return; + } + if (CollectionUtil.isNotEmpty(urlList)) { + List deleteEntityList = new ArrayList<>(); + urlList.forEach(item ->{ + EqFileDeleteEntity deleteEntity = new EqFileDeleteEntity(); + deleteEntity.setUrl(item); + + deleteEntityList.add(deleteEntity); + }); + batchHandle(deleteEntityList, null); + } + } + + + // 分批更新 + @Override + public void batchHandle(List listData, Integer batchSize) { + if (CollectionUtil.isEmpty(listData)) { + return; + } + if (Objects.isNull(batchSize)) { + batchSize = 100; + } + int count = listData.size() % batchSize == 0 ? listData.size() / batchSize : (listData.size() / batchSize) + 1; + + for (int i = 0; i < count; i++) { + int startIndex = i * batchSize; + int endIndex = 0; + if (i == count - 1) { + endIndex = listData.size(); + } else { + endIndex = startIndex + batchSize; + } + List batchData = listData.subList(startIndex, endIndex); + this.saveBatch(batchData); + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileManageServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileManageServiceImpl.java new file mode 100644 index 0000000..bcb3e50 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqfilemanage/service/impl/EqFileManageServiceImpl.java @@ -0,0 +1,269 @@ +package com.thing.eq.eqfilemanage.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqfilemanage.dto.EqFileManageDTO; +import com.thing.eq.eqfilemanage.entity.EqFileManageEntity; +import com.thing.eq.eqfilemanage.mapper.EqFileManageMapper; +import com.thing.eq.eqfilemanage.service.EqFileManageService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 设备文件管理 + * + * @author zy zy@xx,com + * @since 3.0 2021-10-28 + */ +@Service +public class EqFileManageServiceImpl extends BaseServiceImpl implements EqFileManageService { + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + + String fileName = Objects.isNull(params.get("fileName")) ? "" : String.valueOf(params.get("fileName")); + String parentId = Objects.isNull(params.get("parentId")) ? "" : String.valueOf(params.get("parentId")); + + if (StringUtils.isNotBlank(fileName) && StringUtils.isNotBlank(parentId)) { + wrapper.like("file_name", fileName); + wrapper.eq("parent_id", parentId); + } + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + return wrapper; + } + + + @Override + public EqFileManageEntity selectFileByName(String fileName, Long parentId, String fileType) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("file_name", fileName); + wrapper.eq("parent_id", parentId); + wrapper.eq("file_type", fileType); +// wrapper.last("limit 1"); + EqFileManageEntity eqFileManageEntity = mapper.selectOneExt(wrapper); + return eqFileManageEntity; + } + + @Override + public List selectFileByParentId(Long parentId, String needFileSize, String fileName) { + + List eqFileManageEntities = null; + if ("0".equals(needFileSize)) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("parent_id", parentId); + if (StringUtils.isNotBlank(fileName)) { + wrapper.like("file_name", fileName); + } + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + wrapper.orderBy("file_type",true); + wrapper.orderBy("create_date",false); + + eqFileManageEntities = mapper.selectListExt(wrapper); + } else { + eqFileManageEntities = mapper.queryAll(parentId); + } + + return eqFileManageEntities; + } + + @Override + public List selectAllChildFileByParentId(Long parentId, String containDir, String containParentFile) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("file_id_path", parentId); + + if (!"1".equals(containDir)) { + wrapper.eq("file_type", "1"); + } + if ("1".equals(containParentFile)) { +// wrapper.or(); + wrapper.eq("id", parentId); + } + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqFileManageEntities = mapper.selectListExt(wrapper); + return eqFileManageEntities; + } + + @Override + public List selectChildFileByParentId(List srcFileIds, String containParentFile) { + List eqFileManageEntities = new ArrayList<>(); + for (Long srcFileId : srcFileIds) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("file_id_path", srcFileId); + if ("1".equals(containParentFile)) { +// wrapper.or(); + wrapper.eq("id", srcFileId); + } + List batchEntity = mapper.selectListExt(wrapper); + eqFileManageEntities.addAll(batchEntity); + } + + return eqFileManageEntities; + } + + @Override + public void deleteFileByParentId(Long parentId) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("file_id_path", parentId); + mapper.deleteByQuery(wrapper); + } + + // 剪切移动 + @Override + public void cutMove(EqFileManageDTO srcDirFile, EqFileManageDTO disDirFile, List srcFileId, Long srcDirFileId, Long disDirFileId) { + String srcFilePath = Objects.isNull(srcDirFile) ? "/" : srcDirFile.getFilePath() + srcDirFile.getFileName() + "/"; + String srcFileIdPath = Objects.isNull(srcDirFile) ? "/" : srcDirFile.getFileIdPath() + srcDirFile.getId() + "/"; + + String disFilePath = Objects.isNull(disDirFile) ? "/" : disDirFile.getFilePath() + disDirFile.getFileName() + "/"; + String disFileIdPath = Objects.isNull(disDirFile) ? "/" : disDirFile.getFileIdPath() + disDirFile.getId() + "/"; + + for (String moveDirFileId : srcFileId) { + + EqFileManageEntity moveDirFile = this.getByIdAs(Long.parseLong(moveDirFileId),EqFileManageEntity.class); + if (Objects.isNull(moveDirFile)) { + continue; + } + // 验证目标目录是否存在相同名称的文件夹 + EqFileManageEntity eqFileManageEntity = this.selectFileByName(moveDirFile.getFileName(), disDirFileId, moveDirFile.getFileType()); + if (!Objects.isNull(eqFileManageEntity)) { + throw new SysException("文件或目录:" + moveDirFile.getFileName() + "已存在"); + } + + List fileList = this.selectAllChildFileByParentId(Long.parseLong(moveDirFileId), "1", "1"); + + if ("/".equals(srcFilePath)) { + for (EqFileManageEntity entity : fileList) { + entity.setFilePath(disFilePath); + entity.setFileIdPath(disFileIdPath); + // id等于源目录id的 -> 待粘贴的直接子文件 + if (entity.getParentId().compareTo(srcDirFileId) == 0) { + entity.setParentId(disDirFileId); + } + } + } else { + for (EqFileManageEntity entity : fileList) { + String oldFilePath = entity.getFilePath(); + String oldFileIdPath = entity.getFileIdPath(); + + if (oldFilePath.contains(srcFilePath)) { + entity.setFilePath(oldFilePath.replace(srcFilePath, disFilePath)); + } + if (oldFileIdPath.contains(srcFileIdPath)) { + entity.setFileIdPath(oldFileIdPath.replace(srcFileIdPath, disFileIdPath)); + } + // id等于源目录id的 -> 待粘贴的直接子文件 + if (entity.getParentId().compareTo(srcDirFileId) == 0) { + entity.setParentId(disDirFileId); + } + } + } + + this.updateBatch(fileList); + } + } + + // 分批更新 + public void batchHandle(List listData, Integer batchSize) { + if (CollectionUtil.isEmpty(listData)) { + return; + } + if (Objects.isNull(batchSize)) { + batchSize = 100; + } + int count = listData.size() % batchSize == 0 ? listData.size() / batchSize : (listData.size() / batchSize) + 1; + + for (int i = 0; i < count; i++) { + int startIndex = i * batchSize; + int endIndex = 0; + if (i == count - 1) { + endIndex = listData.size(); + } else { + endIndex = startIndex + batchSize; + } + List batchData = listData.subList(startIndex, endIndex); + this.updateBatch(batchData); + } + } + + + // 复制粘贴 + @Override + public void copyPaste(EqFileManageDTO disDirFile, List srcFileId, Long disDirFileId) { + String disFilePath = disDirFile.getFilePath() + disDirFile.getFileName() + "/"; + String disFileIdPath = disDirFile.getFileIdPath() + disDirFile.getId() + "/"; + + for (String moveDirFileId : srcFileId) { + EqFileManageEntity moveDirFile = this.getByIdAs(Long.parseLong(moveDirFileId),EqFileManageEntity.class); + if (Objects.isNull(moveDirFile)) { + continue; + } + // 验证目标目录是否存在相同名称的文件夹 + EqFileManageEntity eqFileManageEntity = this.selectFileByName(moveDirFile.getFileName(), disDirFileId, moveDirFile.getFileType()); + if (!Objects.isNull(eqFileManageEntity)) { + throw new SysException("文件或目录:" + moveDirFile.getFileName() + "已存在"); + } + + // 新增时id置null + moveDirFile.setId(null); + moveDirFile.setParentId(disDirFileId); + moveDirFile.setFilePath(disFilePath); + moveDirFile.setFileIdPath(disFileIdPath); + + this.saveDto(moveDirFile); + + if ("0".equals(moveDirFile.getFileType())) { + List childFile = this.selectAllChildFileByParentId(moveDirFile.getId(), "1", "0"); + copyInsert(childFile, moveDirFile); + } + } + } + + @Override + public boolean checkFileExist(Long disDirFileId, List srcFileId) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("file_id_path", disDirFileId); +// wrapper.like("file_id_path", srcFileId); + +// wrapper.last(" limit 1"); + + + return false; + } + + // 递归新增 + public void copyInsert(List fileList, EqFileManageEntity parentFile) { + String disFilePath = parentFile.getFilePath() + parentFile.getFileName() + "/"; + String disFileIdPath = parentFile.getFileIdPath() + parentFile.getId() + "/"; + + if (CollectionUtil.isNotEmpty(fileList)) { + return; + } + + for (EqFileManageEntity entity : fileList) { + // 新增时id置null + entity.setId(null); + entity.setParentId(parentFile.getId()); + entity.setFilePath(disFilePath); + entity.setFileIdPath(disFileIdPath); + + this.saveDto(entity); + + if ("0".equals(entity.getFileType())) { + List childFile = this.selectAllChildFileByParentId(parentFile.getId(), "1", "0"); + copyInsert(childFile, entity); + } + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/EqScrapController.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/EqScrapController.java new file mode 100644 index 0000000..286470b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/EqScrapController.java @@ -0,0 +1,145 @@ +package com.thing.eq.eqmanager.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqmanager.dto.EqScrapDTO; +import com.thing.eq.eqmanager.excel.EqScrapExcel; +import com.thing.eq.eqmanager.service.EqScrapService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** +* 设备报废 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-02 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqscrap") +@Tag(name="设备-设备报废") +public class EqScrapController { + @Autowired + private EqScrapService eqScrapService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name ="scrapDisposal", description = "报废是否处理 0未处理 1已处理"), + @Parameter(name = "relationTypeId", description = "结构类型id"), + @Parameter(name = "thingName", description = "设备名称") + }) +// @RequiresPermissions("equipment:scrapped:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqScrapService.page(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "查询信息") + public Result get(@PathVariable("id") Long id){ + EqScrapDTO data = eqScrapService.getByIdAs(id,EqScrapDTO.class); + return new Result().ok(data); + } + + @GetMapping("{eqId}/check") + @Operation(summary = "设备报废校验") + @LogOperation("设备报废校验") +// @RequiresPermissions("equipment:equipmentStatus:scrapped") + public Result> checkScrap(@PathVariable("eqId") Long eqId){ + Map result = eqScrapService.checkScrap(eqId); + return new Result>().ok(result); + } + + + @GetMapping("save/{eqId}") + @Operation(summary = "设备报废") + @LogOperation("设备报废") +// @RequiresPermissions("equipment:equipmentStatus:scrapped") + public Result saveScrap(@PathVariable("eqId") Long eqId,@RequestParam String scrap){ + eqScrapService.saveScrap(eqId,scrap); + return new Result(); + } + + @PostMapping("handle/{id}") + @Operation(summary = "设备报废处理(审核)") + @LogOperation("设备报废处理(审核)") +// @RequiresPermissions("equipment:scrapped:treatment") + public Result saveScrapDisposal(@PathVariable("id") Long id){ + eqScrapService.saveScrapDisposal(id); + return new Result(); + } + +// @PutMapping +// @ApiOperation("修改") +// @LogOperation("修改") +// public Result update(@RequestBody EqScrapDTO dto){ +// //效验数据 +// ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); +// +// eqScrapService.update(dto); +// +// return new Result(); +// } +// +// @DeleteMapping +// @ApiOperation("删除") +// @LogOperation("删除") +// public Result delete(@RequestBody Long[] ids){ +// //效验数据 +// AssertUtils.isArrayEmpty(ids, "id"); +// +// eqScrapService.delete(ids); +// +// return new Result(); +// } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @Parameters({ +// @Parameter(name ="scrapDisposal", value = "报废是否处理 0未处理 1已处理", paramType = "query", dataType="String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query",required = true, dataType="String") +// }) +// @RequiresPermissions("equipment:scrapped:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + List list = eqScrapService.getExportlist(params); + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (EqScrapExcel eqScrapExcel : list ){ + if (idList.contains(eqScrapExcel.getId())){ + newList.add(eqScrapExcel); + } + } + }else{ + newList.addAll(list); + } + ExcelUtils.exportExcel(newList,null, "设备报废", EqScrapExcel.class,"设备报废.xls",response); + +// ExcelUtils.exportExcelToTarget(response, "设备报废", newList, EqScrapExcel.class); +// ExcelUtils.exportExcelToTarget(response, null, "设备报废", list, EqScrapExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingBaseInfoController.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingBaseInfoController.java new file mode 100644 index 0000000..4c12551 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingBaseInfoController.java @@ -0,0 +1,128 @@ +package com.thing.eq.eqmanager.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.excel.IotThingBaseInfoExcel; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 设备基础信息 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping("eqBaseInfo") +@Tag(name="设备-设备基础信息") +public class IotThingBaseInfoController { + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "deptId", description = "部门") , + @Parameter(name = "keyWord", description = "关键字(设备名称/编号/型号)") + }) +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = iotThingBaseInfoService.getPageData(params,IotThingBaseInfoDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:info") + public Result get(@PathVariable("id") Long id){ + IotThingBaseInfoDTO data = iotThingBaseInfoService.getByIdAs(id,IotThingBaseInfoDTO.class); + + return new Result().ok(data); + } + + @GetMapping("getByThingId") + @Operation(summary = "信息") + public Result getByThingId(Long thingId){ + IotThingBaseInfoDTO data = iotThingBaseInfoService.getByThingsId(thingId); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:save") + public Result save(@RequestBody IotThingBaseInfoDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + iotThingBaseInfoService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:update") + public Result update(@RequestBody IotThingBaseInfoDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + iotThingBaseInfoService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + iotThingBaseInfoService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("eqBaseInfo:iotthingbaseinfo:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = iotThingBaseInfoService.listAs(params,IotThingBaseInfoDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, IotThingBaseInfoExcel.class); + ExcelUtils.exportExcel(list1,null, "设备基础信息", IotThingBaseInfoExcel.class,"设备基础信息.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "设备基础信息", list, IotThingBaseInfoExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingsController.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingsController.java new file mode 100644 index 0000000..28e51ae --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/controller/IotThingsController.java @@ -0,0 +1,125 @@ +package com.thing.eq.eqmanager.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.service.IotThingEntityService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +/** +* 设备管理 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping(Constant.API_BASE + "/eqthings") +@Tag(name="设备-设备管理") +public class IotThingsController { + @Autowired + private IotThingsService iotThingsService; +// @Autowired +// private ThingsRelationService thingsRelationService; + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @Autowired + private IotThingEntityService iotThingEntityService; + +// @GetMapping("page") +// @ApiOperation("分页(废弃)") +// @Parameters({ +// @Parameter(name = Constant.PAGE, value = "当前页码,从1开始", paramType = "query", required = true, dataType="int") , +// @Parameter(name = Constant.LIMIT, value = "每页显示记录数", paramType = "query",required = true, dataType="int") , +// @Parameter(name = Constant.ORDER_FIELD, value = "排序字段", paramType = "query", dataType="String") , +// @Parameter(name = Constant.ORDER, value = "排序方式,可选值(asc、desc)", paramType = "query", dataType="String") +// }) +// @RequiresPermissions("equipment:equipmentStatus:page") +// public Result> page(@ApiIgnore @RequestParam Map params){ +// PageData page = iotThingsService.page(params); +// +// return new Result>().ok(page); +// } + + @GetMapping("{id}/{eqTypeId}") + @Operation(summary = "设备详情") +// @RequiresPermissions("eq:iotthings:info") + public Result get(@PathVariable("id") Long id, @PathVariable("eqTypeId") Long eqTypeId){ + EqDTO data = iotThingsService.getInfo(id); + IotThingEntity eq = iotThingEntityService.getById(eqTypeId); +// IotThingsDTO eq = iotThingsService.get(eqTypeId); + data.setEqTypeName(eq.getName()); + data.setEqTypeId(eq.getId()); + return new Result().ok(data); + } + + @GetMapping("{eqCode}") + @Operation(summary = "设备详情(小程序扫码用)") +// @RequiresPermissions("eq:iotthings:info") + public Result getEqInfo(@PathVariable("eqCode") String eqCode){ + Long thingId = iotThingBaseInfoService.getByEqCode(eqCode); + EqDTO data = iotThingsService.getInfo(thingId); +// IotThingsDTO eq = iotThingsService.get(eqTypeId); +// data.setEqTypeName(eq.getName()); +// data.setEqTypeId(eq.getId()); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存设备") + @LogOperation("保存设备") +// @RequiresPermissions("equipment:equipmentStatus:add") + public Result saveEq(@RequestBody EqDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotThingsService.saveEq(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改设备") + @LogOperation("修改设备") +// @RequiresPermissions("equipment:equipmentStatus:update") + public Result update(@RequestBody EqDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotThingsService.updateEq(dto); + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除设备") + @LogOperation("删除设备") +// @RequiresPermissions("equipment:equipmentStatus:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotThingsService.deleteEqs(ids); + return new Result(); + } + + @PostMapping("importExcel/{rootId}/{relationTopId}/{eqTypeId}") + @Operation(summary = "从excel导入设备") + @LogOperation("从excel导入设备") +// @RequiresPermissions("equipment:equipmentStatus:upload") + public Result inputEq(@RequestParam(value ="File") MultipartFile file,@PathVariable("rootId") String rootId,@PathVariable("relationTopId") String relationTopId,@PathVariable("eqTypeId") String eqTypeId){ + String successFlag = iotThingsService.importExcel(file,rootId,relationTopId,eqTypeId); + return new Result().ok(successFlag); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqAttacmentDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqAttacmentDTO.java new file mode 100644 index 0000000..bf93089 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqAttacmentDTO.java @@ -0,0 +1,40 @@ +package com.thing.eq.eqmanager.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 附件 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-29 +*/ +@Data +@Schema(description = "附件") +public class EqAttacmentDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "设备id/维修id/保养id...各种外键id") + private Long thingId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "图片或文件url") + private String url; + @Schema(description = "0图片,1附件") + private String type; + @Schema(description = "模块:0设备管理 1能源管理") + private String modularType; + @Schema(description = "名称") + private String name; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqDTO.java new file mode 100644 index 0000000..f7d0261 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqDTO.java @@ -0,0 +1,110 @@ +package com.thing.eq.eqmanager.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 物管理 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备管理") +public class EqDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; +// @Schema(description = "thingsboard entity_id 设备client_id") +// private String entityId; + @Schema(description = "设备物id") + private Long thingId; + @Schema(description = "物编号") + private String code; + @Schema(description = "设备名称") + private String name; + @Schema(description = "设备类型(equipment表示设备,equipment_part表示部件)") + private String type; + @Schema(description = "企业id") + private String deptId; + @Schema(description = "租户code") + private Long tenantCode; + private Long creator; + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long createDate; + private Long updater; + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long updateDate; + @Schema(description = "0虚拟 1真实") + private String deviceType; + @Schema(description = "设备类型id(即父级的thingId)") + private Long eqTypeId; + @Schema(description = "设备类型名称(即父级的thingName)") + private String eqTypeName; + @Schema(description = "结构id(关系根主键)") + private Long relationTypeId; + @Schema(description = "关系顶级物id") + private Long relationTopId; + + @Schema(description = "设备规格") + private String standard; + @Schema(description = "设备图片url") + private String imageUrl; + @Schema(description = "负责人") + private String director; + @Schema(description = "设备位置") + private String eqPosition; + @Schema(description = "购置时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date buyDate; + @Schema(description = "设备状态") + private String eqStatus; + @Schema(description = "生产厂商") + private String manufacturer; + @Schema(description = "供应商") + private String supplier; + @Schema(description = "参考价") + private String price; + @Schema(description = "库存") + private String stock; + @Schema(description = "使用状态") + private String useStatus; + @Schema(description = "使用部门Id") + private Long useDeptId; + @Schema(description = "操作人id") + private String operator; + @Schema(description = "是否报废 0未报废 1已报废") + private String scrap; + @Schema(description = "报废是否处理 0未处理 1已处理") + private String scrapDisposal; + @Schema(description = "出厂编号") + private String exFactoryNo; + @Schema(description = "出厂日期") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date exFactoryDate; + @Schema(description = "设备详细参数") + private String params; + @Schema(description = "计量单位") + private String unit; + @Schema(description = "备注") + private String tip; + @Schema(description = "设备编号") + private String eqCode; + + @Schema(description = "图片路径") + List imageUrls; + + @Schema(description = "附件路径") + List attachmentUrls; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqInfoDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqInfoDTO.java new file mode 100644 index 0000000..a02dc1e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqInfoDTO.java @@ -0,0 +1,42 @@ +package com.thing.eq.eqmanager.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 设备信息 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备信息") +public class EqInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "设备Id") + private Long eqId; + @Schema(description = "物编号") + private String code; + @Schema(description = "设备名称") + private String name; + @Schema(description = "设备规格") + private String standard; + @Schema(description = "设备图片url") + private String imageUrl; + @Schema(description = "设备类型名称") + private String eqTypeName; + @Schema(description = "使用部门id") + private Long useDeptId; + @Schema(description = "使用部门名称") + private String useDeptName; + @Schema(description = "负责人") + private String director; + @Schema(description = "设备位置") + private String eqPosition; + @Schema(description = "设备编号") + private String eqCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqScrapDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqScrapDTO.java new file mode 100644 index 0000000..929ec9e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqScrapDTO.java @@ -0,0 +1,45 @@ +package com.thing.eq.eqmanager.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 设备报废 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-02 +*/ +@Data +@Schema(description = "设备报废") +public class EqScrapDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "设备id") + private Long eqId; + @Schema(description = "报废人id") + private Long scrapUserId; + @Schema(description = "报废人名称") + private String scrapUserName; + @Schema(description = "审核人id") + private Long examineUserId; + @Schema(description = "审核人名称") + private String examineUserName; + @Schema(description = "报废是否处理 0未处理 1已处理") + private String scrapDisposal; + @Schema(description = "设备信息") + private EqInfoDTO eqInfo; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqTypeNameAndIdDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqTypeNameAndIdDTO.java new file mode 100644 index 0000000..d14ba64 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/EqTypeNameAndIdDTO.java @@ -0,0 +1,22 @@ +package com.thing.eq.eqmanager.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; + +/** +* 物管理 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备类型id和名称") +public class EqTypeNameAndIdDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + private String name; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingBaseInfoDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingBaseInfoDTO.java new file mode 100644 index 0000000..03d0da4 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingBaseInfoDTO.java @@ -0,0 +1,76 @@ +package com.thing.eq.eqmanager.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 设备基础信息 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "设备基础信息") +public class IotThingBaseInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "实体id") + private Long thingId; + @Schema(description = "设备规格") + private String standard; + @Schema(description = "设备图片url") + private String imageUrl; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "负责人") + private String director; + @Schema(description = "设备位置") + private String eqPosition; + @Schema(description = "购置时间") + private Date buyDate; + @Schema(description = "设备状态") + private String eqStatus; + @Schema(description = "生产厂商") + private String manufacturer; + @Schema(description = "供应商") + private String supplier; + @Schema(description = "参考价") + private String price; + @Schema(description = "库存") + private String stock; + @Schema(description = "使用状态") + private String useStatus; + @Schema(description = "使用部门Id") + private Long useDeptId; + @Schema(description = "操作人id") + private String operator; + @Schema(description = "是否报废 0未报废 1已报废") + private String scrap; + @Schema(description = "报废是否处理 0未处理 1已处理") + private String scrapDisposal; + @Schema(description = "出厂编号") + private String exFactoryNo; + @Schema(description = "出厂日期") + private Date exFactoryDate; + @Schema(description = "设备详细参数") + private String params; + @Schema(description = "计量单位") + private String unit; + @Schema(description = "备注") + private String tip; + @Schema(description = "设备编号") + private String eqCode; + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingsDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingsDTO.java new file mode 100644 index 0000000..8f933b8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/IotThingsDTO.java @@ -0,0 +1,63 @@ +//package iot.thing.eq.eqmanager.dto; +// +//import io.swagger.annotations.ApiModel; +//import io.swagger.annotations.ApiModelProperty; +//import lombok.Data; +// +//import java.io.Serializable; +//import java.util.Date; +// +///** +//* 物管理 +//* +//* @author xiezw 806671840@qq.com +//* @since 3.0 2021-09-22 +//*/ +//@Data +//@ApiModel(value = "物管理") +//public class IotThingsDTO implements Serializable { +// private static final long serialVersionUID = 1L; +// +// private Long id; +// @Schema(description = "thingsboard entity_id 设备client_id") +// private String entityId; +// @Schema(description = "物编号") +// private String code; +// @Schema(description = "物名称") +// private String name; +// @Schema(description = "物类型") +// private String type; +// @Schema(description = "物标签") +// private String label; +// @Schema(description = "物状态") +// private String status; +// @Schema(description = "物模型id") +// private String modelId; +// @Schema(description = "其他字段") +// private String otherJson; +// @Schema(description = "附加信息") +// private String additionalInfo; +// @Schema(description = "企业id") +// private Long deptId; +// @Schema(description = "租户code") +// private Long tenantCode; +// private Long creator; +// private Date createDate; +// private Long updater; +// private Date updateDate; +// @Schema(description = "能源类型的json") +// private String powerTypeJson; +// private String address; +// @Schema(description = "频率") +// private Long rate; +// @Schema(description = "原tb的entityId") +// private String oldEntityId; +// @Schema(description = "硬件错误码") +// private String errorCode; +// @Schema(description = "报错时间") +// private Date errorTime; +// @Schema(description = "虚拟设备还是实际设备 0或者null 为实际设备 1为虚拟设备") +// private String deviceType; +// +//// private List list; +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationDTO.java new file mode 100644 index 0000000..5bcb9af --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationDTO.java @@ -0,0 +1,42 @@ +//package iot.thing.eq.eqmanager.dto; +// +//import io.swagger.annotations.ApiModel; +//import io.swagger.annotations.ApiModelProperty; +//import lombok.Data; +// +//import java.io.Serializable; +// +// +///** +// * ${comments} +// * +// * @author chenyang chenyang@gmail.com +// * @since 1.0.0 2020-09-04 +// */ +//@Data +//@ApiModel(value = "物关系表") +//public class ThingsRelationDTO implements Serializable { +// private static final long serialVersionUID = 1L; +// +// @Schema(description = "从 ID") +// private String fromId; +// +// @Schema(description = "至 ID") +// private String toId; +// +// @Schema(description = "关联类id") +// private String relationTypeId; +// +// @Schema(description = "附加信息") +// private String additionalInfo; +// +// private String otherJson; +// +// private String lineJson; +// +// private String pointJson; +// @Schema(description = "排序") +// private Integer sort; +// +// +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationReqDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationReqDTO.java new file mode 100644 index 0000000..4fd5584 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/dto/ThingsRelationReqDTO.java @@ -0,0 +1,45 @@ +//package iot.thing.eq.eqmanager.dto; +// +//import io.swagger.annotations.ApiModelProperty; +//import lombok.Data; +// +//import javax.validation.constraints.NotBlank; +//import java.io.Serializable; +// +//@Data +//public class ThingsRelationReqDTO implements Serializable { +// @Schema(description = "从 ID") +// @NotBlank(message = "父ID不为空") +// private String fromId; +// +// @Schema(description = "至 ID") +// @NotBlank(message = "子ID不为空") +// private String toId; +// +// @Schema(description = "关联类id") +// @NotBlank(message = "结构类型ID不能为空") +// private String relationTypeId; +// +// @Schema(description = "附加信息") +// private String thingsAdditionalInfo; +// +// @Schema(description = "其它字段") +// private String otherJson; +// +// @Schema(description = "工序名称") +// private String thingsName; +// +// @Schema(description = "能源类型的json字符串,{\"A\":\"电\",\"B\":\"水\"}") +// private String powerTypeJson; +// +// +// @Schema(description = "线json") +// private String lineJson; +// +// @Schema(description = "点json") +// private String pointJson; +// +// private String cellId; +// +// +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqAttacmentEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqAttacmentEntity.java new file mode 100644 index 0000000..c51895e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqAttacmentEntity.java @@ -0,0 +1,61 @@ +package com.thing.eq.eqmanager.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 附件 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-29 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_attacment") +public class EqAttacmentEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 设备id/维修id/保养id...各种外键id + */ + private Long thingId; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 图片或文件url + */ + private String url; + /** + * 0图片,1附件 + */ + private String type; + /** + * 模块:0设备管理 1能源管理 + */ + private String modularType; + + /** + * 名称 + */ + private String name; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqScrapEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqScrapEntity.java new file mode 100644 index 0000000..84eadce --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/EqScrapEntity.java @@ -0,0 +1,67 @@ +package com.thing.eq.eqmanager.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备报废 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-02 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_scrap") +public class EqScrapEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 设备id + */ + private Long eqId; + /** + * 报废人id + */ + private Long scrapUserId; + /** + * 审核人id + */ + private Long examineUserId; + /** + * 报废是否处理 0未处理 1已处理 + */ + private String scrapDisposal; + /** + * 部门id + */ + private Long deptId; + /** + * 租户编码 + */ + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingBaseInfoEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingBaseInfoEntity.java new file mode 100644 index 0000000..74f474e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingBaseInfoEntity.java @@ -0,0 +1,149 @@ +package com.thing.eq.eqmanager.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 设备基础信息 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_base_info") +public class IotThingBaseInfoEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 实体id + */ + private Long thingId; + /** + * 设备规格 + */ + private String standard; + /** + * 设备图片url + */ + private String imageUrl; + /** + * 设备类型 + */ + private String eqType; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 负责人 + */ + private String director; + /** + * 设备位置 + */ + private String eqPosition; + /** + * 购置时间 + */ + private Date buyDate; + /** + * 设备状态 + */ + private String eqStatus; + /** + * 生产厂商 + */ + private String manufacturer; + /** + * 供应商 + */ + private String supplier; + /** + * 参考价 + */ + private String price; + /** + * 库存 + */ + private String stock; + + /** + * 设备名称 + */ + private String deviceName; + + /** + * 设备编号 + */ + private String deviceCode; + + /** + * 部门名称 + */ + private String deptName; + /** + * 使用状态 + */ + private String useStatus; + /** + * 使用部门Id + */ + private Long useDeptId; + /** + * 操作人id + */ + private String operator; + /** + * 是否报废 0未报废 1已报废 + */ + private String scrap; + /** + * 报废是否处理 0未处理 1已处理 + */ + private String scrapDisposal; + /** + * 出厂编号 + */ + private String exFactoryNo; + /** + * 出厂日期 + */ + private Date exFactoryDate; + /** + * 设备详细参数 + */ + private String params; + /** + * 计量单位 + */ + private String unit; + /** + * 备注 + */ + private String tip; + + /** + * 设备编号 + */ + private String eqCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingsEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingsEntity.java new file mode 100644 index 0000000..fcb926f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/IotThingsEntity.java @@ -0,0 +1,105 @@ +//package iot.thing.eq.eqmanager.entity; +// +//import com.baomidou.mybatisplus.annotation.FieldFill; +//import com.baomidou.mybatisplus.annotation.TableField; +//import com.baomidou.mybatisplus.annotation.TableId; +//import com.baomidou.mybatisplus.annotation.TableName; +//import lombok.Data; +//import lombok.EqualsAndHashCode; +// +//import java.util.Date; +// +///** +// * 物管理 +// * +// * @author xiezw 806671840@qq.com +// * @since 3.0 2021-09-22 +// */ +//@Data +//@EqualsAndHashCode(callSuper=false) +//@Table("iot_things") +//public class IotThingsEntity { +// private static final long serialVersionUID = 1L; +// +// @TableId +// private Long id; +// /** +// * thingsboard entity_id 设备client_id +// */ +// private String entityId; +// /** +// * 物编号 +// */ +// private String code; +// /** +// * 物名称 +// */ +// private String name; +// /** +// * 物类型 +// */ +// private String type; +// /** +// * 物标签 +// */ +// private String label; +// /** +// * 物状态 +// */ +// private String status; +// /** +// * 物模型id +// */ +// private String modelId; +// /** +// * 其他字段 +// */ +// private String otherJson; +// /** +// * 附加信息 +// */ +// private String additionalInfo; +// /** +// * 企业id +// */ +// +// private Long deptId; +// /** +// * 租户code +// */ +// +// private Long tenantCode; +// +// private Long creator; +// +// private Date createDate; +// +// private Long updater; +// +// private Date updateDate; +// /** +// * 能源类型的json +// */ +// private String powerTypeJson; +// private String address; +// /** +// * 频率 +// */ +// private Long rate; +// /** +// * 原tb的entityId +// */ +// private String oldEntityId; +// /** +// * 硬件错误码 +// */ +// private String errorCode; +// /** +// * 报错时间 +// */ +// private Date errorTime; +// /** +// * 虚拟设备还是实际设备 0或者null 为实际设备 1为虚拟设备 +// */ +// private String deviceType; +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/ThingsRelationEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/ThingsRelationEntity.java new file mode 100644 index 0000000..f154144 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/entity/ThingsRelationEntity.java @@ -0,0 +1,61 @@ +//package iot.thing.eq.eqmanager.entity; +// +//import com.baomidou.mybatisplus.annotation.TableName; +//import lombok.Data; +//import lombok.EqualsAndHashCode; +// +//import java.io.Serializable; +// +///** +// * 物关系表 +// * +// * @author chenyang chenyang@gmail.com +// * @since 1.0.0 2020-09-04 +// */ +//@Data +//@EqualsAndHashCode(callSuper=false) +//@Table("iot_things_relation") +//public class ThingsRelationEntity implements Serializable { +// +// private static final long serialVersionUID = 5517820693395962879L; +// /** +// * 从 ID +// */ +// private String fromId; +// /** +// * 至 ID +// */ +// private String toId; +// /** +// * 关联类id +// */ +// private String relationTypeId; +// /** +// * 附加信息 +// */ +// private String additionalInfo; +// +// /** +// * 排序字段 +// */ +// private Integer sort; +// /** +// * 其它字段 +// */ +// private String otherJson; +// +// /** +// * 线的json +// */ +// private String lineJson; +// +// /** +// * 点的json +// */ +// private String pointJson; +// +// /** +// * toId对应的前端组件id +// */ +// private String cellId; +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqAttacmentExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqAttacmentExcel.java new file mode 100644 index 0000000..cdf396e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqAttacmentExcel.java @@ -0,0 +1,40 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 附件 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-29 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqAttacmentExcel { + @Excel(name = "Long", orderNum = "1") + private Long id; + @Excel(name = "设备id/维修id/保养id...各种外键id", orderNum = "2") + private Long thingId; + @Excel(name = "创建者", orderNum = "3") + private Long creator; + @Excel(name = "创建时间", orderNum = "4") + private Date createDate; + @Excel(name = "更新者", orderNum = "5") + private Long updater; + @Excel(name = "更新时间", orderNum = "6") + private Date updateDate; + @Excel(name = "图片或文件url", orderNum = "7") + private String url; + @Excel(name = "0图片,1附件", orderNum = "8") + private String type; + @Excel(name = "模块:0设备管理 1能源管理", orderNum = "9") + private String modularType; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqInportExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqInportExcel.java new file mode 100644 index 0000000..3ae2bce --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqInportExcel.java @@ -0,0 +1,47 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import java.util.Date; + +/** + * 设备基础信息 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqInportExcel { + @Excel(name = "*物编号", orderNum = "1") + @NotBlank(message = "[物编号]不能为空") + private String code; + @Excel(name = "*设备编号", orderNum = "2") + @NotBlank(message = "[设备编号]不能为空") + private String eqCode; + @Excel(name = "*设备名称", orderNum = "3") + @NotBlank(message = "[设备名称]不能为空") + private String name; + @Excel(name = "*设备规格", orderNum = "4") + @NotBlank(message = "[设备规格]不能为空") + private String standard; + @Excel(name = "生产厂商", orderNum = "5") + private String manufacturer; + @Excel(name = "购买时间", orderNum = "6") + private Date buyDate; + @Excel(name = "设备位置", orderNum = "7") + private String eqPosition; + @Excel(name = "出厂编号", orderNum = "8") + private String exFactoryNo; + @Excel(name = "出厂日期", orderNum = "9") + private Date exFactoryDate; + @Excel(name = "设备详细参数", orderNum = "10") + private String params; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqScrapExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqScrapExcel.java new file mode 100644 index 0000000..7196108 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/EqScrapExcel.java @@ -0,0 +1,49 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +/** + * 设备报废 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-02 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqScrapExcel { + @ExcelIgnore() + private Long id; + @Excel(name = "设备编号", orderNum= "1") + private String eqCode; + @Excel(name = "设备名称", orderNum= "2") + private String name; + @Excel(name = "设备规格", orderNum= "3") + private String standard; + @Excel(name = "设备类型名称", orderNum= "4") + private String eqTypeName; + @ExcelIgnore() + private Long useDeptId; + @Excel(name = "使用部门名称", orderNum= "5") + private String useDeptName; + @Excel(name = "负责人", orderNum= "6") + private String director; + @Excel(name = "设备位置", orderNum= "7") + private String eqPosition; + @Excel(name = "报废状态", orderNum= "8") + private String scrapDisposal; + @ExcelIgnore() + private Long scrapUserId; + @Excel(name = "提交报废人", orderNum= "9") + private String scrapUserName; + @ExcelIgnore() + private Long examineUserId; + @Excel(name = "审核人", orderNum= "10") + private String examineUserName; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingBaseInfoExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingBaseInfoExcel.java new file mode 100644 index 0000000..24942ee --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingBaseInfoExcel.java @@ -0,0 +1,56 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 设备基础信息 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class IotThingBaseInfoExcel { + @Excel(name = "Long", orderNum = "0") + private Long id; + @Excel(name = "实体id", orderNum = "1") + private Long thingId; + @Excel(name = "设备规格", orderNum = "2") + private String standard; + @Excel(name = "设备图片url", orderNum = "3") + private String imageUrl; + @Excel(name = "设备类型", orderNum = "4") + private String eqType; + @Excel(name = "创建者", orderNum = "5") + private Long creator; + @Excel(name = "创建时间", orderNum = "6") + private Date createDate; + @Excel(name = "更新者", orderNum = "7") + private Long updater; + @Excel(name = "更新时间", orderNum = "8") + private Date updateDate; + @Excel(name = "负责人", orderNum = "9") + private String director; + @Excel(name = "设备位置", orderNum = "10") + private String eqPosition; + @Excel(name = "购置时间", orderNum = "11") + private Date buyDate; + @Excel(name = "设备状态", orderNum = "12") + private String eqStatus; + @Excel(name = "生产厂商", orderNum = "13") + private String manufacturer; + @Excel(name = "供应商", orderNum = "14") + private String supplier; + @Excel(name = "参考价", orderNum = "15") + private String price; + @Excel(name = "库存", orderNum = "16") + private String stock; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsExcel.java new file mode 100644 index 0000000..ea529b2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsExcel.java @@ -0,0 +1,44 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 物管理 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class IotThingsExcel { + @ExcelIgnore() + private Long id; + @Excel(name = "设备编号", orderNum = "1") + private String eqCode; + @Excel(name = "设备名称", orderNum = "2") + private String name; + @Excel(name = "设备规格", orderNum = "3") + private String standard; + @Excel(name = "设备类型", orderNum = "4") + private String eqTypeName; + @Excel(name = "所属部门", orderNum = "5") + private String useDeptName; + @Excel(name = "设备位置", orderNum = "6") + private String eqPosition; + @Excel(name = "购置时间", orderNum = "7") + private Date buyDate; + @Excel(name = "使用状态", orderNum = "8") + private String useStatus; + @ExcelIgnore() + private Long useDeptId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsPartExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsPartExcel.java new file mode 100644 index 0000000..e6e374c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/excel/IotThingsPartExcel.java @@ -0,0 +1,34 @@ +package com.thing.eq.eqmanager.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelIgnore; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +/** + * 物管理 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class IotThingsPartExcel { + @ExcelIgnore() + private Long id; + @Excel(name = "备件编号", orderNum = "1") + private String code; + @Excel(name = "备件名称", orderNum = "2") + private String name; + @Excel(name = "型号规格", orderNum = "3") + private String standard; + @Excel(name = "库存", orderNum = "4") + private String stock; + @Excel(name = "备件编号", orderNum = "5") + private String eqTypeName; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqAttacmentMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqAttacmentMapper.java new file mode 100644 index 0000000..583de19 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqAttacmentMapper.java @@ -0,0 +1,25 @@ +package com.thing.eq.eqmanager.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 附件 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-29 +*/ +@Mapper +public interface EqAttacmentMapper extends PowerBaseMapper { + + void deleteBatchByThingId(@Param("thingId") Long thingId); + + List getByThingId(@Param("thingId") Long thingId); + + void deleteBatchByThingIds(@Param("thingIds") Long[] thingIds); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqScrapMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqScrapMapper.java new file mode 100644 index 0000000..07ebf49 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqScrapMapper.java @@ -0,0 +1,29 @@ +package com.thing.eq.eqmanager.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqmanager.dto.EqScrapDTO; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备报废 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-02 +*/ +@Mapper +public interface EqScrapMapper extends PowerBaseMapper { + + List getList(Map params); + + int findScrapCountByEqId(@Param("eqId") Long eqId); + + List getListData(Map params); + + void deleteByEqId(@Param("eqId") Long eqId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqThingsRelationMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqThingsRelationMapper.java new file mode 100644 index 0000000..756a6db --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/EqThingsRelationMapper.java @@ -0,0 +1,27 @@ +//package iot.thing.eq.eqmanager.dao; +// +// +// +//import iot.thing.eq.eqmanager.dto.EqTypeNameAndIdDTO; +//import org.apache.ibatis.annotations.Mapper; +//import org.apache.ibatis.annotations.Param; +// +///** +// * 物关系 +// * +// * @author chenyang chenyang@gmail.com +// * @since 1.0.0 2020-09-04 +// */ +//@Mapper +//public interface EqThingsRelationDao { +// +// void deleteByToId(@Param("toIds") Long[] toIds); +// +// String getNameByToId(@Param("toId") String toId); +// +// EqTypeNameAndIdDTO getIdAndName(@Param("toId") String toId); +// +// String getRelationTypeId(@Param("toId") String toId); +// +// void deleteByToIdAndRelationTypeId(@Param("toId") String toId, @Param("relationTypeId") String relationTypeId); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingBaseInfoMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingBaseInfoMapper.java new file mode 100644 index 0000000..60d601b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingBaseInfoMapper.java @@ -0,0 +1,51 @@ +package com.thing.eq.eqmanager.mapper; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.IotThingBaseInfoEntity; +import com.thing.eq.eqmanager.excel.IotThingsExcel; +import com.thing.eq.eqmanager.excel.IotThingsPartExcel; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 设备基础信息 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface IotThingBaseInfoMapper extends PowerBaseMapper { + + IotThingBaseInfoDTO getByThingsId(@Param("thingsId") Long thingsId); + + /** + * 扣库存悲观锁(非库存扣减场景慎用) + */ + IotThingBaseInfoDTO getByThingsIdForUpdate(@Param("thingsId") Long thingsId); + + void deleteByThingsId(@Param("thingIds") Long[] thingIds); + + List queryDeviceListByThingsId(Page page, @Param("params") Map params, @Param("useDeptId") Long useDeptId, @Param("relationTypeId") Long relationTypeId, @Param("toId") Long toId, @Param("eqTypeName") String eqTypeName, @Param("keyWord") String keyWord); + + void updateScrap(@Param("eqId") Long eqId, @Param("scrap") String scrap, @Param("scrapDisposal") String scrapDisposal); + + List exportPartListByThingsId(@Param("params") Map params); + + String getStock(@Param("eqId") Long eqId); + + int getCodeCount(@Param("eqCode") String eqCode); + + int getCodeCountNoId(@Param("eqCode") String eqCode, @Param("id") Long id); + + Long getByEqCode(@Param("eqCode") String eqCode); + + List exportDeviceListByThingsId(@Param("relationTypeId") Long relationTypeId, @Param("toId") Long toId, @Param("ids") Long[] ids); + + List exportPartListByThingsId(@Param("relationTypeId") Long relationTypeId, @Param("toId") Long toId, @Param("ids") Long[] ids); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingsDao.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingsDao.java new file mode 100644 index 0000000..d41444a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/mapper/IotThingsDao.java @@ -0,0 +1,30 @@ +//package iot.thing.eq.eqmanager.dao; +// +// +//import iot.thing.eq.eqby.dto.EqInfo; +//import iot.thing.eq.eqmanager.dto.EqDTO; +//import iot.thing.eq.eqmanager.dto.EqInfoDTO; +//import org.apache.ibatis.annotations.Mapper; +//import org.apache.ibatis.annotations.Param; +// +///** +//* 物管理 +//* +//* @author xiezw 806671840@qq.com +//* @since 3.0 2021-09-22 +//*/ +//@Mapper +//public interface IotThingsDao{ +// +// EqInfo getThingInfo(@Param("thingId") Long thingId, @Param("relationTypeId") String relationTypeId); +// +// EqInfoDTO getEqInfo(@Param("eqId") Long eqId, @Param("relationTypeId") String relationTypeId); +// +// int getCodeCount(@Param("code") String code); +// +// int getCodeCountNoId(@Param("code") String code, @Param("id") Long id); +// +// EqInfo getThingInfoNew(@Param("eqPartId") Long eqPartId); +// +// EqDTO getThingsByCode(@Param("code") String code); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/pojo/IotThingsPojo.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/pojo/IotThingsPojo.java new file mode 100644 index 0000000..ad171a0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/pojo/IotThingsPojo.java @@ -0,0 +1,53 @@ +package com.thing.eq.eqmanager.pojo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class IotThingsPojo implements Serializable { + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "thingsboard entity_id 设备client_id") + private String entityId; + @Schema(description = "物编号") + private String code; + @Schema(description = "物名称") + private String name; + @Schema(description = "物类型") + private String type; + @Schema(description = "物标签") + private String label; + @Schema(description = "物状态") + private String status; + @Schema(description = "物模型id") + private String modelId; + @Schema(description = "其他字段") + private String otherJson; + @Schema(description = "附加信息") + private String additionalInfo; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + private Long creator; + private Date createDate; + private Long updater; + private Date updateDate; + @Schema(description = "能源类型的json") + private String powerTypeJson; + private String address; + @Schema(description = "频率") + private Long rate; + @Schema(description = "原tb的entityId") + private String oldEntityId; + @Schema(description = "硬件错误码") + private String errorCode; + @Schema(description = "报错时间") + private Date errorTime; + @Schema(description = "虚拟设备还是实际设备 0或者null 为实际设备 1为虚拟设备") + private String deviceType; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqAttacmentService.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqAttacmentService.java new file mode 100644 index 0000000..71166da --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqAttacmentService.java @@ -0,0 +1,23 @@ +package com.thing.eq.eqmanager.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; + +import java.util.List; + +/** + * 附件 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-29 + */ +public interface EqAttacmentService extends IBaseService { + + void deleteBatchByThingId(Long id); + + List getByThingId(Long id); + + void deleteBatchByThingIds(Long[] ids); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqScrapService.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqScrapService.java new file mode 100644 index 0000000..f5fdc22 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/EqScrapService.java @@ -0,0 +1,36 @@ +package com.thing.eq.eqmanager.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqmanager.dto.EqScrapDTO; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import com.thing.eq.eqmanager.excel.EqScrapExcel; + +import java.util.List; +import java.util.Map; + +/** + * 设备报废 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-02 + */ +public interface EqScrapService extends IBaseService { + + void saveScrap(Long eqId, String scrap); + + void saveScrapDisposal(Long id); + + List getExportlist(Map params); + + Map checkScrap(Long eqId); + + EqScrapEntity getByEqId(Long eqId); + + void checkThingStatus(Long eqId); + + PageData page(Map params); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingBaseInfoService.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingBaseInfoService.java new file mode 100644 index 0000000..517f981 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingBaseInfoService.java @@ -0,0 +1,49 @@ +package com.thing.eq.eqmanager.service; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.ThingDTO; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.IotThingBaseInfoEntity; +import com.thing.eq.eqmanager.excel.IotThingsExcel; +import com.thing.eq.eqmanager.excel.IotThingsPartExcel; + +import java.util.List; +import java.util.Map; + +/** + * 设备基础信息 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface IotThingBaseInfoService extends IBaseService { + + IotThingBaseInfoDTO getByThingsId(Long id); + + IotThingBaseInfoDTO getByThingsIdForUpdate(Long thingsId); + + void deleteByThingsId(Long[] ids); + + Page queryDeviceListByThingsId(Map params); + + ThingDTO getThingInfo(ThingDTO thingDTO, Long thingId, List settingList, String showDeviceType, String relationTypeId); + + String getDeviceName(Long relationTypeId, Long thingId); + + List listExport(Map params, Long[] ids); + + void updateScrap(Long eqId, String scrap, String scrapDisposal); + + List listExportPart(Map params, Long[] ids); + + String getStock(String eqPartId); + + int getCodeCount(String eqCode); + + int getCodeCountNoId(String eqCode, Long id); + + Long getByEqCode(String eqCode); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingsService.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingsService.java new file mode 100644 index 0000000..ef215fc --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/IotThingsService.java @@ -0,0 +1,35 @@ +package com.thing.eq.eqmanager.service; + + + +import com.thing.eq.eqby.dto.EqInfo; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.EqInfoDTO; +import org.springframework.web.multipart.MultipartFile; + +/** + * 物管理 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface IotThingsService { + + void saveEq(EqDTO dto); + + void updateEq(EqDTO dto); + + void deleteEqs(Long[] ids); + + EqDTO getInfo(Long id); + + EqInfo getThingInfo(Long thingId, String relationTypeId); + + EqInfoDTO getEqInfo(Long eqId, String relationTypeId); + + String importExcel(MultipartFile file, String rootId,String relationTopId,String eqTypeId); + + EqInfo getThingInfoNew(Long eqPartId); + + EqDTO getThingsByCode(String code); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/ThingsRelationService.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/ThingsRelationService.java new file mode 100644 index 0000000..d9df75b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/ThingsRelationService.java @@ -0,0 +1,29 @@ +//package iot.thing.eq.eqmanager.service; +// +// +//import iot.thing.common.service.CrudService; +//import iot.thing.eq.eqmanager.dto.EqTypeNameAndIdDTO; +//import iot.thing.eq.eqmanager.dto.ThingsRelationDTO; +//import iot.thing.eq.eqmanager.entity.ThingsRelationEntity; +// +// +///** +// * ${comments} +// * +// * @author chenyang chenyang@gmail.com +// * @since 1.0.0 2020-09-04 +// */ +//public interface ThingsRelationService { +// +// +// void deleteByToId(Long[] ids); +// +// String getThingName(String id); +// +// EqTypeNameAndIdDTO getIdAndName(String toString); +// +// String getRelationTypeId(String eqTypeId); +// Integer getSort(String fromId, String relationTypeId); +// +// void deleteByToIdAndRelationTypeId(String toString, String toString1); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqAttacmentServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqAttacmentServiceImpl.java new file mode 100644 index 0000000..34cf44f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqAttacmentServiceImpl.java @@ -0,0 +1,47 @@ +package com.thing.eq.eqmanager.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import com.thing.eq.eqmanager.mapper.EqAttacmentMapper; +import com.thing.eq.eqmanager.service.EqAttacmentService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 附件 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-29 + */ +@Service +public class EqAttacmentServiceImpl extends BaseServiceImpl implements EqAttacmentService { + + @Autowired + private EqAttacmentMapper eqAttacmentDao ; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void deleteBatchByThingId(Long thingId) { + eqAttacmentDao.deleteBatchByThingId(thingId); + } + + @Override + public List getByThingId(Long id) { + return eqAttacmentDao.getByThingId(id); + } + + @Override + public void deleteBatchByThingIds(Long[] ids) { + eqAttacmentDao.deleteBatchByThingIds(ids); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqScrapServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqScrapServiceImpl.java new file mode 100644 index 0000000..40b2bf1 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqScrapServiceImpl.java @@ -0,0 +1,245 @@ +package com.thing.eq.eqmanager.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqmanager.dto.EqInfoDTO; +import com.thing.eq.eqmanager.dto.EqScrapDTO; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import com.thing.eq.eqmanager.excel.EqScrapExcel; +import com.thing.eq.eqmanager.mapper.EqScrapMapper; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 设备报废 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-02 + */ +@Service +public class EqScrapServiceImpl extends BaseServiceImpl implements EqScrapService { + @Autowired + private EqByPlanService eqByPlanService; + @Autowired + private EqCheckSettingService eqCheckSettingService; + @Autowired + private EqBxService eqBxService; + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + @Autowired + private EqScrapMapper eqScrapDao; + @Autowired + private IotThingsService iotThingsService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysUserService sysUserService; + + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public PageData page(Map params) { + Page page = getPage(params); + if (Objects.isNull(params.get("relationTypeId"))) { + throw new SysException("结构类型id不能为空"); + } + String relationTypeId = params.get("relationTypeId").toString(); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqScrapDTOS = eqScrapDao.getList(params); + for (EqScrapDTO eqScrapDTO : eqScrapDTOS) { + if (eqScrapDTO != null) { + if (eqScrapDTO.getEqId() != null) { + EqInfoDTO thingsInfo = iotThingsService.getEqInfo(eqScrapDTO.getEqId(), relationTypeId); + if (thingsInfo != null && thingsInfo.getUseDeptId() != null) { + SysDeptEntity sysDeptEntity = sysDeptService.getById(thingsInfo.getUseDeptId()); + thingsInfo.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + eqScrapDTO.setEqInfo(thingsInfo); + } + } + //报废人 + if (eqScrapDTO.getScrapUserId() != null) { + SysUserEntity sysUserEntity = sysUserService.getById(eqScrapDTO.getScrapUserId()); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + eqScrapDTO.setScrapUserName(sysUserEntity.getRealName()); + } + } + //审核人 + if (eqScrapDTO.getExamineUserId() != null) { + SysUserEntity sysUserEntity = sysUserService.getById(eqScrapDTO.getExamineUserId()); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + eqScrapDTO.setExamineUserName(sysUserEntity.getRealName()); + } + } + } + return new PageData<>(eqScrapDTOS, page.getTotalRow()); + } + + @Override + public void saveScrap(Long eqId,String scrap) { + //取消报废 + if (StringUtils.equals("0",scrap)){ + //更新设备报废状态,设备未处理状态 + iotThingBaseInfoService.updateScrap(eqId,scrap,"0"); + //删除报废记录 + eqScrapDao.deleteByEqId(eqId); + }else{ + EqScrapDTO dto = new EqScrapDTO(); + dto.setEqId(eqId); + UserDetail user = SecurityUser.getUser(); + if (user != null){ + dto.setScrapUserId(user.getId()); + } + //报废未处理 + dto.setScrapDisposal("0"); + //更新设备报废状态,设备未处理状态 + iotThingBaseInfoService.updateScrap(eqId,scrap,"0"); + //保存记录 + super.saveDto(dto); + } + } + + @Override + public void saveScrapDisposal(Long id) { + EqScrapDTO data = getByIdAs(id,EqScrapDTO.class); + if (data == null) { + throw new SysException("报废记录不存在"); + } + UserDetail user = SecurityUser.getUser(); + if (user != null) { + data.setExamineUserId(user.getId()); + } + data.setScrapDisposal("1"); + //更新 + updateDto(data); + if (data.getEqId() != null) { + //更新设备报废状态,设备未处理状态 + iotThingBaseInfoService.updateScrap(data.getEqId(), "1", "1"); + } + } + + @Override + public List getExportlist(Map params) { + List excels = new ArrayList<>(); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqScrapDTOS = eqScrapDao.getListData(params); + String relationTypeId = null; + if (params.get("relationTypeId")!=null){ + relationTypeId = params.get("relationTypeId").toString(); + } + for (EqScrapDTO eqScrapDTO : eqScrapDTOS) { + EqScrapExcel excel = new EqScrapExcel(); + //报废人 + if (eqScrapDTO.getScrapUserId() != null) { + SysUserEntity sysUserEntity = sysUserService.getById(eqScrapDTO.getScrapUserId()); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + excel.setScrapUserName(sysUserEntity.getRealName()); + } + } + //审核人 + if (eqScrapDTO.getExamineUserId() != null) { + SysUserEntity sysUserEntity = sysUserService.getById(eqScrapDTO.getExamineUserId()); + if (sysUserEntity != null && StringUtils.isNotBlank(sysUserEntity.getRealName())) { + excel.setExamineUserName(sysUserEntity.getRealName()); + } + } + if (StringUtils.isNotBlank(eqScrapDTO.getScrapDisposal())) { + if (StringUtils.equals("0", eqScrapDTO.getScrapDisposal())) { + excel.setScrapDisposal("未审核"); + } else { + excel.setScrapDisposal("已审核"); + } + } + if (eqScrapDTO != null && eqScrapDTO.getEqId() != null) { + EqInfoDTO thingsInfo = iotThingsService.getEqInfo(eqScrapDTO.getEqId(), relationTypeId); + if (thingsInfo != null) { + BeanUtils.copyProperties(thingsInfo, excel); + if (thingsInfo.getUseDeptId() != null) { + SysDeptEntity sysDeptEntity = sysDeptService.getById(thingsInfo.getUseDeptId()); + excel.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + } + } + excel.setId(eqScrapDTO.getId()); + excels.add(excel); + } + return excels; + } + + @Override + public Map checkScrap(Long eqId) { + Map map = new HashMap<>(); + //判断设备是否已经报废 + int scrapCount = eqScrapDao.findScrapCountByEqId(eqId); + if (scrapCount > 0){ + map.put("1","该设备已经报废"); + } + //判断设备是否存在报修 + int bxCount = eqBxService.findBxCountByEqId(eqId); + if (bxCount > 0){ + map.put("2","该设备存在报修"); + } + //判断设备是否存在保养计划 + int byPlanCount = eqByPlanService.getByCountByEqId(eqId); + if (byPlanCount > 0){ + map.put("3","该设备存在保养计划"); + } + //判断设备是否存在巡检计划 + int patrolPlanCount = eqCheckSettingService.getPatrolPlanCountByEqId(eqId,"1"); + if (patrolPlanCount > 0){ + map.put("4","该设备存在巡检计划"); + } + //判断设备是否存在点检计划 + int spotPlanCount = eqCheckSettingService.getPatrolPlanCountByEqId(eqId,"0"); + if (spotPlanCount > 0){ + map.put("5","该设备存在点检计划"); + } + return map; + } + + @Override + public EqScrapEntity getByEqId(Long eqId) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("eq_id", eqId); + wrapper.eq("scrap_disposal", "1"); + + return mapper.selectOneExt(wrapper); + } + + @Override + public void checkThingStatus(Long eqId) { + //查询是否报废 + EqScrapEntity eqScrapEntity = getByEqId(eqId); + if(!Objects.isNull(eqScrapEntity)){ + throw new SysException("选择已报废设备,无法操作"); + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqThingsRelationServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqThingsRelationServiceImpl.java new file mode 100644 index 0000000..5ea6f3c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/EqThingsRelationServiceImpl.java @@ -0,0 +1,76 @@ +//package iot.thing.eq.eqmanager.service.impl; +// +//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +//import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +//import iot.thing.common.service.impl.CrudServiceImpl; +//import iot.thing.eq.eqmanager.dao.EqThingsRelationDao; +//import iot.thing.eq.eqmanager.dto.EqTypeNameAndIdDTO; +//import iot.thing.eq.eqmanager.dto.ThingsRelationDTO; +//import iot.thing.eq.eqmanager.entity.ThingsRelationEntity; +//import iot.thing.eq.eqmanager.service.ThingsRelationService; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Service; +// +//import java.util.List; +//import java.util.Map; +// +///** +// * ${comments} +// * +// * @author chenyang chenyang@gmail.com +// * @since 1.0.0 2020-09-04 +// */ +//@Service +//public class EqThingsRelationServiceImpl extends CrudServiceImpl implements ThingsRelationService { +// +// @Autowired +// private EqThingsRelationDao eqThingsRelationDao; +// +// @Override +// public QueryWrapper getWrapper(Map params){ +// +// QueryWrapper wrapper = new QueryWrapper<>(); +// wrapper.eq("to_id", params.get("toId")); +// wrapper.eq("relation_type_id", params.get("relationTypeId")); +// return wrapper; +// } +// +// +// @Override +// public void deleteByToId(Long[] toIds) { +// eqThingsRelationDao.deleteByToId(toIds); +// } +// +// @Override +// public String getThingName(String id) { +// return eqThingsRelationDao.getNameByToId(id); +// } +// +// @Override +// public EqTypeNameAndIdDTO getIdAndName(String id) { +// return eqThingsRelationDao.getIdAndName(id); +// } +// +// @Override +// public String getRelationTypeId(String eqTypeId) { +// return eqThingsRelationDao.getRelationTypeId(eqTypeId); +// } +// +// @Override +// public Integer getSort(String fromId, String relationTypeId) { +// +// QueryWrapper queryWrapper = new QueryWrapper(); +// queryWrapper.lambda().eq(ThingsRelationEntity::getFromId, fromId) +// .eq(ThingsRelationEntity::getRelationTypeId, relationTypeId) +// .orderByDesc(ThingsRelationEntity::getSort); +// List thingsRelationList = eqThingsRelationDao.selectList(queryWrapper); +// return CollectionUtils.isEmpty(thingsRelationList) ? +// new Integer(0) : new Integer(thingsRelationList.get(0).getSort()==null? 1: thingsRelationList.get(0).getSort()+ 1); +// +// } +// +// @Override +// public void deleteByToIdAndRelationTypeId(String toId, String relationTypeId) { +// eqThingsRelationDao.deleteByToIdAndRelationTypeId(toId,relationTypeId); +// } +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingBaseInfoServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingBaseInfoServiceImpl.java new file mode 100644 index 0000000..34f131c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingBaseInfoServiceImpl.java @@ -0,0 +1,348 @@ +package com.thing.eq.eqmanager.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.dto.EqCheckStandardDTO; +import com.thing.eq.eqcheck.dto.ThingDTO; +import com.thing.eq.eqcheck.service.EqCheckStandardService; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.EqScrapEntity; +import com.thing.eq.eqmanager.entity.IotThingBaseInfoEntity; +import com.thing.eq.eqmanager.excel.IotThingsExcel; +import com.thing.eq.eqmanager.excel.IotThingsPartExcel; +import com.thing.eq.eqmanager.mapper.IotThingBaseInfoMapper; +import com.thing.eq.eqmanager.service.EqScrapService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import org.apache.commons.lang3.StringUtils; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备基础信息 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class IotThingBaseInfoServiceImpl extends BaseServiceImpl implements IotThingBaseInfoService { + + @Autowired + private IotThingBaseInfoMapper iotThingBaseInfoDao; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + +// @Autowired +// private IotThingsServiceFeign iotThingsServiceFeign; + + @Autowired + private EqCheckStandardService eqCheckStandardService; + + @Autowired + private SysDeptService sysDeptService; + + @Autowired + private IotThingEntityService iotThingEntityService; + +// @Autowired +// private DictTypeServiceFeign dictTypeServiceFeign; + + @Autowired + private EqScrapService eqScrapService; + + @Autowired + private SysDictDataService sysDictDataService; + + @Autowired + private SysDictTypeService sysDictTypeService; + + +// @Autowired +// private IotDictTypeService iotDictTypeService; +// +// @Autowired +// private ThingsService thingsService; +// +// @Autowired +// private ThingsRelationService thingsRelationService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public IotThingBaseInfoDTO getByThingsId(Long thingsId) { + return iotThingBaseInfoDao.getByThingsId(thingsId); + } + + @Override + public IotThingBaseInfoDTO getByThingsIdForUpdate(Long thingsId) { + return iotThingBaseInfoDao.getByThingsIdForUpdate(thingsId); + } + + @Override + public void deleteByThingsId(Long[] thingIds) { + iotThingBaseInfoDao.deleteByThingsId(thingIds); + } + + @Override + public Page queryDeviceListByThingsId(Map params) { + + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("page、limit不能为空"); + } + int page = Integer.parseInt(String.valueOf(params.get("page"))); + int limit = Integer.parseInt(String.valueOf(params.get("limit"))); + + + String relationTypeId = params.get("relationTypeId").toString(); + Long aLong1 = new Long(relationTypeId); + Long aLong2=null; + if (ObjectUtil.isNotEmpty(params.get("toId"))){ + String toId = params.get("toId").toString(); + aLong2 = new Long(toId); + }; + + String eqTypeName = null; + if(!Objects.isNull(params.get("eqTypeName"))){ + eqTypeName = params.get("eqTypeName").toString(); + } + String keyWord = null; + if(!Objects.isNull(params.get("keyWord"))){ + keyWord = params.get("keyWord").toString(); + } + Long aLong = null; + if(!Objects.isNull(params.get("useDeptId"))){ + String useDeptId = params.get("useDeptId").toString(); + aLong = new Long(useDeptId); + } + List eqDTOS = iotThingBaseInfoDao.queryDeviceListByThingsId(new Page<>(page, limit), params,aLong,aLong1,aLong2,eqTypeName,keyWord); + Page eqDTOPage = new Page<>(); + eqDTOPage.setRecords(eqDTOS); + eqDTOPage.setTotalPage(eqDTOS.size()); + return eqDTOPage; + } + + @Override + public ThingDTO getThingInfo(ThingDTO thingDTO, Long thingId, List settingList, String showDeviceType, String relationTypeId) { + + IotThingBaseInfoDTO baseThing = iotThingBaseInfoService.getByThingsId(thingId); + + EqScrapEntity eqScrapEntity = eqScrapService.getByEqId(thingId); + if(!Objects.isNull(eqScrapEntity)){ + thingDTO.setThingStatus("报废"); + }else { + thingDTO.setThingStatus("正常"); + } + + //调整 + IotThingEntityDTO data1 = iotThingEntityService.getByIdAs(thingId,IotThingEntityDTO.class); + // 获取设备关联的物 + if (!Objects.isNull(data1)) { + thingDTO.setThingCode(Objects.isNull(data1.getCode()) ? "" : data1.getCode()); + thingDTO.setThingName(Objects.isNull(data1.getName()) ? "" : data1.getName()); + } + +// Result response = iotThingsServiceFeign.getThingById(thingId); +// if (Objects.isNull(response) || response.getCode() != 0) { +// throw new RenException(!Objects.isNull(response) ? response.getMsg() : "服务异常"); +// } +// +// Map responseData = (Map) response.getData(); +// if (!Objects.isNull(responseData)) { +// thingDTO.setThingCode(Objects.isNull(responseData.get("code")) ? "" : String.valueOf(responseData.get("code"))); +// thingDTO.setThingName(Objects.isNull(responseData.get("name")) ? "" : String.valueOf(responseData.get("name"))); +// } + thingDTO.setThingId(thingId); + if (!Objects.isNull(baseThing)) { + thingDTO.setThingStandard(baseThing.getStandard()); + thingDTO.setDevicePosition(baseThing.getEqPosition()); + thingDTO.setUseDeptId(baseThing.getUseDeptId()); + thingDTO.setEqCode(baseThing.getEqCode()); + thingDTO.setStock(baseThing.getStock()); + } + + if ("1".equals(showDeviceType) && StringUtils.isNotBlank(relationTypeId)) { + String deviceTypeName = getDeviceName(Long.parseLong(relationTypeId), thingId); + thingDTO.setDeviceType(deviceTypeName); + } + + if (CollectionUtil.isNotEmpty(settingList)) { + List standardIdList = settingList.stream().map(EqCheckSettingDTO::getStandardId).collect(Collectors.toList()); + thingDTO.setStandardIdList(standardIdList); + if (CollectionUtil.isNotEmpty(standardIdList)) { + StringBuilder stringBuilder = new StringBuilder(); + List standardList = eqCheckStandardService.getStandardListByStandardIdList(standardIdList); + standardList.forEach(standard -> { + stringBuilder.append(standard.getName() + ","); + }); + thingDTO.setStandardNameList(stringBuilder.length() > 0 ? stringBuilder.toString().substring(0, stringBuilder.length() - 1) : ""); + } + } + + return thingDTO; + } + + @Autowired + private IotThingRelationDetailService iotThingRelationDetailService; + + @Override + public String getDeviceName(Long relationTypeId, Long thingId) { + //调整 + IotThingRelationDetailEntity data = iotThingRelationDetailService.getThingsRelation(relationTypeId,thingId); + IotThingEntityDTO thingsDTO = new IotThingEntityDTO(); + if(!Objects.isNull(data) && ObjectUtil.isNotEmpty(data.getFromId())){ + thingsDTO = iotThingEntityService.getByIdAs(data.getFromId(),IotThingEntityDTO.class); + } +// Result response = iotThingsServiceFeign.getDeviceTypeThingByThingId(relationTypeId, thingId); +// if (Objects.isNull(response) || response.getCode() != 0) { +// return ""; +// } +// Map thingRelationData = (Map) response.getData(); +// if (Objects.isNull(thingRelationData)) { +// return ""; +// } + String deviceTypeName = Objects.isNull(thingsDTO.getName()) ? "" : String.valueOf(thingsDTO.getName()); + return deviceTypeName; + } + + @Override + public List listExport(Map params, Long[] ids) { + + String aLong1 = params.get("relationTypeId").toString(); + Long relationTypeId = new Long(aLong1); + Long toId=null; + if (ObjectUtil.isNotEmpty(params.get("toId"))){ + String aLong2 = params.get("toId").toString(); + toId = new Long(aLong2); + }; +// String group = null; +// if (ObjectUtil.isNotEmpty(params.get("group"))){ +// group = params.get("group").toString(); +// }; +// String type = null; +// if (ObjectUtil.isNotEmpty(params.get("type"))){ +// type = params.get("type").toString(); +// }; +// String tenantName = null; +// if (ObjectUtil.isNotEmpty(params.get("tenantName"))){ +// tenantName = params.get("tenantName").toString(); +// }; + + + List excels = iotThingBaseInfoDao.exportDeviceListByThingsId(relationTypeId,toId,ids); + + //调整 + String dictTypeStr = "useStatus"; + List dictTypeList1 = StringUtils.isNotBlank(dictTypeStr) ? Arrays.asList(dictTypeStr.split(",")) : null; + List dictTypeList = sysDictTypeService.getDictListByDictTypeList(dictTypeList1); + + Map dictMap = null; + if (CollectionUtil.isNotEmpty(dictTypeList)) { + dictMap = dictTypeList.stream().collect(Collectors.toMap(item -> String.valueOf(item.getDictType()), item -> item.getDataList())); + } + if (CollectionUtil.isNotEmpty(excels)) { + for (IotThingsExcel excel : excels) { + if (excel.getUseDeptId() != null) { + SysDeptEntity sysDeptEntity = sysDeptService.getById(excel.getUseDeptId()); + excel.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + } + if (StringUtils.isNotBlank(excel.getUseStatus())) { + if (dictMap != null) { + excel.setUseStatus(getDictValue(dictMap, "useStatus", excel.getUseStatus())); + } + } + } + } + return excels; + } + + public String getDictValue(Map dictMap, String dictType, String curValue) { + if (CollectionUtil.isEmpty(dictMap)) { + return ""; + } + //调整 + List temp = new ArrayList<>(); + Object obj = dictMap.get(dictType); + temp= JSON.parseArray(JSON.toJSONString(obj),SysDictDataEntity.class); + + if (CollectionUtil.isEmpty(temp)) { + return ""; + } + Map dictObject = new HashMap<>(); + for (SysDictDataEntity dictData:temp){ + dictObject.put(dictData.getDictValue(),dictData.getDictLabel()); + } + +// List temp = (List) dictMap.get(dictType); +// if (CollectionUtil.isEmpty(temp)) { +// return ""; +// } +// Map dictObject = temp.stream().collect(Collectors.toMap(item -> String.valueOf(item.get("dictValue")), item -> String.valueOf(item.get("dictLabel")))); + String faultType = StringUtils.isNotBlank(dictObject.get(curValue)) ? dictObject.get(curValue) : "当前字典值不存在"; + return faultType; + } + + @Override + public void updateScrap(Long eqId, String scrap, String scrapDisposal) { + iotThingBaseInfoDao.updateScrap(eqId, scrap, scrapDisposal); + } + + @Override + public List listExportPart(Map params, Long[] ids) { + String aLong1 = params.get("relationTypeId").toString(); + Long relationTypeId = new Long(aLong1); + Long toId=null; + if (ObjectUtil.isNotEmpty(params.get("toId"))){ + String aLong2 = params.get("toId").toString(); + toId = new Long(aLong2); + }; +// ids.length + List excels = iotThingBaseInfoDao.exportPartListByThingsId(relationTypeId,toId,ids); + return excels; + } + + @Override + public String getStock(String eqPartId) { + return iotThingBaseInfoDao.getStock(Long.parseLong(eqPartId)); + } + + @Override + public int getCodeCount(String eqCode) { + return iotThingBaseInfoDao.getCodeCount(eqCode); + } + + @Override + public int getCodeCountNoId(String eqCode, Long id) { + return iotThingBaseInfoDao.getCodeCountNoId(eqCode,id); + } + + @Override + public Long getByEqCode(String eqCode) { + return iotThingBaseInfoDao.getByEqCode(eqCode); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingsServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingsServiceImpl.java new file mode 100644 index 0000000..1ee01ab --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqmanager/service/impl/IotThingsServiceImpl.java @@ -0,0 +1,712 @@ +package com.thing.eq.eqmanager.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.DefaultType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.BizUtils; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqby.dto.EqInfo; +import com.thing.eq.eqby.service.EqByPlanPartService; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.EqInfoDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import com.thing.eq.eqmanager.entity.IotThingBaseInfoEntity; +import com.thing.eq.eqmanager.excel.EqInportExcel; +import com.thing.eq.eqmanager.service.EqAttacmentService; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.eqpartrecord.service.EqPartRecordService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.mapper.IotThingEntityMapper; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.dto.RelationDetailBatchSaveDTO; +import com.thing.thing.relation.detail.dto.ThingRelationDTO; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; +import org.thingsboard.server.common.data.Device; + +import java.util.*; + +/** + * 物管理 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +@Slf4j +public class IotThingsServiceImpl implements IotThingsService { + @Autowired + private SysDeptService sysDeptService; + +// @Autowired +// private RestClientUtils restClientUtils; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; +// +// @Autowired +// private ThingsRelationService thingsRelationService; + + @Autowired + private EqAttacmentService eqAttacmentService; + + @Autowired + private EqByPlanService eqByPlanService; + + @Autowired + private EqCheckSettingService eqCheckSettingService; + + @Autowired + private EqBxService eqBxService; + + @Autowired + private EqPartRecordService eqPartRecordService; + + @Autowired + private EqByPlanPartService eqByPlanPartService; + + @Autowired + private IotThingEntityMapper iotThingEntityDao; + + + @Autowired + private IotThingRelationDetailService iotThingRelationDetailService; + + @Autowired + private IotThingEntityService iotThingEntityService; + + + + @Override + public void saveEq(EqDTO dto) { + String code = BizUtils.trimAll(dto.getCode()); + //校验物编号是否重复 + if (StringUtils.isNotBlank(dto.getCode())){ + boolean exist = iotThingEntityDao.existsExt(QueryWrapper.create().eq(IotThingEntity::getCode, code)); + if (exist) { + throw new SysException("物编码已存在,请勿重复添加"); + } + } + //校验设备编号是否重复 + if (StringUtils.isNotBlank(dto.getEqCode())){ + int count = iotThingBaseInfoService.getCodeCount(dto.getEqCode()); + if (count > 0 ){ + throw new SysException("设备编号已存在,请确认"); + } + } + IotThingEntity parentThing = iotThingEntityDao.selectOneById(dto.getEqTypeId()); + if (parentThing==null){ + throw new SysException("上级设备不存在,请确认"); + } + //1.保存设备 调整 + //Long id = IdWorker.getId(); +// dto.setId(id); + IotThingEntity tenantEntity = new IotThingEntity(); +// tenantEntity.setId(id); + tenantEntity.setName(BizUtils.trimAll(dto.getName())); + tenantEntity.setCode(code); + tenantEntity.setRealType("0"); + tenantEntity.setTemplateMark("0"); + //获取deptId以及tenantCode + SysDeptEntity sysDeptEntity; + if (dto.getDeptId()==null){ + sysDeptEntity = sysDeptService.getDeptVaildPermission((Long) null); + }else { + sysDeptEntity = sysDeptService.getDeptVaildPermission(dto.getDeptId()); + } + + tenantEntity.setTenantCode(sysDeptEntity.getTenantCode()); + tenantEntity.setCompanyId(sysDeptEntity.getTenantCode()); + tenantEntity.setDeptIds(sysDeptEntity.getTenantCode().toString()); + if (dto.getType().equals("equipment")){ + tenantEntity.setType(DefaultType.EQUIPMENT.getValue()); + }else{ + tenantEntity.setType(DefaultType.EQUIPMENT_PART.getValue()); + } + tenantEntity.setEnableStatus("1"); + //推送TB + // Device device = sendEntityToTB(code); + //物实体新增 + iotThingEntityDao.insert(tenantEntity); + //*** + IotThingEntity thingEntity = iotThingEntityDao.selectCode(code); + //保存设备基础信息 + //图片不为空,取第一个图片 + int num = 0; + List attacmentDTOS = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(dto.getImageUrls())){ + for (EqAttacmentDTO imageurl : dto.getImageUrls()){ + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + //调整 + attacmentDTO.setThingId(thingEntity.getId()); + attacmentDTO.setUrl(imageurl.getUrl()); + attacmentDTO.setType("0"); + attacmentDTO.setModularType("0"); + attacmentDTO.setName(imageurl.getName()); + attacmentDTOS.add(attacmentDTO); + if (num==0){ + //第一个图片需要展示 + dto.setImageUrl(imageurl.getUrl()); + } + num++; + } + } + if (CollectionUtil.isNotEmpty(dto.getAttachmentUrls())){ + for (EqAttacmentDTO attacmentDTOUrl: dto.getAttachmentUrls()){ + //上传附件 + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + //调整 + attacmentDTO.setThingId(thingEntity.getId()); + attacmentDTO.setUrl(attacmentDTOUrl.getUrl()); + attacmentDTO.setType("1"); + attacmentDTO.setModularType("0"); + attacmentDTO.setName(attacmentDTOUrl.getName()); + attacmentDTOS.add(attacmentDTO); + } + } + IotThingBaseInfoDTO baseInfoDTO = new IotThingBaseInfoDTO(); + BeanUtils.copyProperties(dto,baseInfoDTO); + //*** + baseInfoDTO.setThingId(thingEntity.getId()); + baseInfoDTO.setScrap("0"); + baseInfoDTO.setScrapDisposal("0"); + iotThingBaseInfoService.saveDto(baseInfoDTO); + //保存设备的关系 + ThingRelationDTO relationDTO = new ThingRelationDTO(); + relationDTO.setRootId(dto.getRelationTypeId()); + relationDTO.setRootThingId(dto.getRelationTopId()); + relationDTO.setFromId(parentThing.getId()); + relationDTO.setFromCode(parentThing.getCode()); + relationDTO.setFromName(parentThing.getName()); + List relationList = new ArrayList<>(); + ThingRelationDTO.RelationEntity relation = new ThingRelationDTO.RelationEntity(); + + relation.setToId(thingEntity.getId()); + relation.setToCode(code); + relation.setToName(dto.getName()); + relationList.add(relation); + relationDTO.setRelationList(relationList); + iotThingRelationDetailService.saveRelationNodes(relationDTO); + //保存附件 + eqAttacmentService.saveBatch(ConvertUtils.sourceToTarget(attacmentDTOS, EqAttacmentEntity.class)); + } + +// /** +// * 将物实体推送到TB +// * +// * @param code +// */ +// public Device sendEntityToTB(String code) { +// Device device = deviceDataFacade.getSingleDevice(code); +// if (ObjectUtil.isNull(device)) { +// //存入tb,并从tb获取到deviceId的数据:必须在TB中已经存在当前设备,不要不然后面插入静态属性的时候,报错空指针 +// DeviceEntity deviceEntity = new DeviceEntity(); +// deviceEntity.setThingCode(code); +// deviceEntity.setGateWayMark(false); +// device = deviceDataFacade.saveDevice(deviceEntity); +// if (ObjectUtil.isNull(device)) { +// throw new SysException("插入tb返回成功,但是却没有从tb中查询到该设备数据"); +// } +// } +// return device; +// } + + @Override + public void updateEq(EqDTO dto) { + //校验设备编号是否重复 + if (StringUtils.isNotBlank(dto.getEqCode())){ + int count = iotThingBaseInfoService.getCodeCountNoId(dto.getEqCode(),dto.getId()); + if (count > 0 ){ + throw new SysException("设备编号已存在,请确认"); + } + } + IotThingEntity thingEntity = iotThingEntityDao.selectOneById(dto.getId()); + thingEntity.setName(dto.getName()); + thingEntity.setCode(dto.getCode()); + //更新设备 + iotThingEntityDao.update(thingEntity); + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(dto.getId()); + if (baseInfoDTO==null){ + return; + } + Long baseInfoId = baseInfoDTO.getId(); + //更新基础信息 + //删除所有附件 + eqAttacmentService.deleteBatchByThingId(dto.getId()); + //删除附件 + deleteFileByThingsId(dto.getId()); + //图片不为空,取第一个图片 + int num = 0; + List attacmentDTOS = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(dto.getImageUrls())){ + for (EqAttacmentDTO imageurl : dto.getImageUrls()){ + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + attacmentDTO.setThingId(dto.getId()); + attacmentDTO.setUrl(imageurl.getUrl()); + attacmentDTO.setType("0"); + attacmentDTO.setModularType("0"); + attacmentDTO.setName(imageurl.getName()); + attacmentDTOS.add(attacmentDTO); + if (num==0){ + //第一个图片需要展示 + dto.setImageUrl(imageurl.getUrl()); + } + num++; + } + }else{ + dto.setImageUrl(null); + } + if (CollectionUtil.isNotEmpty(dto.getAttachmentUrls())){ + for (EqAttacmentDTO attacmentDTOUrl: dto.getAttachmentUrls()){ + //上传附件 + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + attacmentDTO.setThingId(dto.getId()); + attacmentDTO.setUrl(attacmentDTOUrl.getUrl()); + attacmentDTO.setType("1"); + attacmentDTO.setModularType("0"); + attacmentDTO.setName(attacmentDTOUrl.getName()); + attacmentDTOS.add(attacmentDTO); + } + } + BeanUtils.copyProperties(dto, baseInfoDTO); + baseInfoDTO.setId(baseInfoId); + iotThingBaseInfoService.updateDto(baseInfoDTO); + +// thingsRelationService.deleteByToIdAndRelationTypeId(dto.getId().toString(),dto.getRelationTypeId().toString()); +// ThingsRelationDTO relationDTO = new ThingsRelationDTO(); +// relationDTO.setFromId(dto.getEqTypeId().toString()); +// relationDTO.setToId(dto.getId().toString()); +// relationDTO.setSort(thingsRelationService.getSort(dto.getEqTypeId().toString(),dto.getRelationTypeId().toString())); +// relationDTO.setRelationTypeId(dto.getRelationTypeId().toString()); +// thingsRelationService.save(relationDTO); + //保存附件 + eqAttacmentService.saveBatch(ConvertUtils.sourceToTarget(attacmentDTOS, EqAttacmentEntity.class)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteEqs(Long[] ids) { + if(null == ids || ids.length == 0){ + throw new SysException("参数不能为空"); + } + for (Long eqId : ids){ + IotThingEntity eq = iotThingEntityDao.selectOneById(eqId); + if (eq !=null && StringUtils.isNotBlank(eq.getName())){ + if (StringUtils.isNotBlank(eq.getType())){ + //equipment表示设备,equipment_part表示部件 + if (StringUtils.equals("equipment",eq.getType())){ + //判断设备是否存在报修 + int bxCount = eqBxService.findBxCountByEqId(eqId); + if (bxCount > 0){ + throw new SysException(eq.getName()+" 设备存在报修,无法删除,请确认"); + } + //判断设备是否存在保养计划 + int byPlanCount = eqByPlanService.getByCountByEqId(eqId); + if (byPlanCount > 0){ + throw new SysException(eq.getName()+" 设备存在保养计划,无法删除,请确认"); + } + //判断设备是否存在巡检计划 + int patrolPlanCount = eqCheckSettingService.getPatrolPlanCountByEqId(eqId,"1"); + if (patrolPlanCount > 0){ + throw new SysException(eq.getName()+" 设备存在巡检计划,无法删除,请确认"); + } + //判断设备是否存在点检计划 + int spotPlanCount = eqCheckSettingService.getPatrolPlanCountByEqId(eqId,"0"); + if (spotPlanCount > 0){ + throw new SysException(eq.getName()+" 设备存在点检计划,无法删除,请确认"); + } + }else if (StringUtils.equals("equipment_part",eq.getType())){ + //判断维修存在备件更换记录 + int wxPlanCount = eqPartRecordService.getByEqId(eq.getId(),"0"); + if (wxPlanCount > 0){ + throw new SysException(eq.getName()+" 部件已被维修使用,无法删除,请确认"); + } + //判断保养计划是否使用备件 + int byPlanCount = eqByPlanPartService.getByCountByEqId(eq.getId()); + if (byPlanCount > 0){ + throw new SysException(eq.getName()+" 部件已被保养计划使用,无法删除,请确认"); + } + //判断保养存在备件更换记录 + int byCount = eqPartRecordService.getByEqId(eq.getId(),"1"); + if (byCount > 0){ + throw new SysException(eq.getName()+" 部件已被保养使用,无法删除,请确认"); + } + } + } + } + } + //删除设备 + iotThingEntityDao.deleteBatchByIds(Arrays.asList(ids)); + //删除设备基本信息 + iotThingBaseInfoService.deleteByThingsId(ids); + //设备的关系 + iotThingRelationDetailService.removeByIds(Arrays.asList(ids)); +// thingsRelationService.deleteByToId(ids); + for (Long id : ids){ + //删除附件 + deleteFileByThingsId(id); + } + //删除设备的附件 + eqAttacmentService.deleteBatchByThingIds(ids); + } + + private void deleteFileByThingsId(Long id) { + List imageUrls = new ArrayList<>(); + List attachmentUrls = new ArrayList<>(); + List attacmentDTOS = eqAttacmentService.getByThingId(id); + if (CollectionUtil.isNotEmpty(attacmentDTOS)){ + for (EqAttacmentDTO attacmentDTO : attacmentDTOS){ + if (StringUtils.equals(attacmentDTO.getType(),"0")){ + imageUrls.add(attacmentDTO); + }else { + attachmentUrls.add(attacmentDTO); + } + } + } + eqBxService.deleteFile(id,imageUrls,attachmentUrls); + } + + @Override + public EqDTO getInfo(Long id) { + EqDTO info = new EqDTO(); + //获取设备 + IotThingEntity eq = iotThingEntityDao.selectOneById(id); + if (eq==null){ + return null; + } + info.setId(eq.getId()); + info.setCode(eq.getCode()); + info.setName(eq.getName()); + info.setType(eq.getType()); + info.setTenantCode(eq.getTenantCode()); + info.setDeptId(eq.getDeptIds()); + info.setCreator(eq.getCreator()); + info.setCreateDate(eq.getCreateDate()); + info.setUpdater(eq.getUpdater()); + info.setUpdateDate(eq.getUpdateDate()); +// BeanUtils.copyProperties(eq,info); + //获取设备基础信息 + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(id); + BeanUtils.copyProperties(baseInfoDTO,info); + + List imageUrls = new ArrayList<>(); + List attachmentUrls = new ArrayList<>(); + List attacmentDTOS = eqAttacmentService.getByThingId(id); + if (CollectionUtil.isNotEmpty(attacmentDTOS)){ + for (EqAttacmentDTO attacmentDTO : attacmentDTOS){ + if (StringUtils.equals(attacmentDTO.getType(),"0")){ + imageUrls.add(attacmentDTO); + }else { + attachmentUrls.add(attacmentDTO); + } + } + } + info.setImageUrls(imageUrls); + info.setAttachmentUrls(attachmentUrls); + //获取设备类型名称 +// String name = thingsRelationService.getThingName(id.toString()); + return info; + } + + @Override + public EqInfo getThingInfo(Long thingId, String relationTypeId) { + EqInfo result = new EqInfo(); + IotThingEntity eq = iotThingEntityDao.selectOneById(thingId); + if (eq==null){ + return null; + } + result.setId(eq.getId()); + result.setCode(eq.getCode()); + result.setName(eq.getName()); + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(thingId); + result.setStandard(baseInfoDTO.getStandard()); + result.setUseDeptId(baseInfoDTO.getUseDeptId()); + result.setEqCode(baseInfoDTO.getEqCode()); + result.setScrap(baseInfoDTO.getScrap()); + result.setScrapDisposal(baseInfoDTO.getScrapDisposal()); + return result; +// return iotThingsDao.getThingInfo(thingId,relationTypeId); + } + + @Override + public EqInfoDTO getEqInfo(Long eqId, String relationTypeId) { + EqInfoDTO result = new EqInfoDTO(); + IotThingEntity eq = iotThingEntityDao.selectOneById(eqId); + if (eq==null){ + return null; + } + result.setEqId(eq.getId()); + result.setCode(eq.getCode()); + result.setName(eq.getName()); + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(eqId); + result.setStandard(baseInfoDTO.getStandard()); + result.setUseDeptId(baseInfoDTO.getUseDeptId()); + result.setEqCode(baseInfoDTO.getEqCode()); + result.setDirector(baseInfoDTO.getDirector()); + result.setEqPosition(baseInfoDTO.getEqPosition()); + result.setImageUrl(baseInfoDTO.getImageUrl()); + IotThingRelationDetailEntity thingsRelation = iotThingRelationDetailService.getThingsRelations(Long.parseLong(relationTypeId), eqId); + result.setEqTypeName(thingsRelation.getFromName()); + return result; +// return iotThingsDao.getEqInfo(eqId,relationTypeId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String importExcel(MultipartFile file, String rootId,String relationTopId,String eqTypeId) { + if (StringUtils.isBlank(eqTypeId)){ + throw new SysException("设备类型不能为空"); + } + String fileName = file.getOriginalFilename(); + if(!StringUtils.endsWith(fileName, "xls") && !StringUtils.endsWith(fileName, "xlsx")){ + throw new SysException("文件格式不合法,请选择Excel文件"); + } + //2.获取Excel中的数据信息 + List EqInportExcels = ExcelUtils.importExcel(file, 0, 1, EqInportExcel.class); + if(CollectionUtil.isEmpty(EqInportExcels)){ + throw new SysException("没有可导入的数据,请确认文件是否有内容"); + } + String successFlag =""; + List EqInportExcels1 = new ArrayList<>(); + Map map = new HashMap<>(); + for (EqInportExcel excel : EqInportExcels ) { + if (StringUtils.isBlank(excel.getName())){ + throw new SysException("请确认部分数据设备名称必填项"); + } + if (StringUtils.isBlank(excel.getStandard())){ + throw new SysException("请确认部分数据设备规格必填项"); + } + + if (StringUtils.isBlank(excel.getCode())) { + throw new SysException("请确认部分数据物编号必填项"); + } else { + if (map != null && StringUtils.contains(map.get("code"),excel.getCode())){ + throw new SysException("请确认导入数据物编号是否有重复"); + } + boolean exist = iotThingEntityDao.existsExt(QueryWrapper.create().eq(IotThingEntity::getCode, excel.getCode())); + if (exist) { + continue; + } +// int count = iotThingsDao.getCodeCount(excel.getCode()); +// if (count > 0) { +// continue; +// } + map.put("code",excel.getCode()); + } + //校验设备编号是否重复 + if (StringUtils.isBlank(excel.getEqCode())){ + throw new SysException("请确认部分数据设备编号必填项"); + }else{ + if (map != null && StringUtils.contains(map.get("eqCode"),excel.getCode())){ + throw new SysException("请确认导入数据设备编号是否有重复"); + } + int count = iotThingBaseInfoService.getCodeCount(excel.getEqCode()); + if (count > 0 ){ + continue; + } + map.put("eqCode",excel.getEqCode()); + } + EqInportExcels1.add(excel); + } + IotThingEntity parentThing = iotThingEntityDao.selectOneById(eqTypeId); + if (parentThing==null){ + throw new SysException("上级设备不存在,请确认"); + } + if(CollectionUtil.isEmpty(EqInportExcels1)){ + throw new SysException("没有可导入的数据,请确认文件是否有内容"); + } + //设备 + List eqDTOS = new ArrayList<>(); + List iotThingBaseInfoDTOS = new ArrayList<>(); + RelationDetailBatchSaveDTO request = new RelationDetailBatchSaveDTO(); + request.setRootId(Long.parseLong(rootId)); + List details = new ArrayList<>(); + RelationDetailBatchSaveDTO.ThingRelationDetail relationDetail = new RelationDetailBatchSaveDTO.ThingRelationDetail(); + relationDetail.setRootThingId(Long.parseLong(relationTopId)); + List relationDetails = new ArrayList<>(); + //处理设备 + for (EqInportExcel excel : EqInportExcels1){ + //1.保存设备 + String code = excel.getCode(); + //Long id = IdWorker.getId(); + IotThingEntity tenantEntity = new IotThingEntity(); +// tenantEntity.setId(id); + tenantEntity.setName(BizUtils.trimAll(excel.getName())); + tenantEntity.setCode(code); + tenantEntity.setRealType("0"); + tenantEntity.setTemplateMark("0"); + //获取deptId以及tenantCode + SysDeptEntity sysDeptEntity; + sysDeptEntity = sysDeptService.getDeptVaildPermission((Long) null); + tenantEntity.setTenantCode(sysDeptEntity.getTenantCode()); + tenantEntity.setCompanyId(sysDeptEntity.getTenantCode()); + tenantEntity.setDeptIds(sysDeptEntity.getTenantCode().toString()); + tenantEntity.setType(DefaultType.EQUIPMENT.getValue()); + + tenantEntity.setEnableStatus("1"); + //推送TB + //Device device = sendEntityToTB(code); + //物实体新增 +// iotThingEntityDao.insert(tenantEntity); + //设备基础信息 + IotThingBaseInfoDTO baseInfoDTO = new IotThingBaseInfoDTO(); + BeanUtils.copyProperties(excel,baseInfoDTO); + //调整 +// baseInfoDTO.setId(id); +// baseInfoDTO.setThingId(id); + baseInfoDTO.setScrap("0"); + baseInfoDTO.setScrapDisposal("0"); + iotThingBaseInfoDTOS.add(baseInfoDTO); + //设备的关系 + IotThingRelationDetailDTO relationDTO = new IotThingRelationDetailDTO(); + + relationDTO.setFromId(parentThing.getId()); + relationDTO.setFromName(parentThing.getName()); + relationDTO.setFromCode(parentThing.getCode()); + //调整 +// relationDTO.setToId(id); + relationDTO.setToName(excel.getName()); + relationDTO.setToCode(excel.getCode()); + relationDTO.setRootId(Long.parseLong(rootId)); + relationDTO.setRootThingId(Long.parseLong(relationTopId)); + relationDetails.add(relationDTO); + } + //保存 + iotThingEntityService.saveBatch(eqDTOS); + iotThingBaseInfoService.saveBatch(ConvertUtils.sourceToTarget(iotThingBaseInfoDTOS, IotThingBaseInfoEntity.class)); + relationDetail.setRelationDetails(relationDetails); + details.add(relationDetail); + request.setDetails(details); + iotThingRelationDetailService.saveDto(request); + return successFlag; + } + + @Override + public EqInfo getThingInfoNew(Long eqPartId) { + EqInfo result = new EqInfo(); + IotThingEntity eq = iotThingEntityDao.selectOneById(eqPartId); + if (eq==null){ + return null; + } + result.setId(eq.getId()); + result.setCode(eq.getCode()); + result.setName(eq.getName()); + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(eqPartId); + result.setStandard(baseInfoDTO.getStandard()); + result.setUseDeptId(baseInfoDTO.getUseDeptId()); + result.setEqCode(baseInfoDTO.getEqCode()); + result.setScrap(baseInfoDTO.getScrap()); + result.setScrapDisposal(baseInfoDTO.getScrapDisposal()); + return result; +// return iotThingsDao.getThingInfoNew(eqPartId); + } + + @Override + public EqDTO getThingsByCode(String code) { + EqDTO info = new EqDTO(); + //获取设备 + IotThingEntity eq = iotThingEntityDao.selectOneById(code); + if (eq==null){ + return null; + } + info.setId(eq.getId()); + info.setCode(eq.getCode()); + info.setName(eq.getName()); + info.setType(eq.getType()); + info.setTenantCode(eq.getTenantCode()); + info.setDeptId(eq.getDeptIds()); + info.setCreator(eq.getCreator()); + info.setCreateDate(eq.getCreateDate()); + info.setUpdater(eq.getUpdater()); + info.setUpdateDate(eq.getUpdateDate()); +// BeanUtils.copyProperties(eq,info); + //获取设备基础信息 + IotThingBaseInfoDTO baseInfoDTO = iotThingBaseInfoService.getByThingsId(eq.getId()); + BeanUtils.copyProperties(baseInfoDTO,info); + + List imageUrls = new ArrayList<>(); + List attachmentUrls = new ArrayList<>(); + List attacmentDTOS = eqAttacmentService.getByThingId(eq.getId()); + if (CollectionUtil.isNotEmpty(attacmentDTOS)){ + for (EqAttacmentDTO attacmentDTO : attacmentDTOS){ + if (StringUtils.equals(attacmentDTO.getType(),"0")){ + imageUrls.add(attacmentDTO); + }else { + attachmentUrls.add(attacmentDTO); + } + } + } + info.setImageUrls(imageUrls); + info.setAttachmentUrls(attachmentUrls); + //获取设备类型名称 +// String name = thingsRelationService.getThingName(id.toString()); + return info; +// return iotThingsDao.getThingsByCode(code); + } + + // @TokenFailureCatch + private String syncThingsToTB(EqDTO thingsReqDTO) { + //todo + Device device = new Device(); + device.setName(thingsReqDTO.getCode()); + device.setType(thingsReqDTO.getType()); + String deviceId = null; +// try { +// //插入tb一条设备信息,如果存在此设备,返回该设备编号已存在 +// restClientUtils.saveDevice(device); +// }catch (Exception e){ +// e.printStackTrace(); +// throw new RenException("数据中台:"+handlerExMsg(e.getMessage())); +// }finally{ +// deviceId = String.valueOf(restClientUtils.getTenantDevice(thingsReqDTO.getCode()).get().getId()); +// return deviceId; +// } + return null; + } + + /** + * 处理报错Msg + * @param message + * @return + */ + private String handlerExMsg(String message) { + try{ + StringBuilder msg = new StringBuilder(message); + msg.insert(0,"{"); + msg.insert(message.length()+1,"}"); + Map result = JSONObject.parseObject(StringUtils.replace(msg.toString(), " : ", ":"), HashMap.class); + for(Map.Entry entry : result.entrySet()){ + List jsonObjects = ((JSONArray)entry.getValue()).toJavaList(JSONObject.class); + message = (String)jsonObjects.get(0).get("message"); + } + return message; + }catch(Exception ex){ + log.info("处理报错Msg出现异常,异常原因:{}", ex.getMessage()); + return message; + } + } + + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/controller/EqPlanRecordController.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/controller/EqPlanRecordController.java new file mode 100644 index 0000000..d995606 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/controller/EqPlanRecordController.java @@ -0,0 +1,122 @@ +package com.thing.eq.eqpalnrecord.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqpalnrecord.dto.EqPlanRecordDTO; +import com.thing.eq.eqpalnrecord.excel.EqPlanRecordExcel; +import com.thing.eq.eqpalnrecord.service.EqPlanRecordService; +import com.thing.eq.task.PlanTimeTask; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.Map; + + +/** +* 计划未执行记录 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-06-15 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqpalnrecord") +@Tag(name="设备-计划未执行记录") +public class EqPlanRecordController { + @Autowired + private EqPlanRecordService eqPlanRecordService; + @Autowired + private PlanTimeTask planTimeTask; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "type", description = "类型 0保养 1巡检"), + @Parameter(name = "handle", description = "是否处理 0未处理 1已处理"), + @Parameter(name = "planNo", description = "计划单号") + }) +// @RequiresPermissions("eqpalnrecord:eqplanrecord:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqPlanRecordService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("eqpalnrecord:eqplanrecord:info") + public Result get(@PathVariable("id") Long id){ + EqPlanRecordDTO data = eqPlanRecordService.getById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("eqpalnrecord:eqplanrecord:save") + public Result save(@RequestBody EqPlanRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + eqPlanRecordService.saveDto(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("eqpalnrecord:eqplanrecord:update") + public Result update(@RequestBody EqPlanRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + eqPlanRecordService.updateDto(dto); + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("eqpalnrecord:eqplanrecord:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqPlanRecordService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("eqpalnrecord:eqplanrecord:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqPlanRecordService.listAs(params,EqPlanRecordDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqPlanRecordExcel.class); + ExcelUtils.exportExcel(list1,null, "计划未执行记录", EqPlanRecordExcel.class,"计划未执行记录.xls",response); + +// ExcelUtils.exportExcelToTarget(response, null, "计划未执行记录", list, EqPlanRecordExcel.class); + } + + @GetMapping("test") + @Operation(summary="测试") + public void get(){ + planTimeTask.send(); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/dto/EqPlanRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/dto/EqPlanRecordDTO.java new file mode 100644 index 0000000..dd471e3 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/dto/EqPlanRecordDTO.java @@ -0,0 +1,70 @@ +package com.thing.eq.eqpalnrecord.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 计划未执行记录 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-06-15 +*/ +@Data +@Schema(description = "计划未执行记录") +public class EqPlanRecordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @Schema(description = "计划开始时间") + private Date planStartTime; + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @Schema(description = "计划结束时间") + private Date planEndTime; + @Schema(description = "计划执行人") + private String userId; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "计划名称") + private String planName; + @Schema(description = "计划id") + private Long planId; + @Schema(description = "设备id") + private Long thingId; + @Schema(description = "是否处理 0未处理 1已处理") + private String handle; + @Schema(description = "计划类型 0保养 1巡检") + private String type; + @Schema(description = "未执行原因") + private String reason; + @Schema(description = "计划编号") + private String planNo; + + + @Schema(description = "设备名称") + private String eqName; + @Schema(description = "设备编号") + private String eqCode; + @Schema(description = "计划执行人(名称)") + private String userIdStr; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/entity/EqPlanRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/entity/EqPlanRecordEntity.java new file mode 100644 index 0000000..9e90f1f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/entity/EqPlanRecordEntity.java @@ -0,0 +1,91 @@ +package com.thing.eq.eqpalnrecord.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 计划未执行记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-06-15 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_plan_record") +public class EqPlanRecordEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 创建人 + */ + private Long creator; + /** + * 创建时间 + */ + private Date createDate; + /** + * 更新人 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 计划开始时间 + */ + private Date planStartTime; + /** + * 计划结束时间 + */ + private Date planEndTime; + /** + * 计划执行人 + */ + private String userId; + /** + * 企业id + */ + private Long deptId; + /** + * 租户code + */ + private Long tenantCode; + /** + * 计划名称 + */ + private String planName; + /** + * 计划id + */ + private Long planId; + /** + * 设备id + */ + private Long thingId; + /** + * 是否处理 0未处理 1已处理 + */ + private String handle; + /** + * 计划类型 0保养 1巡检 + */ + private String type; + /** + * 计划类型 0保养 1巡检 + */ + private String reason; + /** + * 计划编号 + */ + private String planNo; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/excel/EqPlanRecordExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/excel/EqPlanRecordExcel.java new file mode 100644 index 0000000..4dbb9bf --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/excel/EqPlanRecordExcel.java @@ -0,0 +1,54 @@ +package com.thing.eq.eqpalnrecord.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 计划未执行记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-06-15 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqPlanRecordExcel { + @Excel(name = "id", orderNum = "0") + private Long id; + @Excel(name = "创建人", orderNum = "1") + private Long creator; + @Excel(name = "创建时间", orderNum = "2") + private Date createDate; + @Excel(name = "更新人", orderNum = "3") + private Long updater; + @Excel(name = "更新时间", orderNum = "4") + private Date updateDate; + @Excel(name = "计划开始时间", orderNum = "5") + private Date planStartTime; + @Excel(name = "计划结束时间", orderNum = "6") + private Date planEndTime; + @Excel(name = "计划执行人", orderNum = "7") + private String userId; + @Excel(name = "企业id", orderNum = "8") + private Long deptId; + @Excel(name = "租户code", orderNum = "9") + private Long tenantCode; + @Excel(name = "计划名称", orderNum = "10") + private String planName; + @Excel(name = "计划id", orderNum = "11") + private Long planId; + @Excel(name = "设备id", orderNum = "12") + private Long thingId; + @Excel(name = "是否处理 0未处理 1已处理", orderNum = "13") + private String handle; + @Excel(name = "计划类型 0保养 1巡检", orderNum = "14") + private String type; + @Excel(name = "计划类型 0保养 1巡检", orderNum = "15") + private String reason; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/mapper/EqPlanRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/mapper/EqPlanRecordMapper.java new file mode 100644 index 0000000..a346b9f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/mapper/EqPlanRecordMapper.java @@ -0,0 +1,21 @@ +package com.thing.eq.eqpalnrecord.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqpalnrecord.dto.EqPlanRecordDTO; +import com.thing.eq.eqpalnrecord.entity.EqPlanRecordEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 计划未执行记录 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-06-15 +*/ +@Mapper +public interface EqPlanRecordMapper extends PowerBaseMapper { + + List getListData(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/EqPlanRecordService.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/EqPlanRecordService.java new file mode 100644 index 0000000..2cddce1 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/EqPlanRecordService.java @@ -0,0 +1,22 @@ +package com.thing.eq.eqpalnrecord.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqpalnrecord.dto.EqPlanRecordDTO; +import com.thing.eq.eqpalnrecord.entity.EqPlanRecordEntity; + +import java.util.Map; + +/** + * 计划未执行记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-06-15 + */ +public interface EqPlanRecordService extends IBaseService { + + EqPlanRecordDTO getById(Long id); + + PageData page(Map params); + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/impl/EqPlanRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/impl/EqPlanRecordServiceImpl.java new file mode 100644 index 0000000..3a7c364 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpalnrecord/service/impl/EqPlanRecordServiceImpl.java @@ -0,0 +1,96 @@ +package com.thing.eq.eqpalnrecord.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.eqpalnrecord.dto.EqPlanRecordDTO; +import com.thing.eq.eqpalnrecord.entity.EqPlanRecordEntity; +import com.thing.eq.eqpalnrecord.mapper.EqPlanRecordMapper; +import com.thing.eq.eqpalnrecord.service.EqPlanRecordService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 计划未执行记录 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-06-15 + */ +@Service +public class EqPlanRecordServiceImpl extends BaseServiceImpl implements EqPlanRecordService { + + @Autowired + private IotThingsService iotThingsService; + @Autowired + private SysUserService sysUserService; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = getPage(params); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List EqPlanRecordDTOs = mapper.getListData(params); + for (EqPlanRecordDTO eqPlanRecordDTO : EqPlanRecordDTOs) { + EqDTO data = iotThingsService.getInfo(eqPlanRecordDTO.getThingId()); + if (data != null) { + eqPlanRecordDTO.setEqName(data.getName()); + eqPlanRecordDTO.setEqCode(data.getEqCode()); + } + //调整 + String userId = eqPlanRecordDTO.getUserId(); + String[] split = userId.split(","); + + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } + List planUserName1 = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName1) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqPlanRecordDTO.setUserIdStr(name); + } + return new PageData<>(EqPlanRecordDTOs, page.getTotalRow()); + } + + @Override + public EqPlanRecordDTO getById(Long id) { + EqPlanRecordDTO eqPlanRecordDTO = getById(id); + EqDTO data = iotThingsService.getInfo(eqPlanRecordDTO.getThingId()); + if (data != null) { + eqPlanRecordDTO.setEqName(data.getName()); + eqPlanRecordDTO.setEqCode(data.getEqCode()); + } + //调整 + String userId = eqPlanRecordDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + }List planUserName1 = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName1) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqPlanRecordDTO.setUserIdStr(name); + return eqPlanRecordDTO; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/controller/EqPartRecordController.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/controller/EqPartRecordController.java new file mode 100644 index 0000000..6e59e9a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/controller/EqPartRecordController.java @@ -0,0 +1,118 @@ +package com.thing.eq.eqpartrecord.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import com.thing.eq.eqpartrecord.excel.EqPartRecordExcel; +import com.thing.eq.eqpartrecord.service.EqPartRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.List; +import java.util.Map; + + +/** +* 部件使用记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@RestController +@RequestMapping("eqpartrecord/eqpartrecord") +@Tag(name="设备-部件使用记录") +public class EqPartRecordController { + @Autowired + private EqPartRecordService eqPartRecordService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "eqIds", description = "备件id,多个用,隔开") + }) +// @RequiresPermissions("equipment:sparePartStore:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqPartRecordService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("eqpartrecord:eqpartrecord:info") + public Result get(@PathVariable("id") Long id){ + EqPartRecordDTO data = eqPartRecordService.getByIdAs(id,EqPartRecordDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("equipment:sparePartStore:add") + public Result save(@RequestBody EqPartRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + eqPartRecordService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("equipment:sparePartStore:update") + public Result update(@RequestBody EqPartRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + eqPartRecordService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("equipment:sparePartStore:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + eqPartRecordService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("equipment:sparePartStore:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqPartRecordService.listAs(params,EqPartRecordDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqPartRecordExcel.class); + ExcelUtils.exportExcel(list,null, "部件使用记录", EqPartRecordExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, null, "部件使用记录", list, EqPartRecordExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/dto/EqPartRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/dto/EqPartRecordDTO.java new file mode 100644 index 0000000..36a2907 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/dto/EqPartRecordDTO.java @@ -0,0 +1,49 @@ +package com.thing.eq.eqpartrecord.dto; + +import com.thing.eq.eqby.dto.EqInfo; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 部件使用记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Data +@Schema(description = "部件使用记录") +public class EqPartRecordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "更换数量") + private String useCount; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "设备操作id(例如设备保养,设备维修等)") + private Long eqOperateId; + @Schema(description = "操作类型0设备维修 1设备保养") + private String type; + + @Schema(description = "设备部件id") + private Long eqPartId; + @Schema(description = "设备信息") + private EqInfo thingsInfo; + @Schema(description = "操作单号(保养单号、维修单号)") + private String eqOperateNo; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/entity/EqPartRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/entity/EqPartRecordEntity.java new file mode 100644 index 0000000..2fe6297 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/entity/EqPartRecordEntity.java @@ -0,0 +1,73 @@ +package com.thing.eq.eqpartrecord.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_part_record") +public class EqPartRecordEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 更换数量 + */ + private String useCount; + /** + * 创建人 + */ + + private Long creator; + /** + * 创建时间 + */ + + private Date createDate; + /** + * 更新人 + */ + + private Long updater; + /** + * 更新时间 + */ + + private Date updateDate; + /** + * 设备操作id(例如设备保养,设备维修等) + */ + private Long eqOperateId; + /** + * 操作类型0设备维修 1设备保养 + */ + private String type; + /** + * 设备部件id + */ + private Long eqPartId; + /** + * 企业id + */ + + private Long deptId; + /** + * 租户code + */ + + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/excel/EqPartRecordExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/excel/EqPartRecordExcel.java new file mode 100644 index 0000000..7a271f6 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/excel/EqPartRecordExcel.java @@ -0,0 +1,36 @@ +package com.thing.eq.eqpartrecord.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqPartRecordExcel { + @Excel(name= "id", orderNum = "1") + private Long id; + @Excel(name= "更换数量", orderNum = "2") + private String useCount; + @Excel(name= "创建人", orderNum = "3") + private Long creator; + @Excel(name= "创建时间", orderNum = "4") + private Date createDate; + @Excel(name= "更新人", orderNum = "5") + private Long updater; + @Excel(name= "更新时间", orderNum = "6") + private Date updateDate; + @Excel(name= "设备维修id", orderNum = "7") + private Long eqWxId; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/mapper/EqPartRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/mapper/EqPartRecordMapper.java new file mode 100644 index 0000000..f9ffe4c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/mapper/EqPartRecordMapper.java @@ -0,0 +1,27 @@ +package com.thing.eq.eqpartrecord.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import com.thing.eq.eqpartrecord.entity.EqPartRecordEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 部件使用记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-09-22 +*/ +@Mapper +public interface EqPartRecordMapper extends PowerBaseMapper { + + List getByEqById(@Param("byId") Long byId); + + int getByEqId(@Param("eqId") Long eqId, @Param("type") String type); + + List getList(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/EqPartRecordService.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/EqPartRecordService.java new file mode 100644 index 0000000..e626bee --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/EqPartRecordService.java @@ -0,0 +1,26 @@ +package com.thing.eq.eqpartrecord.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import com.thing.eq.eqpartrecord.entity.EqPartRecordEntity; + +import java.util.List; +import java.util.Map; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +public interface EqPartRecordService extends IBaseService { + + List getByEqById(Long id); + + int getByEqId(Long id, String s); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/impl/EqPartRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/impl/EqPartRecordServiceImpl.java new file mode 100644 index 0000000..89bf48f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqpartrecord/service/impl/EqPartRecordServiceImpl.java @@ -0,0 +1,127 @@ +package com.thing.eq.eqpartrecord.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqbxwx.dto.EqWxPlanDTO; +import com.thing.eq.eqbxwx.service.EqBxService; +import com.thing.eq.eqbxwx.service.EqWxPlanService; +import com.thing.eq.eqby.dto.EqByDTO; +import com.thing.eq.eqby.dto.EqByPlanPartInfoDTO; +import com.thing.eq.eqby.dto.EqInfo; +import com.thing.eq.eqby.service.EqByService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.eqpartrecord.dto.EqPartRecordDTO; +import com.thing.eq.eqpartrecord.entity.EqPartRecordEntity; +import com.thing.eq.eqpartrecord.mapper.EqPartRecordMapper; +import com.thing.eq.eqpartrecord.service.EqPartRecordService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 部件使用记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-22 + */ +@Service +public class EqPartRecordServiceImpl extends BaseServiceImpl implements EqPartRecordService { + + @Autowired + private EqPartRecordMapper eqPartRecordDao; + @Autowired + private IotThingsService iotThingsService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private EqByService eqByService; + @Autowired + private EqBxService eqBxService; + @Autowired + private EqWxPlanService eqWxPlanService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = getPage(params); + String eqIds; + List eqthingIds = new ArrayList<>(); + if (params.get("eqIds") != null) { + eqIds = params.get("eqIds").toString(); + eqthingIds = Arrays.stream(eqIds.split(",")).map(s -> Long.parseLong(s.trim())).collect(Collectors.toList()); + params.put("eqIdList", eqthingIds); + } + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + int superAdmin = SecurityUser.getUser().getSuperAdmin(); + QueryWrapper queryWrapper = new QueryWrapper(); + //为超管时,并且还没切换到租户时则不过滤 + if(superAdmin == SuperAdminEnum.YES.value() && CompareUtil.compare(tenantCode, SecurityUser.getUser().getTenantCode()) == 0){ + + }else { + if(ObjectUtil.isNotEmpty(tenantCode)){ + queryWrapper.eq(EqPartRecordEntity::getTenantCode, tenantCode); + } + } + if(ObjectUtil.isNotEmpty(tenantCode)){ + queryWrapper.in(EqPartRecordEntity::getEqPartId, eqthingIds); + } + List eqPartRecordDTOS = ConvertUtils.sourceToTarget(eqPartRecordDao.selectListExt(queryWrapper), EqPartRecordDTO.class); +// List eqPartRecordDTOS = eqPartRecordDao.getList(params); +// IPage eqPartRecordEntityPage = baseDao.selectPage(page, getWrapper(params)); +// List eqPartRecordDTOS = ConvertUtils.sourceToTarget(eqPartRecordEntityPage.getRecords(), EqPartRecordDTO.class); + for (EqPartRecordDTO eqPartRecordDTO : eqPartRecordDTOS) { + //获取保养or维修工单 + if (StringUtils.equals(eqPartRecordDTO.getType(), "1")) { + EqByDTO eqByDTO = eqByService.getByIdAs(eqPartRecordDTO.getEqOperateId(),EqByDTO.class); + if (eqByDTO != null) { + eqPartRecordDTO.setEqOperateNo(eqByDTO.getByNo()); + } + } else { + EqWxPlanDTO eqWxPlanDTO = eqWxPlanService.getByIdAs(eqPartRecordDTO.getEqOperateId(),EqWxPlanDTO.class); +// EqBxDTO eqBxDTO = eqBxService.get(eqPartRecordDTO.getEqOperateId()); + if (eqWxPlanDTO != null) { + eqPartRecordDTO.setEqOperateNo(eqWxPlanDTO.getWxNo()); + } + } + EqInfo thingsInfo = iotThingsService.getThingInfoNew(eqPartRecordDTO.getEqPartId()); + if (thingsInfo != null) { + SysDeptEntity sysDeptEntity = sysDeptService.getById(thingsInfo.getUseDeptId()); + thingsInfo.setUseDeptName(ObjectUtil.isNotNull(sysDeptEntity) ? sysDeptEntity.getName() : null); + eqPartRecordDTO.setThingsInfo(thingsInfo); + } + } + return new PageData<>(eqPartRecordDTOS, eqPartRecordDTOS.size()); + } + + @Override + public List getByEqById(Long byId) { + return eqPartRecordDao.getByEqById(byId); + } + + @Override + public int getByEqId(Long eqId, String type) { + return eqPartRecordDao.getByEqId(eqId,type); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/controller/EqPlanRemindRecordController.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/controller/EqPlanRemindRecordController.java new file mode 100644 index 0000000..6c4ccb5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/controller/EqPlanRemindRecordController.java @@ -0,0 +1,53 @@ +package com.thing.eq.eqremindrecord.controller; + + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqremindrecord.dto.EqPlanRemindRecordDTO; +import com.thing.eq.eqremindrecord.service.EqPlanRemindRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +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; + +import java.util.Map; + + +/** +* 计划到期提醒记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-26 +*/ +@RestController +@RequestMapping(Constant.API_BASE +"/eqplanremindrecord") +@Tag(name="设备-计划到期提醒记录") +public class EqPlanRemindRecordController { + @Autowired + private EqPlanRemindRecordService eqPlanRemindRecordService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "planNo", description = "单号"), + @Parameter(name = "userId", description = "人员") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqPlanRemindRecordService.page(params); + + return new Result>().ok(page); + } + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/dto/EqPlanRemindRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/dto/EqPlanRemindRecordDTO.java new file mode 100644 index 0000000..3d551f5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/dto/EqPlanRemindRecordDTO.java @@ -0,0 +1,45 @@ +package com.thing.eq.eqremindrecord.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 计划到期提醒记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-26 +*/ +@Data +@Schema(description = "计划到期提醒记录") +public class EqPlanRemindRecordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "类型0保养计划 1巡检计划") + private String type; + @Schema(description = "单号") + private String no; + @Schema(description = "计划开始时间") + private Date startTime; + @Schema(description = "计划截止时间") + private Date endTime; + @Schema(description = "人员id,多个,隔开") + private String userId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "租户编码") + private Long tenantCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/entity/EqPlanRemindRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/entity/EqPlanRemindRecordEntity.java new file mode 100644 index 0000000..2b39885 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/entity/EqPlanRemindRecordEntity.java @@ -0,0 +1,77 @@ +package com.thing.eq.eqremindrecord.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 计划到期提醒记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-26 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_plan_remind_record") +public class EqPlanRemindRecordEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 创建人 + */ + + private Long creator; + /** + * 创建时间 + */ + + private Date createDate; + /** + * 更新人 + */ + + private Long updater; + /** + * 更新时间 + */ + + private Date updateDate; + /** + * 类型0保养计划 1巡检计划 + */ + private String type; + /** + * 单号 + */ + private String no; + /** + * 计划开始时间 + */ + private Date startTime; + /** + * 计划截止时间 + */ + private Date endTime; + /** + * 人员id,多个,隔开 + */ + private String userId; + /** + * 部门id + */ +// + private Long deptId; + /** + * 租户编码 + */ +// + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/excel/EqPlanRemindRecordExcel.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/excel/EqPlanRemindRecordExcel.java new file mode 100644 index 0000000..13f779f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/excel/EqPlanRemindRecordExcel.java @@ -0,0 +1,46 @@ +package com.thing.eq.eqremindrecord.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 计划到期提醒记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-26 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqPlanRemindRecordExcel { + @Excel(name = "id", orderNum = "0") + private Long id; + @Excel(name = "创建人", orderNum = "1") + private Long creator; + @Excel(name = "创建时间", orderNum = "2") + private Date createDate; + @Excel(name = "更新人", orderNum = "3") + private Long updater; + @Excel(name = "更新时间", orderNum = "4") + private Date updateDate; + @Excel(name = "类型0保养计划 1巡检计划", orderNum = "5") + private String type; + @Excel(name = "单号", orderNum = "6") + private String no; + @Excel(name = "保养计划时间", orderNum = "7") + private Date planTime; + @Excel(name = "计划截止时间", orderNum = "8") + private Date endTime; + @Excel(name = "人员id,多个,隔开", orderNum = "9") + private String user; + @Excel(name = "部门id", orderNum = "10") + private Long deptId; + @Excel(name = "租户编码", orderNum = "11") + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/mapper/EqPlanRemindRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/mapper/EqPlanRemindRecordMapper.java new file mode 100644 index 0000000..6f88724 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/mapper/EqPlanRemindRecordMapper.java @@ -0,0 +1,20 @@ +package com.thing.eq.eqremindrecord.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqremindrecord.entity.EqPlanRemindRecordEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 计划到期提醒记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2021-11-26 +*/ +@Mapper +public interface EqPlanRemindRecordMapper extends PowerBaseMapper { + + List getList(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/EqPlanRemindRecordService.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/EqPlanRemindRecordService.java new file mode 100644 index 0000000..9a25c04 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/EqPlanRemindRecordService.java @@ -0,0 +1,20 @@ +package com.thing.eq.eqremindrecord.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqremindrecord.dto.EqPlanRemindRecordDTO; +import com.thing.eq.eqremindrecord.entity.EqPlanRemindRecordEntity; + +import java.util.Map; + +/** + * 计划到期提醒记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-26 + */ +public interface EqPlanRemindRecordService extends IBaseService { + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/impl/EqPlanRemindRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/impl/EqPlanRemindRecordServiceImpl.java new file mode 100644 index 0000000..9d6c9f8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/eqremindrecord/service/impl/EqPlanRemindRecordServiceImpl.java @@ -0,0 +1,53 @@ +package com.thing.eq.eqremindrecord.service.impl; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqremindrecord.dto.EqPlanRemindRecordDTO; +import com.thing.eq.eqremindrecord.entity.EqPlanRemindRecordEntity; +import com.thing.eq.eqremindrecord.mapper.EqPlanRemindRecordMapper; +import com.thing.eq.eqremindrecord.service.EqPlanRemindRecordService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 计划到期提醒记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-11-26 + */ +@Service +public class EqPlanRemindRecordServiceImpl extends BaseServiceImpl implements EqPlanRemindRecordService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); +// wrapper.eq(StringUtils.isNotBlank((String)params.get("planNo")), "no", params.get("planNo")); +// wrapper.like(StringUtils.isNotBlank((String)params.get("userId")), "user_id", params.get("userId")); + return wrapper; + } + + @Override + public PageData page(Map params) { +// //转换成like +// paramsToLike(params, "user"); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + params.put("no", params.get("planNo")); + params.put("userId", params.get("userId")); + //分页 + Page page = getPage(params); + //查询 + List list = mapper.getList(params); + List targetList = ConvertUtils.sourceToTarget(list, EqPlanRemindRecordDTO.class); + return new PageData<>(targetList, page.getTotalRow()); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/controller/EqUserGroupController.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/controller/EqUserGroupController.java new file mode 100644 index 0000000..ca37d46 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/controller/EqUserGroupController.java @@ -0,0 +1,165 @@ +package com.thing.eq.equsergroup.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqcheck.dto.EqPatrolCheckPlanDTO; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.excel.EqUserGroupExcel; +import com.thing.eq.equsergroup.service.EqUserGroupService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 用户组 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2022-06-24 +*/ +@RestController +@RequestMapping("equsergroup/equsergroup") +@Tag(name="设备-用户组") +public class EqUserGroupController { + @Autowired + private EqUserGroupService eqUserGroupService; + @Autowired + private EqByPlanService eqByPlanService; + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "用户组名(模糊查询)"), + @Parameter(name = "userId", description = "用户id") + }) +// @RequiresPermissions("equsergroup:equsergroup:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = eqUserGroupService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("equsergroup:equsergroup:info") + public Result get(@PathVariable("id") Long id){ + EqUserGroupDTO data = eqUserGroupService.getById(id); + + return new Result().ok(data); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "name", description = "用户组名(模糊查询)"), + @Parameter(name = "userId", description = "用户id") + }) +// @RequiresPermissions("equsergroup:equsergroup:info") + public Result> getList(@Parameter(hidden = true) @RequestParam Map params){ + List data = eqUserGroupService.getList(params); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("equsergroup:equsergroup:save") + public Result save(@RequestBody EqUserGroupDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + if (StringUtils.isNotBlank(dto.getName())){ + EqUserGroupDTO eqUserGroupDTO = eqUserGroupService.getByName(dto.getName(),tenantCode); + if (eqUserGroupDTO !=null){ + throw new SysException("用户组名称不可重复"); + } + } + + eqUserGroupService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("equsergroup:equsergroup:update") + public Result update(@RequestBody EqUserGroupDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + if (StringUtils.isNotBlank(dto.getName())){ + EqUserGroupDTO eqUserGroupDTO = eqUserGroupService.getByNameNoCurrenId(dto.getName(),tenantCode,dto.getId()); + if (eqUserGroupDTO !=null){ + throw new SysException("用户组名称不可重复"); + } + } + eqUserGroupService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("equsergroup:equsergroup:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + for (Long id : ids){ + List byLists = eqByPlanService.getByGroupId(id,tenantCode); + List checkPlanDTOS = eqPatrolCheckPlanService.getByGroupId(id,tenantCode); + if (CollectionUtil.isNotEmpty(byLists) || CollectionUtil.isNotEmpty(checkPlanDTOS)){ + throw new SysException("有巡检计划或保养计划已选择用户组,不可删除"); + } + } + + eqUserGroupService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("equsergroup:equsergroup:export") + public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = eqUserGroupService.listAs(params,EqUserGroupDTO.class); + List list1 = ConvertUtils.sourceToTarget(list, EqUserGroupExcel.class); + ExcelUtils.exportExcel(list1,null, "用户组", EqUserGroupExcel.class,null,response); + +// ExcelUtils.exportExcelToTarget(response, null, "用户组", list, EqUserGroupExcel.class); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/dto/EqUserGroupDTO.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/dto/EqUserGroupDTO.java new file mode 100644 index 0000000..41df363 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/dto/EqUserGroupDTO.java @@ -0,0 +1,42 @@ +package com.thing.eq.equsergroup.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 用户组 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2022-06-24 +*/ +@Data +@Schema(description = "用户组") +public class EqUserGroupDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "usrid,多个,隔开") + private String userId; + @Schema(description = "用户组名") + private String name; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + + @Schema(description = "租户code") + private String userIdStr; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/entity/EqUserGroupEntity.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/entity/EqUserGroupEntity.java new file mode 100644 index 0000000..f4449df --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/entity/EqUserGroupEntity.java @@ -0,0 +1,66 @@ +package com.thing.eq.equsergroup.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 用户组 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2022-06-24 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_user_group") +public class EqUserGroupEntity { + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * usrid,多个,隔开 + */ + private String userId; + /** + * 用户组名 + */ + private String name; + /** + * 创建人 + */ + + private Long creator; + /** + * 创建时间 + */ + + private Date createDate; + /** + * 更新人 + */ + + private Long updater; + /** + * 更新时间 + */ + + private Date updateDate; + /** + * 部门id + */ + + private Long deptId; + /** + * 租户code + */ + + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/excel/EqUserGroupExcel.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/excel/EqUserGroupExcel.java new file mode 100644 index 0000000..1f914e4 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/excel/EqUserGroupExcel.java @@ -0,0 +1,41 @@ +package com.thing.eq.equsergroup.excel; + + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 用户组 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2022-06-24 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EqUserGroupExcel { + @Excel(name = "id", orderNum = "0") + private Long id; + @Excel (name = "usrid,多个,隔开", orderNum = "1") + private String userId; + @Excel (name = "用户组名", orderNum = "2") + private String name; + @Excel (name = "创建人", orderNum = "3") + private Long creator; + @Excel (name = "创建时间", orderNum = "4") + private Date createDate; + @Excel (name = "更新人", orderNum = "5") + private Long updater; + @Excel (name = "更新时间", orderNum = "6") + private Date updateDate; + @Excel (name = "部门id", orderNum = "7") + private Long deptId; + @Excel (name = "租户code", orderNum = "8") + private Long tenantCode; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/mapper/EqUserGroupMapper.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/mapper/EqUserGroupMapper.java new file mode 100644 index 0000000..862e1de --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/mapper/EqUserGroupMapper.java @@ -0,0 +1,29 @@ +package com.thing.eq.equsergroup.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.entity.EqUserGroupEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 用户组 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2022-06-24 +*/ +@Mapper +public interface EqUserGroupMapper extends PowerBaseMapper { + + List getListData(Map params); + + EqUserGroupDTO getByName(@Param("name") String name, @Param("tenantCode") Long tenantCode); + + EqUserGroupDTO getByNameNoCurrenId(@Param("name") String name, @Param("tenantCode") Long tenantCode, @Param("id") Long id); + + EqUserGroupDTO selectByid(Long id); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/EqUserGroupService.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/EqUserGroupService.java new file mode 100644 index 0000000..8ee16eb --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/EqUserGroupService.java @@ -0,0 +1,31 @@ +package com.thing.eq.equsergroup.service; + + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.entity.EqUserGroupEntity; + +import java.util.List; +import java.util.Map; + +/** + * 用户组 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2022-06-24 + */ +public interface EqUserGroupService extends IBaseService { + + EqUserGroupDTO getById(Long id); + + List getList(Map params); + + EqUserGroupDTO getByName(String name, Long tenantCode); + + EqUserGroupDTO getByNameNoCurrenId(String name, Long tenantCode, Long id); + + PageData page(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/impl/EqUserGroupServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/impl/EqUserGroupServiceImpl.java new file mode 100644 index 0000000..55246e9 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/equsergroup/service/impl/EqUserGroupServiceImpl.java @@ -0,0 +1,113 @@ +package com.thing.eq.equsergroup.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.equsergroup.dto.EqUserGroupDTO; +import com.thing.eq.equsergroup.entity.EqUserGroupEntity; +import com.thing.eq.equsergroup.mapper.EqUserGroupMapper; +import com.thing.eq.equsergroup.service.EqUserGroupService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 用户组 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2022-06-24 + */ +@Service +public class EqUserGroupServiceImpl extends BaseServiceImpl implements EqUserGroupService { + + @Autowired + private SysUserService sysUserService; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = getPage(params); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqUserGroupDTOs = mapper.getListData(params); + for (EqUserGroupDTO eqUserGroupDTO : eqUserGroupDTOs) { + String userId = eqUserGroupDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } + List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqUserGroupDTO.setUserIdStr(name); + } + return new PageData<>(eqUserGroupDTOs, eqUserGroupDTOs.size()); + } + + @Override + public EqUserGroupDTO getById(Long id) { + EqUserGroupDTO eqUserGroupDTO = mapper.selectByid(id); + //调整 + String userId = eqUserGroupDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } + List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqUserGroupDTO.setUserIdStr(name); + return eqUserGroupDTO; + } + + @Override + public List getList(Map params) { + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List eqUserGroupDTOs = mapper.getListData(params); + for (EqUserGroupDTO eqUserGroupDTO : eqUserGroupDTOs) { + String userId = eqUserGroupDTO.getUserId(); + String[] split = userId.split(","); + List longs = new ArrayList<>(); + for (String s : split) { + longs.add(new Long(s)); + } + List planUserName = sysUserService.getUserNameLists(longs); + String name=""; + for (String s : planUserName) { + name+=s+","; + } + name = name.substring(0, name.length() - 1); + eqUserGroupDTO.setUserIdStr(name); + } + return eqUserGroupDTOs; + } + + @Override + public EqUserGroupDTO getByName(String name, Long tenantCode) { + return mapper.getByName(name,tenantCode); + } + + @Override + public EqUserGroupDTO getByNameNoCurrenId(String name, Long tenantCode, Long id) { + return mapper.getByNameNoCurrenId(name,tenantCode,id); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/feign/DictTypeServiceFeign.java b/modules/equipment/src/main/java/com/thing/eq/feign/DictTypeServiceFeign.java new file mode 100644 index 0000000..1dafd54 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/feign/DictTypeServiceFeign.java @@ -0,0 +1,20 @@ +package com.thing.eq.feign;//package iot.thing.eq.feign; +// +//import iot.thing.common.utils.Result; +//import org.springframework.cloud.openfeign.FeignClient; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestMethod; +//import org.springframework.web.bind.annotation.RequestParam; +// +//import java.util.Map; +// +///** +// * @author zy +// * @version 1.0 +// * @date 2021/9/29 0029 14:55:20 +// */ +//@FeignClient(name = "ms-pwm-admin") +//public interface DictTypeServiceFeign { +// @RequestMapping(value = "/iot/dict/type/getDictListByDictTypeList",method = RequestMethod.GET) +// Result getDictListByDictTypeList(@RequestParam String dictTypeStr); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/feign/FileServiceFeign.java b/modules/equipment/src/main/java/com/thing/eq/feign/FileServiceFeign.java new file mode 100644 index 0000000..d6b8948 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/feign/FileServiceFeign.java @@ -0,0 +1,24 @@ +package iot.thing.eq.feign;//package iot.thing.eq.feign; +// +//import iot.thing.common.utils.Result; +//import iot.thing.eq.eqfilemanage.dto.DeleteFileParam; +//import org.springframework.cloud.openfeign.FeignClient; +//import org.springframework.http.MediaType; +//import org.springframework.web.bind.annotation.*; +//import org.springframework.web.multipart.MultipartFile; +// +//import java.util.List; +// +///** +// * @author zy +// * @version 1.0 +// * @date 2021/9/29 0029 14:55:20 +// */ +//@FeignClient(name = "ms-pwm-admin") +//public interface FileServiceFeign { +// @RequestMapping(value = "/sys/oss/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE) +// Result uploadFile(@RequestPart("file") MultipartFile file); +// +// @RequestMapping(value = "/sys/oss/deleteFile", method = RequestMethod.POST) +// Result deleteFile(@RequestBody DeleteFileParam deleteFileParam); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/feign/IotThingsServiceFeign.java b/modules/equipment/src/main/java/com/thing/eq/feign/IotThingsServiceFeign.java new file mode 100644 index 0000000..1fdc64c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/feign/IotThingsServiceFeign.java @@ -0,0 +1,25 @@ +package iot.thing.eq.feign;//package iot.thing.eq.feign; +// +//import iot.thing.common.utils.Result; +//import org.springframework.cloud.openfeign.FeignClient; +//import org.springframework.web.bind.annotation.PathVariable; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestMethod; +//import org.springframework.web.bind.annotation.RequestParam; +// +///** +// * @author zy +// * @version 1.0 +// * @date 2021/10/11 0011 17:14:07 +// */ +//@FeignClient(name = "ms-pwm-admin") +//public interface IotThingsServiceFeign { +// @RequestMapping(value = "/things/things/{id}",method = RequestMethod.GET) +// Result getThingById(@PathVariable("id") Long id); +// +// @RequestMapping(value = "/things/thingsrelation/getRelationByThingId",method = RequestMethod.GET) +// Result getRelationByThingId(@RequestParam Long relationTypeId, @RequestParam Long thingId); +// +// @RequestMapping(value = "/things/thingsrelation/getDeviceTypeByThingId",method = RequestMethod.GET) +// Result getDeviceTypeThingByThingId(@RequestParam Long relationTypeId, @RequestParam Long thingId); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/feign/TreeService.java b/modules/equipment/src/main/java/com/thing/eq/feign/TreeService.java new file mode 100644 index 0000000..eb662a2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/feign/TreeService.java @@ -0,0 +1,24 @@ +package iot.thing.eq.feign;//package iot.thing.eq.feign; +// +//import iot.thing.common.utils.Result; +//import org.springframework.cloud.openfeign.FeignClient; +//import org.springframework.web.bind.annotation.RequestMapping; +//import org.springframework.web.bind.annotation.RequestMethod; +//import org.springframework.web.bind.annotation.RequestParam; +// +//import java.util.List; +//import java.util.Map; +// +///** +// * @author zy +// * @version 1.0 +// * @date 2021/9/29 0029 14:55:20 +// */ +//@FeignClient(name = "ms-pwm-admin") +//public interface TreeService { +// @RequestMapping(value = "/tree/getThingsTreeByToId",method = RequestMethod.GET) +// Result getChildThingsId(@RequestParam Map params); +// +// @RequestMapping(value = "/tree/getChildTreeByToId",method = RequestMethod.GET) +// Result getChildTreeByToId(@RequestParam Map params); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/controller/FileController.java b/modules/equipment/src/main/java/com/thing/eq/file/controller/FileController.java new file mode 100644 index 0000000..9407586 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/controller/FileController.java @@ -0,0 +1,32 @@ +package com.thing.eq.file.controller; + + +import com.thing.common.core.constants.Constant; +import com.thing.eq.file.service.FileService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.poi.ss.formula.functions.T; +import org.springframework.beans.factory.annotation.Autowired; +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; + +@Tag(name = "设备-文档接口") +@RequestMapping(Constant.API_BASE + "/file") +@RestController +public class FileController { + @Autowired + FileService fileService; + + @PostMapping("/upload") + @Operation(summary = "文件上传") + public String upload(@RequestParam(value = "file", required = false) MultipartFile file, T t) { + return fileService.upload(file, t); + } + + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/entiy/EqAttacmentEntity.java b/modules/equipment/src/main/java/com/thing/eq/file/entiy/EqAttacmentEntity.java new file mode 100644 index 0000000..8c6dd6b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/entiy/EqAttacmentEntity.java @@ -0,0 +1,65 @@ +package com.thing.eq.file.entiy; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.Date; + +/** + * 附件 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2021-09-29 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("eq_attacment") +public class EqAttacmentEntity { + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 设备id/维修id/保养id...各种外键id + */ + private Long thingId; + /** + * 创建者 + */ + + private Long creator; + /** + * 创建时间 + */ + + private Date createDate; + /** + * 更新者 + */ + + private Long updater; + /** + * 更新时间 + */ + + private Date updateDate; + /** + * 图片或文件url + */ + private String url; + /** + * 0图片,1附件 + */ + private String type; + /** + * 模块:0设备管理 1能源管理 + */ + private String modularType; + + /** + * 名称 + */ + private String name; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/entiy/FileData.java b/modules/equipment/src/main/java/com/thing/eq/file/entiy/FileData.java new file mode 100644 index 0000000..75c1fc5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/entiy/FileData.java @@ -0,0 +1,16 @@ +package iot.thing.eq.file.entiy; + +import lombok.Data; + +@Data +public class FileData { + private Long id; + + private String moduleType; + + private String textType; + + private String photoType; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/mapper/FileMapper.java b/modules/equipment/src/main/java/com/thing/eq/file/mapper/FileMapper.java new file mode 100644 index 0000000..f283317 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/mapper/FileMapper.java @@ -0,0 +1,16 @@ +package com.thing.eq.file.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface FileMapper extends PowerBaseMapper { + List selectUrlList(@Param("thingId") Long thingId, + @Param("type") String type); + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/service/FileService.java b/modules/equipment/src/main/java/com/thing/eq/file/service/FileService.java new file mode 100644 index 0000000..79d52d7 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/service/FileService.java @@ -0,0 +1,23 @@ +package com.thing.eq.file.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + + +public interface FileService extends IBaseService { + String upload(@RequestParam(value = "file", required = false) MultipartFile file, T t); + + String uploadText(List imageUrls, List attachmentUrls, Long id); + + Map> getDocument(Long id); + + void deleteDocument(Long id); + + List getDocumentById(Long id); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/file/service/impl/FileServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/file/service/impl/FileServiceImpl.java new file mode 100644 index 0000000..3af7f28 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/file/service/impl/FileServiceImpl.java @@ -0,0 +1,135 @@ +package com.thing.eq.file.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqmanager.dto.EqAttacmentDTO; +import com.thing.eq.eqmanager.entity.EqAttacmentEntity; +import com.thing.eq.file.mapper.FileMapper; +import com.thing.eq.file.service.FileService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +public class FileServiceImpl extends BaseServiceImpl implements FileService { + @Autowired + FileService fileService; + @Autowired + FileMapper fileDao; + + @Override + public String upload(MultipartFile file, T t) { + Class aClass = t.getClass(); + String name = aClass.getSimpleName(); + String[] dtos = name.split("DTO"); + String s = dtos[0].toLowerCase(); + System.out.println("s = " + s); + + if (file.isEmpty()) { + return "文件为空"; + } + String fileName = file.getOriginalFilename(); + String suffixName = fileName.substring(fileName.lastIndexOf(".")); + String filePath = "D://"; + File dest = new File(filePath + fileName); + if (!dest.getParentFile().exists()) { + dest.getParentFile().mkdir(); + } + try { + file.transferTo(dest); + return "上传成功"; + } catch (IOException e) { + e.printStackTrace(); + } + return "上传失败"; + } + + + public String uploadText(List imageUrls, List attachmentUrls, Long id) { + List attacmentDTOS = new ArrayList<>(); + int num = 0; + StringBuffer stringBuffer = new StringBuffer(); + if (CollectionUtil.isNotEmpty(imageUrls)) { + for (EqAttacmentDTO url : imageUrls) { + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + attacmentDTO.setThingId(id); + attacmentDTO.setUrl(url.getUrl()); + attacmentDTO.setType("0"); + attacmentDTO.setModularType("0"); + attacmentDTO.setName(url.getName()); + attacmentDTOS.add(attacmentDTO); + if (num == 0) { + stringBuffer.append(url); + } + num++; + } + } + if (CollectionUtil.isNotEmpty(attachmentUrls)) { + for (EqAttacmentDTO url : attachmentUrls) { + EqAttacmentDTO attacmentDTO = new EqAttacmentDTO(); + attacmentDTO.setThingId(id); + attacmentDTO.setUrl(url.getUrl()); + attacmentDTO.setName(url.getName()); + attacmentDTO.setType("1"); + attacmentDTO.setModularType("0"); + attacmentDTOS.add(attacmentDTO); + } + } + fileService.saveBatch(ConvertUtils.sourceToTarget(attacmentDTOS, EqAttacmentEntity.class)); + return stringBuffer.toString(); + } + + @Override + public QueryWrapper getWrapper(Map params) { + return null; + } + + public Map> getDocument(Long id) { + List imageUrls = fileDao.selectUrlList(id, "0"); + ArrayList eqAttacmentDTOS = new ArrayList<>(); + if (imageUrls != null && imageUrls.size() > 0) { + for (EqAttacmentEntity entity : imageUrls) { + EqAttacmentDTO dto = new EqAttacmentDTO(); + BeanUtils.copyProperties(entity, dto); + eqAttacmentDTOS.add(dto); + } + } + List attachmentUrls = fileDao.selectUrlList(id, "1"); + ArrayList eqAttacmentDTOS2 = new ArrayList<>(); + if (attachmentUrls != null && attachmentUrls.size() > 0) { + for (EqAttacmentEntity entity : attachmentUrls) { + EqAttacmentDTO dto = new EqAttacmentDTO(); + BeanUtils.copyProperties(entity, dto); + eqAttacmentDTOS2.add(dto); + } + } + HashMap> map = new HashMap<>(); + map.put("image", eqAttacmentDTOS); + map.put("attachment", eqAttacmentDTOS2); + return map; + } + + + public void deleteDocument(Long id) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("thing_id",id); + fileDao.deleteByQuery(queryWrapper); + } + + @Override + public List getDocumentById(Long id) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("thing_id",id); + return mapper.selectListExt(queryWrapper); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/message/EmailMessage.java b/modules/equipment/src/main/java/com/thing/eq/message/EmailMessage.java new file mode 100644 index 0000000..9fed79f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/message/EmailMessage.java @@ -0,0 +1,49 @@ +//package iot.thing.eq.message; +// +//import cn.hutool.core.collection.CollectionUtil; +//import iot.thing.modules.sys.dao.SysUserDao; +//import iot.thing.modules.sys.entity.SysUserEntity; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.mail.SimpleMailMessage; +//import org.springframework.mail.javamail.JavaMailSender; +//import org.springframework.stereotype.Component; +// +//import java.util.*; +// +//@Component +//public class EmailMessage { +// @Autowired +// private JavaMailSender javaMailSender; +// +// @Autowired +// private SysUserDao sysUserDao; +// +// public void sendMessage(Map map) { +// String desc = map.get("desc"); +// String userID = map.get("userId"); +// +// List userIdList = Arrays.asList(userID.split(",")); +// if (CollectionUtil.isNotEmpty(userIdList)) { +// return; +// } +// for (String userId : userIdList) { +// SysUserEntity sysUserEntity = sysUserDao.getById(Long.parseLong(userId)); +// if(Objects.isNull(sysUserEntity)){ +// continue; +// } +// String email = sysUserEntity.getEmail(); +// if (StringUtils.isNotEmpty(email)) { +// continue; +// } +// +// SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); +// simpleMailMessage.setSubject("设备故障"); +// simpleMailMessage.setFrom("1239135658@qq.com"); +// simpleMailMessage.setTo(email); +// simpleMailMessage.setSentDate(new Date()); +// simpleMailMessage.setText(desc); +// javaMailSender.send(simpleMailMessage); +// } +// } +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/controller/DeviceMonitorController.java b/modules/equipment/src/main/java/com/thing/eq/monitor/controller/DeviceMonitorController.java new file mode 100644 index 0000000..00d6104 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/controller/DeviceMonitorController.java @@ -0,0 +1,52 @@ +//package iot.thing.eq.monitor.controller; +// +// +//import iot.thing.common.constant.Constant; +//import iot.thing.common.utils.params.Result; +//import iot.thing.eq.monitor.dto.DeviceMonitorDTO; +//import iot.thing.eq.monitor.service.DeviceMonitorService; +//import io.swagger.annotations.Api; +//import io.swagger.annotations.Parameter; +//import io.swagger.annotations.Parameters; +//import io.swagger.annotations.ApiOperation; +//import org.springframework.beans.factory.annotation.Autowired; +//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; +//import springfox.documentation.annotations.ApiIgnore; +// +//import java.util.List; +//import java.util.Map; +// +// +///** +// * 设备监控 +// * +// * @author CCL 1239135658@qq.com +// * @since 3.0 2021-10-19 +// */ +// +//@RestController +//@RequestMapping(Constant.API_BASE + "/devicemonitor") +//@Api(tags = "设备监控") +//public class DeviceMonitorController { +// @Autowired +// DeviceMonitorService deviceMonitorService; +// +// @GetMapping("page") +// @ApiOperation("水泵监控") +// @Parameters({ +// @Parameter(name = "deviceId", value = "设备id", paramType = "query",allowMultiple=true, dataType = "String"), +// @Parameter(name = "frequency", value = "设备频率类型", paramType = "query", dataType = "String"), +// @Parameter(name = "pressure", value = "出水压力类型", paramType = "query", dataType = "String"), +// @Parameter(name = "start", value = "当天开始的时间", paramType = "query", dataType = "String"), +// @Parameter(name = "end", value = "当天结束的时间", paramType = "query", dataType = "String"), +// }) +// public Result page(@ApiIgnore @RequestParam Map params) { +// List list = deviceMonitorService.selectAll(params); +// return new Result().ok(list); +// } +// +// +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/controller/ReadingRecordController.java b/modules/equipment/src/main/java/com/thing/eq/monitor/controller/ReadingRecordController.java new file mode 100644 index 0000000..cb02518 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/controller/ReadingRecordController.java @@ -0,0 +1,48 @@ +package com.thing.eq.monitor.controller; + + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.constants.Constant; +import com.thing.eq.monitor.dto.ReadingRecordDTO; +import com.thing.eq.monitor.service.ReadingRecordService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +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; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping(Constant.API_BASE + "/readingrecord") +@Tag(name = "抄表记录表") +public class ReadingRecordController { + @Autowired + ReadingRecordService readingRecordService; + + @GetMapping("queryYearMeterReadingRecord") + @Operation(summary = "年度每月抄表记录表") + @Parameters({ + @Parameter(name = "year", description = "年份"), + }) + public JSONObject queryYearMeterReadingRecord(@Parameter(hidden = true) @RequestParam Map params) { + //成品车间,芯片车间,冷却系统,生活用水 + params.put("code1", "B_11000654_1"); + params.put("code2", "B_11000657_1"); + params.put("code3", "B_11000660_1"); + params.put("code4", "B_V00001_7"); +// params.put("code1", "成品车间"); +// params.put("code2", "芯片车间"); +// params.put("code3", "冷却系统"); +// params.put("code4", "生活用水"); + List list = readingRecordService.queryYearMeterReadingRecord(params); + JSONObject jsonObject = new JSONObject(); + jsonObject.put("data", list); + return jsonObject; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/dto/DeviceMonitorDTO.java b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/DeviceMonitorDTO.java new file mode 100644 index 0000000..e0a88c8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/DeviceMonitorDTO.java @@ -0,0 +1,44 @@ +package com.thing.eq.monitor.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +@Data +@Schema(description = "水泵监控实体") +public class DeviceMonitorDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "设备名称") + private String entityName; + + @Schema(description = "设备编号") + private String entityCode; + + @Schema(description = "设备的id") + private String entityId; + + @Schema(description = "设备频率") + private Double frequency; + + @Schema(description = "出水压力") + private Double pressure; + + @Schema(description = "当日在线时间") + private Double onlineTime; + + @Schema(description = "当日离线时间") + private Double offlineTime; + + @Schema(description = "保养累计") + private Double maintainTotal; + + @Schema(description = "当日离线时间段") + private List offlinePeriod; + + @Schema(description = "当日在线时间段") + private List onlinePeriod; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordDTO.java b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordDTO.java new file mode 100644 index 0000000..0c66755 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordDTO.java @@ -0,0 +1,42 @@ +package com.thing.eq.monitor.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ReadingRecordDTO { + + @Schema(description = "年份") + private String year; + + @Schema(description = "设备名称") + private String eqName; + + @Schema(description = "读表名称 :底数,抄见,实用") + private String meterName; + + @Schema(description = "1月") + private Double jan = 0d; + @Schema(description = "2月") + private Double feb = 0d; + @Schema(description = "3月") + private Double mar = 0d; + @Schema(description = "4月") + private Double apr = 0d; + @Schema(description = "5月") + private Double may = 0d; + @Schema(description = "6月") + private Double june = 0d; + @Schema(description = "7月") + private Double july = 0d; + @Schema(description = "8月") + private Double aug = 0d; + @Schema(description = "9月") + private Double sept = 0d; + @Schema(description = "10月") + private Double oct = 0d; + @Schema(description = "11月") + private Double nov = 0d; + @Schema(description = "12月") + private Double dec = 0d; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordVO.java b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordVO.java new file mode 100644 index 0000000..93af7c7 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/dto/ReadingRecordVO.java @@ -0,0 +1,13 @@ +package com.thing.eq.monitor.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class ReadingRecordVO { + @Schema(description = "底数") + private Double base; + + @Schema(description = "抄见") + private Double baseCopy; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/entity/DeviceMonitorEntity.java b/modules/equipment/src/main/java/com/thing/eq/monitor/entity/DeviceMonitorEntity.java new file mode 100644 index 0000000..c6c309b --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/entity/DeviceMonitorEntity.java @@ -0,0 +1,5 @@ +//package iot.thing.eq.monitor.entity; +// +//public class DeviceMonitorEntity { +// +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/entity/ReadingRecordEntity.java b/modules/equipment/src/main/java/com/thing/eq/monitor/entity/ReadingRecordEntity.java new file mode 100644 index 0000000..06c1f80 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/entity/ReadingRecordEntity.java @@ -0,0 +1,5 @@ +package com.thing.eq.monitor.entity; + + +public class ReadingRecordEntity { +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/DeviceMonitorDao.java b/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/DeviceMonitorDao.java new file mode 100644 index 0000000..f130ab8 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/DeviceMonitorDao.java @@ -0,0 +1,17 @@ +//package iot.thing.eq.monitor.dao; +// +//import iot.thing.common.dao.BaseDao; +//import iot.thing.eq.eqby.dto.EqByDTO; +//import iot.thing.eq.eqmanager.entity.IotThingsEntity; +//import iot.thing.eq.monitor.entity.DeviceMonitorEntity; +//import org.apache.ibatis.annotations.Mapper; +//import org.apache.ibatis.annotations.Param; +// +//import java.util.List; +// +//@Mapper +//public interface DeviceMonitorDao extends BaseDao { +// List selectMaintainTotal(@Param("entityId") String entityId); +// +// IotThingsEntity selectEntityName(@Param("entityId") String entityId); +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/ReadingRecordMapper.java b/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/ReadingRecordMapper.java new file mode 100644 index 0000000..97439ce --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/mapper/ReadingRecordMapper.java @@ -0,0 +1,10 @@ +package com.thing.eq.monitor.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.monitor.entity.ReadingRecordEntity; +import org.apache.ibatis.annotations.Mapper; + + +@Mapper +public interface ReadingRecordMapper extends PowerBaseMapper { +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/service/DeviceMonitorService.java b/modules/equipment/src/main/java/com/thing/eq/monitor/service/DeviceMonitorService.java new file mode 100644 index 0000000..141b549 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/service/DeviceMonitorService.java @@ -0,0 +1,13 @@ +package com.thing.eq.monitor.service; + + + +import com.thing.eq.monitor.dto.DeviceMonitorDTO; + + +import java.util.List; +import java.util.Map; + +public interface DeviceMonitorService { + List selectAll(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/service/ReadingRecordService.java b/modules/equipment/src/main/java/com/thing/eq/monitor/service/ReadingRecordService.java new file mode 100644 index 0000000..cbf66c5 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/service/ReadingRecordService.java @@ -0,0 +1,12 @@ +package com.thing.eq.monitor.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.monitor.dto.ReadingRecordDTO; +import com.thing.eq.monitor.entity.ReadingRecordEntity; + +import java.util.List; +import java.util.Map; + +public interface ReadingRecordService extends IBaseService { + List queryYearMeterReadingRecord(Map params); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/DeviceMonitorServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/DeviceMonitorServiceImpl.java new file mode 100644 index 0000000..34e990a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/DeviceMonitorServiceImpl.java @@ -0,0 +1,252 @@ +//package iot.thing.eq.monitor.service.impl; +// +//import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +//import iot.thing.common.service.impl.CrudServiceImpl; +//import iot.thing.eq.monitor.dao.DeviceMonitorDao; +//import iot.thing.eq.monitor.dto.DeviceMonitorDTO; +//import iot.thing.eq.monitor.entity.DeviceMonitorEntity; +//import iot.thing.eq.monitor.service.DeviceMonitorService; +//import org.apache.commons.lang3.StringUtils; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Service; +//import org.thingsboard.server.common.data.id.EntityId; +// +//import java.text.ParseException; +//import java.text.SimpleDateFormat; +//import java.util.*; +// +//@Service +//public class DeviceMonitorServiceImpl extends CrudServiceImpl +// implements DeviceMonitorService { +//// @Autowired +//// RestClientUtils restClientUtils; +// +// @Autowired +// DeviceMonitorDao deviceMonitorDao; +// +// +// @Override +// public QueryWrapper getWrapper(Map params) { +// return null; +// } +// +// @Override +// public List selectAll(Map params) { +// String deviceId = (String) params.get("deviceId"); +// List deviceMonitorDTOS = new ArrayList<>(); +// if (StringUtils.isNotEmpty(deviceId) && deviceId.contains(",")) { +// String[] split = deviceId.split(","); +// for (String s : split) { +// DeviceMonitorDTO dto = this.getList(params, s); +// deviceMonitorDTOS.add(dto); +// } +// } else { +// DeviceMonitorDTO dto = this.getList(params, deviceId); +// deviceMonitorDTOS.add(dto); +// } +// return deviceMonitorDTOS; +// } +// +// public DeviceMonitorDTO getList(Map params, String deviceId) { +// String frequency = (String) params.get("frequency"); +// String pressure = (String) params.get("pressure"); +// String keyStr = frequency + "," + pressure; +// Long start = Long.valueOf((String) params.get("start")); +// Long end = Long.valueOf((String) params.get("end")); +// List keys = Arrays.asList(keyStr.split(",")); +// //todo +//// try { +//// EntityId entityId = new DeviceId(UUID.fromString(deviceId)); +//// List> timeseriesByTimeSlot = restClientUtils.getTimeseriesByTimeSlot(entityId, keys, +//// start - 6000, end, SortOrder.Direction.ASC); +//// DeviceMonitorDTO deviceMonitorDTO = new DeviceMonitorDTO(); +//// IotThingsEntity iotThingsEntity = deviceMonitorDao.selectEntityName(entityId.toString()); +//// Date createDate = null; +//// if (iotThingsEntity != null) { +//// deviceMonitorDTO.setEntityName(iotThingsEntity.getName()); +//// deviceMonitorDTO.setEntityCode(iotThingsEntity.getCode()); +//// createDate = iotThingsEntity.getCreateDate(); +//// } +//// this.setTotal(deviceMonitorDTO, entityId, createDate); +//// if (timeseriesByTimeSlot != null && timeseriesByTimeSlot.size() > 0) +//// for (Map stringObjectMap : timeseriesByTimeSlot) { +//// if (stringObjectMap.get("key").equals(frequency)) { +//// List> list = (List>) Convert.toList(stringObjectMap.get("list")); +//// List doubles = new ArrayList<>(); +//// for (Map objectMap : list) { +//// Double value = Double.parseDouble(objectMap.get("value").toString()); +//// doubles.add(value); +//// } +//// Double sumYield = doubles.stream().collect(Collectors.summarizingDouble(value -> value)).getSum(); +//// deviceMonitorDTO.setFrequency(sumYield); +//// } +//// if (pressure.contains("am")) { +//// String[] ams = pressure.split("am"); +//// pressure = ams[0]; +//// } +//// if (stringObjectMap.get("key").equals(pressure)) { +//// List> list = (List>) Convert.toList(stringObjectMap.get("list")); +//// Map stringStringMap = list.get(list.size() - 1); +//// Double value = Double.parseDouble(stringStringMap.get("value").toString()); +//// deviceMonitorDTO.setPressure(value); +//// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); +//// String startTime = dateFormat.format(start); +//// String endTime = dateFormat.format(new Date()); +//// List timeList = new ArrayList<>(); +//// for (Map objectMap : list) { +//// timeList.add((String) objectMap.get("time")); +//// } +//// SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); +//// String time = null; +//// if (startTime.equals(endTime)) { +//// time = timeFormat.format(new Date()); +//// } else { +//// time = timeFormat.format(new Date(end)); +//// } +//// String[] split1 = time.split("\\ ")[1].split(":"); +//// Integer size = (Integer.parseInt(split1[0]) * 4) + (Integer.parseInt(split1[1]) / 15); +//// deviceMonitorDTO.setEntityId(entityId.toString()); +////// deviceMonitorDTO.setOnlineTime(Double.parseDouble(String.valueOf((list.size() - 1) * 0.25))); +////// deviceMonitorDTO.setOfflineTime(Double.parseDouble(String.valueOf((size + 1 - list.size()) * 0.25))); +//// List timeList1 = this.getTimeList(start, size); +//// List timeList2 = new ArrayList<>(); +//// for (String s : timeList1) { +//// timeList2.add(s); +//// } +//// List timeList3 = timeList; +//// timeList1.removeAll(timeList); +//// timeList2.retainAll(timeList3); +//// deviceMonitorDTO.setOfflinePeriod(timeList1); +//// deviceMonitorDTO.setOnlinePeriod(timeList2); +//// if (deviceMonitorDTO.getOnlinePeriod() != null && deviceMonitorDTO.getOnlinePeriod().size() > 0) { +//// deviceMonitorDTO.setOnlineTime((deviceMonitorDTO.getOnlinePeriod().size() - 1) * 0.25); +//// } else { +//// deviceMonitorDTO.setOnlineTime(0d); +//// } +//// if (deviceMonitorDTO.getOfflinePeriod() != null && deviceMonitorDTO.getOfflinePeriod().size() > 0) { +//// deviceMonitorDTO.setOfflineTime((deviceMonitorDTO.getOfflinePeriod().size() - 1) * 0.25); +//// } else { +//// deviceMonitorDTO.setOfflineTime(0d); +//// } +//// this.getNewDto(entityId, keys, start, deviceMonitorDTO, startTime, endTime, pressure, frequency); +//// } +//// } +//// +//// return deviceMonitorDTO; +//// } catch (Exception e) { +//// return new DeviceMonitorDTO(); +//// } +// return null; +// } +// +// public List getTimeList(Long time, Integer size) { +// SimpleDateFormat timeFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +// long t = 900000; +// ArrayList longs = new ArrayList<>(); +// for (int i = 0; i <= size; i++) { +// longs.add(time + t * (i)); +// } +// ArrayList strings = new ArrayList<>(); +// for (Long aLong : longs) { +// Date date = new Date(aLong); +// String format = timeFormat.format(date); +// strings.add(format); +// } +// return strings; +// } +// +// public void getNewDto(EntityId entityId, List keys, Long start, +// DeviceMonitorDTO deviceMonitorDTO, String startTime, String endTime, String pressure, String frequency) { +// //todo +//// List> timeseriesByTimeSlot = restClientUtils.getTimeseriesByTimeSlot(entityId, keys, +//// start + 86400000 - 1000, start + 86400000 + 60000, SortOrder.Direction.ASC); +//// +//// +//// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); +//// SimpleDateFormat dateFormat2 = new SimpleDateFormat("yyyy-MM-dd"); +//// if (dateFormat2.format(new Date(start)).equals(dateFormat2.format(new Date()))) { +//// +//// } else { +//// if (deviceMonitorDTO.getOnlinePeriod().size() > 0) { +//// deviceMonitorDTO.setOnlineTime((deviceMonitorDTO.getOnlinePeriod().size()) * 0.25); +//// } +//// if (deviceMonitorDTO.getOfflinePeriod().size() > 0) { +//// deviceMonitorDTO.setOfflineTime((deviceMonitorDTO.getOfflinePeriod().size()) * 0.25); +//// } +//// } +//// +//// if (timeseriesByTimeSlot != null && timeseriesByTimeSlot.size() > 0) { +//// deviceMonitorDTO.getOnlinePeriod().add(dateFormat.format(start + 86400000)); +////// deviceMonitorDTO.setOnlineTime(deviceMonitorDTO.getOnlineTime() + 0.25); +//// for (Map stringObjectMap : timeseriesByTimeSlot) { +//// if (stringObjectMap.get("key").equals(frequency)) { +//// List> list = (List>) Convert.toList(stringObjectMap.get("list")); +//// if (list != null && list.size() > 0) { +//// Map stringStringMap = list.get(list.size() - 1); +//// Double value = Double.parseDouble(stringStringMap.get("value").toString()); +//// deviceMonitorDTO.setFrequency(deviceMonitorDTO.getFrequency() + value); +//// } +//// } +//// if (pressure.contains("am")) { +//// String[] ams = pressure.split("am"); +//// pressure = ams[0]; +//// } +//// if (stringObjectMap.get("key").equals(pressure)) { +//// List> list = (List>) Convert.toList(stringObjectMap.get("list")); +//// if (list != null && list.size() > 0) { +//// Map stringStringMap = list.get(list.size() - 1); +//// Double value = Double.parseDouble(stringStringMap.get("value").toString()); +//// deviceMonitorDTO.setPressure(value); +//// } +//// } +//// } +//// } else if (timeseriesByTimeSlot == null) { +//// if (!startTime.equals(endTime)) { +//// +//// deviceMonitorDTO.getOfflinePeriod().add(dateFormat.format(start + 86400000)); +////// deviceMonitorDTO.setOfflineTime(deviceMonitorDTO.getOfflineTime() + 0.25); +//// +//// } +//// } +// +// +// } +// +// public void setTotal(DeviceMonitorDTO deviceMonitorDTO, EntityId entityId, Date createDate) throws ParseException { +// //todo +//// List eqByDTOList = deviceMonitorDao.selectMaintainTotal(entityId.toString()); +//// SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm"); +//// if (eqByDTOList != null && eqByDTOList.size() > 0) { +//// Long time1 = null; +//// Long l = null; +//// if (eqByDTOList.get(eqByDTOList.size() - 1).getCreateDate() != null) { +//// time1 = dateFormat.parse(dateFormat.format(eqByDTOList.get(eqByDTOList.size() - 1).getCreateDate())).getTime(); +//// l = dateFormat.parse(dateFormat.format(new Date())).getTime(); +//// long time2 = Math.abs(time1 - l); +//// long minute = time2 / 60000; +//// BigDecimal bigDecimal = new BigDecimal(String.valueOf(minute)); +//// BigDecimal divide = bigDecimal.divide(new BigDecimal("60"), 2, BigDecimal.ROUND_HALF_UP); +//// deviceMonitorDTO.setMaintainTotal(divide.doubleValue()); +//// } else { +//// deviceMonitorDTO.setMaintainTotal(0d); +//// } +//// } else { +//// if (createDate != null) { +//// try { +//// String format = dateFormat.format(createDate); +//// String format2 = dateFormat.format(new Date()); +//// long time2 = Math.abs(dateFormat.parse(format).getTime() - dateFormat.parse(format2).getTime()); +//// long minute = time2 / 60000; +//// BigDecimal bigDecimal = new BigDecimal(String.valueOf(minute)); +//// BigDecimal divide = bigDecimal.divide(new BigDecimal("60"), 2, BigDecimal.ROUND_HALF_UP); +//// deviceMonitorDTO.setMaintainTotal(divide.doubleValue()); +//// } catch (Exception e) { +//// +//// } +//// } else { +//// deviceMonitorDTO.setMaintainTotal(0d); +//// } +//// } +// } +// +//} diff --git a/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/ReadingRecordServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/ReadingRecordServiceImpl.java new file mode 100644 index 0000000..a0e587f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/monitor/service/impl/ReadingRecordServiceImpl.java @@ -0,0 +1,160 @@ +package com.thing.eq.monitor.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.monitor.dto.ReadingRecordDTO; +import com.thing.eq.monitor.mapper.ReadingRecordMapper; +import com.thing.eq.monitor.entity.ReadingRecordEntity; +import com.thing.eq.monitor.service.ReadingRecordService; +import com.thing.eq.utils.DateSplitUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; + + +@Service +public class ReadingRecordServiceImpl extends BaseServiceImpl implements ReadingRecordService { +// @Autowired +// RestClientUtils restClientUtils; + @Autowired + private IotThingsService iotThingsService; + + @Override + public QueryWrapper getWrapper(Map params) { + return null; + } + + @Override + public List queryYearMeterReadingRecord(Map params) { + List dtoArrayList = new ArrayList<>(); + //todo + //年份 +// String year; +// String lastYear; +// if (params.get("year") != null){ +// year = params.get("year").toString(); +// }else{ +// //当前年份 +// SimpleDateFormat sdf = new SimpleDateFormat("yyyy"); +// Date date = new Date(); +// year = sdf.format(date); +// } +// lastYear = String.valueOf(Integer.parseInt(year)-1); +// Date startDate = DateSplitUtils.getStartDayOfYear(DateUtils.stringToDate(year,"yyyy")); +// Date endDate = DateSplitUtils.getEndDayOfYear(DateUtils.stringToDate(year,"yyyy")); +// //开始时间往前推一个月 +// Date startDate1 = DateUtils.addDateMonths(startDate,-1); +// List codes = new ArrayList<>(); +// codes.add(params.get("code1").toString()); +// codes.add(params.get("code2").toString()); +// codes.add(params.get("code3").toString()); +// codes.add(params.get("code4").toString()); +// for (String code :codes){ +// EqDTO eqDTO = iotThingsService.getThingsByCode(code); +// if (eqDTO == null ){ +// continue; +// } +// ReadingRecordDTO recordDTO1 = new ReadingRecordDTO(); +// recordDTO1.setYear(year); +// recordDTO1.setEqName(eqDTO.getName()); +// recordDTO1.setMeterName("底数"); +// ReadingRecordDTO recordDTO2 = new ReadingRecordDTO(); +// recordDTO2.setYear(year); +// recordDTO2.setEqName(eqDTO.getName()); +// recordDTO2.setMeterName("抄见"); +// ReadingRecordDTO recordDTO3 = new ReadingRecordDTO(); +// recordDTO3.setYear(year); +// recordDTO3.setEqName(eqDTO.getName()); +// recordDTO3.setMeterName("实用"); +// +//// EntityId entityId = new DeviceId(UUID.fromString("f429dd80-3aec-11ec-8bb7-51149f870848")); +// EntityId entityId = new DeviceId(UUID.fromString(eqDTO.getEntityId())); +// List keys = new ArrayList<>(); +// keys.add("B2mm"); +// List> timeseriesByTimeSlot = restClientUtils.getTimeseriesByTimeSlot(entityId, keys, +// startDate1.getTime() -1000, endDate.getTime(), SortOrder.Direction.ASC); +// if (timeseriesByTimeSlot != null && timeseriesByTimeSlot.size() > 0) { +// for (Map stringObjectMap : timeseriesByTimeSlot) { +// List> list = (List>) Convert.toList(stringObjectMap.get("list")); +// for (Map objectMap : list) { +// if (StringUtils.equals(objectMap.get("time").toString(),lastYear +"-12-01 00:00:00")){ +// recordDTO1.setJan(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-01-01 00:00:00")){ +// recordDTO1.setFeb(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setJan(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-02-01 00:00:00")){ +// recordDTO1.setMar(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setFeb(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-03-01 00:00:00")){ +// recordDTO1.setApr(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setMar(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-04-01 00:00:00")){ +// recordDTO1.setMar(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setApr(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-05-01 00:00:00")){ +// recordDTO1.setJune(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setMar(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-06-01 00:00:00")){ +// recordDTO1.setJuly(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setJune(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-07-01 00:00:00")){ +// recordDTO1.setAug(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setJuly(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-08-01 00:00:00")){ +// recordDTO1.setSept(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setAug(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-09-01 00:00:00")){ +// recordDTO1.setOct(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setSept(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-10-01 00:00:00")){ +// recordDTO1.setNov(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setOct(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-11-01 00:00:00")){ +// recordDTO1.setDec(Double.parseDouble(objectMap.get("value").toString())); +// recordDTO2.setNov(Double.parseDouble(objectMap.get("value").toString())); +// } +// if (StringUtils.equals(objectMap.get("time").toString(),year +"-12-01 00:00:00")){ +// recordDTO2.setDec(Double.parseDouble(objectMap.get("value").toString())); +// } +// } +// } +// } +// dtoArrayList.add(recordDTO1); +// dtoArrayList.add(recordDTO2); +// recordDTO3.setJan(recordDTO2.getJan()-recordDTO1.getJan()); +// recordDTO3.setFeb(recordDTO2.getFeb()-recordDTO1.getFeb()); +// recordDTO3.setMar(recordDTO2.getMar()-recordDTO1.getMar()); +// recordDTO3.setApr(recordDTO2.getApr()-recordDTO1.getApr()); +// recordDTO3.setMay(recordDTO2.getMay()-recordDTO1.getMay()); +// recordDTO3.setJune(recordDTO2.getJune()-recordDTO1.getJune()); +// recordDTO3.setJuly(recordDTO2.getJuly()-recordDTO1.getJuly()); +// recordDTO3.setAug(recordDTO2.getAug()-recordDTO1.getAug()); +// recordDTO3.setSept(recordDTO2.getSept()-recordDTO1.getSept()); +// recordDTO3.setOct(recordDTO2.getOct()-recordDTO1.getOct()); +// recordDTO3.setNov(recordDTO2.getNov()-recordDTO1.getNov()); +// recordDTO3.setDec(recordDTO2.getDec()-recordDTO1.getDec()); +// dtoArrayList.add(recordDTO3); +// } + return dtoArrayList; + } + + public static void main(String[] args) { + + System.out.println(DateSplitUtils.getStartDayOfYear(DateTimeUtils.stringToDate("2021","yyyy"))); + System.out.println(DateSplitUtils.getEndDayOfYear(DateTimeUtils.stringToDate("2021","yyyy"))); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyCheckController.java b/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyCheckController.java new file mode 100644 index 0000000..b5c91c2 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyCheckController.java @@ -0,0 +1,360 @@ +package com.thing.eq.normalize.controller; + +import cn.afterturn.easypoi.excel.ExcelImportUtil; +import cn.afterturn.easypoi.excel.entity.ImportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.normalize.dto.EnergyCheckDTO; +import com.thing.eq.normalize.entity.EnergyCheckEntity; +import com.thing.eq.normalize.excel.EnergyCheckExcel; +import com.thing.eq.normalize.service.EnergyCheckService; +import com.thing.eq.normalize.service.EnergyProjectService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.service.SysMailTemplateService; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.map.HashedMap; +import org.apache.commons.io.FilenameUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.multipart.MultipartHttpServletRequest; + +import java.io.*; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + + +/** + * @Description 能源检查 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@Slf4j +@RestController +@RequestMapping("/sys/energyCheck/energyCheck") +@Tag(name="能源检查") +public class EnergyCheckController { + @Autowired + private EnergyCheckService energyCheckService; + @Autowired + private SysMailTemplateService sysMailTemplateService; + @Autowired + private EnergyProjectService energyProjectService; + @Autowired + private SysUserService sysUserService; + + /** + * 查询 + * + * @param params + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "deptName", description = "部门"), + @Parameter(name = "startCheckTime", description = "开始检查时间"), + @Parameter(name = "endCheckTime", description = "结束检查时间"), + @Parameter(name = "status", description = "整改状态"), + }) +// @PreAuthorize("hasAuthority('energyCheck:energyCheck:page')") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = energyCheckService.page(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary= "信息") + public Result get(@PathVariable("id") Long id){ + EnergyCheckDTO data = energyCheckService.getByIdAs(id,EnergyCheckDTO.class); + + return new Result().ok(data); + } + + /** + * 保存 + * + * @param dto + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @PostMapping + @Operation(summary= "保存") + @LogOperation("保存") +// @PreAuthorize("hasAuthority('energyCheck:energyCheck:save')") + public Result save(@RequestBody EnergyCheckDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + EnergyCheckEntity energyCheckEntity = ConvertUtils.sourceToTarget(dto, EnergyCheckEntity.class); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + energyCheckEntity.setTenantCode(tenantCode); + UserDetail user = SecurityUser.getUser(); + energyCheckEntity.setCreator(user.getId()); + energyCheckService.saveDto(energyCheckEntity); + EnergyCheckDTO energyCheckDTO = energyCheckService.getName(dto.getDeviceName()); + //发送邮件给具有权限的人去审核 + //新建模板 + SysMailTemplateDTO mailDTO=new SysMailTemplateDTO(); + String name="审核提醒"; + String subject="您有新的能源检查问题待处理"; + String content="您有新的能源检查问题待处理"; + mailDTO.setName(name); + mailDTO.setSubject(subject); + mailDTO.setContent(content); + sysMailTemplateService.saveDto(mailDTO); + //获取这个模板的id + Long mailId=mailDTO.getId(); + //查询具有审核权限的人 通过部门名称查到负责人的id 进而查到负责人id和邮箱 + + List userEntityList=energyCheckService.selectUser(dto.getDeptName()); + //查询超级管理员 + List adminList=energyCheckService.selectAdminUser(SecurityUser.getTenantCode()); + + userEntityList.addAll(adminList); + Set set=new HashSet<>(userEntityList); + List userList=new ArrayList<>(set); + //发送邮件 + energyProjectService.sendCheckMail(userList,mailId); + + //发送站内消息 + //创建通知对象 + SysNoticeDTO noticeDTO=new SysNoticeDTO(); + noticeDTO.setTitle("您有新的能源检查问题待处理"); + noticeDTO.setContent("您有新的能源检查问题待处理"); + noticeDTO.setId(energyCheckDTO.getId()); + //取用户id 集合 + List userIdList=new ArrayList<>(); + for(int i=0;i map=new HashedMap(); + map.put("limit","10000"); + Result> result=page(map); + List list=result.getData().getList(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + String nowTime=sdf.format(new Date()); + if(ids.length==0){ + List excelList=new ArrayList<>(); + excelList.forEach(v->{ + EnergyCheckExcel excel=new EnergyCheckExcel(); + excel.setLocation(v.getLocation()); + excel.setDeptName(v.getDeptName()); + excel.setCheckTime(v.getCheckTime()); + excel.setCheckPeople(v.getCheckPeople()); + excel.setProblem(v.getProblem()); + excel.setRequirement(v.getRequirement()); + excel.setStatus(v.getStatus()); + excel.setChangeTime(v.getChangeTime()); + + excel.setImageUrl(v.getImageUrl()); + excelList.add(excel); +// excelList.add(excel); + }); + ExcelUtils.exportExcel(excelList,null, "能源检查记录-"+nowTime, EnergyCheckExcel.class,"能源检查记录-"+nowTime+".xls",response); +// ExcelUtils.exportExcelToTarget(response, "能源检查记录-"+nowTime, list, EnergyCheckExcel.class); + }else{ + List newList=new ArrayList<>(); + List idList=Arrays.asList(ids); +// if (CollectionUtil.isNotEmpty(idList)){ +// for (EnergyCheckDTO energyProjectDTO : list) { +// if (idList.contains(energyProjectDTO.getId())){ +// newList.add(energyProjectDTO); +// } +// } +// }else{ +// newList.addAll(list); +// } + idList.forEach(v->{ + newList.addAll(list.stream().filter(n->n.getId().equals(v)).collect(Collectors.toList())); + }); + List excelList=new ArrayList<>(); + for (EnergyCheckDTO v : newList) { + EnergyCheckExcel excel=new EnergyCheckExcel(); + excel.setLocation(v.getLocation()); + excel.setDeptName(v.getDeptName()); + Date checkTime = v.getCheckTime(); + SimpleDateFormat targetFormat = new SimpleDateFormat("yyyy-MM-dd"); + String formattedDate = targetFormat.format(checkTime); + SimpleDateFormat targetFormat1 = new SimpleDateFormat("yyyy-MM-dd"); + Date parse = null; + try { + parse = targetFormat1.parse(formattedDate); + excel.setCheckTime(parse); + } catch (ParseException e) { + e.printStackTrace(); + } + excel.setCheckPeople(v.getCheckPeople()); + excel.setProblem(v.getProblem()); + excel.setRequirement(v.getRequirement()); + excel.setStatus(v.getStatus()); + String format = DateTimeUtils.format(v.getChangeTime(), DateTimeUtils.DATE_PATTERN_STR); + excel.setChangeTime(format); + excel.setImageUrl(v.getImageUrl()); + excelList.add(excel); + } + newList.forEach(v->{ + +// excelList.add(excel); + }); + ExcelUtils.exportExcel(excelList,null, "能源检查记录-"+nowTime, EnergyCheckExcel.class,"能源检查记录-"+nowTime+".xls",response); +// CommonExcelUtils.exportExcel(excelList,null,"能源检查记录-"+nowTime,EnergyCheckExcel.class,"能源检查记录-"+nowTime,response); +// ExcelUtils.exportExcelToTarget(response, "能源检查记录-"+nowTime, excelList, EnergyCheckExcel.class); + } + } + + /** + * 上传 + * + * @param request + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @PostMapping("upload") + @Operation(summary= "上传") + @LogOperation("上传") + public Result upload(MultipartHttpServletRequest request) throws IOException { + MultipartFile fileImageUrl = request.getFile("imageUrl"); + if(null==fileImageUrl){ + return new Result().error("imageUrl不能为空"); + } + String extension = FilenameUtils.getExtension(fileImageUrl.getOriginalFilename()); + String imageUrl = OSSFactory.build().uploadSuffix(fileImageUrl.getBytes(), extension); + return new Result().ok(imageUrl); + } + + /** + * 导入 + * + * @param request + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Transactional + @PostMapping("download") + @Operation(summary= "导入") + @LogOperation("导入") +// @PreAuthorize("hasAuthority('energyCheck:energyCheck:download')") + public Result download(MultipartHttpServletRequest request) throws Exception { + MultipartFile file=request.getFile("file"); + InputStream in=file.getInputStream(); + if(file==null){ + return new Result().error("file不能为空"); + } + ImportParams params = new ImportParams(); + List list2 = ExcelImportUtil.importExcel(in,EnergyCheckExcel.class,params); + + for(EnergyCheckExcel bean:list2){ + boolean flagOne=(null==bean.getLocation()||"".equals(bean.getLocation())); + boolean flagTwo=(null==bean.getDeptName()||"".equals(bean.getDeptName())); + boolean flagThree=(null==bean.getCheckTime()||"".equals(bean.getCheckTime())); + boolean flagFour=(null==bean.getCheckPeople()||"".equals(bean.getCheckPeople())); + boolean flagFive=(null==bean.getRequirement()||"".equals(bean.getRequirement())); + boolean flagSix=(null==bean.getProblem()||"".equals(bean.getProblem())); + boolean flagSeven=(null==bean.getImageUrl()||"".equals(bean.getImageUrl())); + if(flagOne || flagTwo || flagThree || flagFour || flagFive || flagSix || flagSeven){ + return new Result().error("excel里面不能有空的数据!"); + } + EnergyCheckEntity entity= ConvertUtils.sourceToTarget(bean,EnergyCheckEntity.class); + energyCheckService.saveDto(entity); + } + return new Result(); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyProjectController.java b/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyProjectController.java new file mode 100644 index 0000000..60a7d3d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/controller/EnergyProjectController.java @@ -0,0 +1,379 @@ +package com.thing.eq.normalize.controller; + + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.normalize.dto.EnergyPeopleDTO; +import com.thing.eq.normalize.dto.EnergyProjectDTO; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.eq.normalize.entity.EnergyProjectEntity; +import com.thing.eq.normalize.excel.EnergyProjectExcel; +import com.thing.eq.normalize.service.EnergyCheckService; +import com.thing.eq.normalize.service.EnergyPeopleService; +import com.thing.eq.normalize.service.EnergyProjectService; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysRoleDataScopeService; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.service.SysMailTemplateService; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.map.HashedMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import java.text.SimpleDateFormat; +import java.util.*; + + +/** + * @Description 节能项目 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@RestController +@RequestMapping("/sys/energyProject/energyProject") +@Tag(name="节能项目") +public class EnergyProjectController { + @Autowired + private EnergyProjectService energyProjectService; + @Autowired + private EnergyPeopleService energyPeopleService; + @Autowired + private SysMailTemplateService sysMailTemplateService; + + @Autowired + private SysUserService sysUserService; + @Autowired + private EnergyCheckService energyCheckService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysRoleDataScopeService sysRoleDataScopeService; + @Autowired + private SysRoleUserService sysRoleUserService; + + /** + * 查询 + * + * @param params + * @return EnergyProjectDTO + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + + + @GetMapping("page") + @Operation(summary= "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "projectName", description = "项目名称"), + }) +// @PreAuthorize("hasAuthority('sys:energyProject:page')") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = energyProjectService.page(params); + List list=page.getList(); + list.forEach(v->{ + List peopleEntities= energyProjectService.queryAllPeopleById(v.getId()); + List dtoList=energyProjectService.getDTOList(peopleEntities); + v.setEnergyPeopleDTOList(dtoList); + }); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary= "信息") + public Result get(@PathVariable("id") Long id){ + EnergyProjectDTO data = energyProjectService.getByIdAs(id,EnergyProjectDTO.class); + return new Result().ok(data); + } + + + @GetMapping("getUser") + @Operation(summary= "返回用户") + public Result getUser(@RequestParam("id") Long id){ + List userList = energyProjectService.getUser(id); + return new Result().ok(userList); + } + + + /** + * 保存 + * + * @param dto + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Transactional + @PostMapping + @Operation(summary= "保存") + @LogOperation("保存") +// @PreAuthorize("hasAuthority('sys:energyProject:save')") + public Result save(@RequestBody EnergyProjectDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); +// dto.getEnergyPeopleDTOList().forEach(v->{ +// ValidatorUtils.validateEntity(v, UpdateGroup.class,DefaultGroup.class); +// }); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + dto.setTenantCode(tenantCode); + dto.setStatus("0"); + UserDetail user = SecurityUser.getUser(); + dto.setCreator(user.getId()); + energyProjectService.saveDto(dto); + EnergyProjectDTO energyProjectDTO = energyProjectService.getNameList(dto.getProjectName()); + + List peopleEntities=dto.getEnergyPeopleDTOList(); + peopleEntities.forEach(v->{ + v.setId(energyProjectDTO.getId()); + v.setTenantCode(tenantCode); + v.setDeptName(dto.getDepartment()); + energyPeopleService.saveDto(v); + }); + EnergyProjectEntity dtoa=energyProjectService.getById(energyProjectDTO.getId()); + + EnergyProjectDTO newDto= ConvertUtils.sourceToTarget(dtoa,EnergyProjectDTO.class); + + //推送给具有审核权限的人 + //新建模板 +// energyProjectService.sendAsyncMail(newDto); + + return new Result(); + } + + + + + /** + * 修改 + * + * @param dto + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @PutMapping + @Operation(summary= "修改") + @LogOperation("修改") +// @PreAuthorize("hasAuthority('sys:energyProject:update')") + public Result update(@RequestBody EnergyProjectDTO dto){ + Long id=dto.getId(); +// //效验数据 +// dto.getEnergyPeopleDTOList().forEach(v->{ +// ValidatorUtils.validateEntity(v,UpdateGroup.class,DefaultGroup.class); +// }); + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + for(int i=0;i userList=dto.getEnergyPeopleDTOList(); + userList.forEach(v->{ + v.setTenantCode(dto.getTenantCode()); + v.setDeptName(dto.getDepartment()); + v.setId(id); + energyPeopleService.saveDto(v); + }); + return new Result(); + } + + + /** + * 删除 + * + * @param ids + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @DeleteMapping() + @Operation(summary= "删除") + @LogOperation("删除") +// @PreAuthorize("hasAuthority('sys:energyProject:delete')") + @Transactional + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + energyProjectService.batchDelete(ids); + energyPeopleService.batchDelete(ids); + return new Result(); + } + + + /** + * 导出 + * + * @param ids + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @PostMapping("export") + @Operation(summary= "导出") + @LogOperation("导出") +// @PreAuthorize("hasAuthority('sys:energyProject:export')") + public void export(HttpServletResponse response, @RequestBody Long[] ids) throws Exception { + Map map=new HashedMap(); + map.put("limit","10000"); + Result> result=page(map); + List list=result.getData().getList(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); + // 取得文件名 + String nowTime = sdf.format(new Date()); + + if(ids.length==0){ + List newList=new ArrayList<>(); + List idList=Arrays.asList(ids); + if (CollectionUtil.isNotEmpty(idList)){ + for (EnergyProjectDTO energyProjectDTO : list) { + if (idList.contains(energyProjectDTO.getId())){ + newList.add(energyProjectDTO); + } + } + } + ExcelUtils.exportExcel(newList,null, "节能项目表-", EnergyProjectExcel.class,"节能项目表.xls",response); +// ExcelUtils.exportExcelToTarget(response, "节能项目表-"+nowTime, list, EnergyProjectExcel.class); + }else{ + List newList=new ArrayList<>(); + List idList=Arrays.asList(ids); + if (CollectionUtil.isNotEmpty(idList)){ + for (EnergyProjectDTO energyProjectDTO : list) { + if (idList.contains(energyProjectDTO.getId())){ + newList.add(energyProjectDTO); + } + } + }else{ + newList.addAll(list); + } + List energyProjectExcels = new ArrayList<>(); + for (EnergyProjectDTO energyProjectDTO : newList) { + EnergyProjectExcel energyProjectExcel = new EnergyProjectExcel(); + energyProjectExcel.setProjectName(energyProjectDTO.getProjectName()); + energyProjectExcel.setSaveEnergy(energyProjectDTO.getSaveEnergy()); + energyProjectExcel.setDepartment(energyProjectDTO.getDepartment()); + energyProjectExcel.setPeople(energyProjectDTO.getPeople()); + String format = DateTimeUtils.format(energyProjectDTO.getImplementTime(), DateTimeUtils.DATE_PATTERN_STR); +// Date time = DateTimeUtils.parse(format, DateTimeUtils.DATE_PATTERN_STR); + energyProjectExcel.setImplementTime(format); + energyProjectExcel.setSaveCost(energyProjectDTO.getSaveCost()); + energyProjectExcel.setDetail(energyProjectDTO.getDetail()); + energyProjectExcels.add(energyProjectExcel); + } + ExcelUtils.exportExcel(energyProjectExcels,null, "节能项目表", EnergyProjectExcel.class,"节能项目表.xls",response); +// ExcelUtils.exportExcelToTarget(response, "节能项目表-"+nowTime, newList, EnergyProjectExcel.class); + } + } + + + /** + * 查询实施人 + * + * @param params + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @GetMapping("query") + @Operation(summary= "查询") + @LogOperation("查询") + public Result> query(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = sysUserService.page(params); + return new Result>().ok(page); + } + + + /** + * 审核 + * + * @param id + * @param status + * @param remark + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @PostMapping("preview") + @Operation(summary= "审核") + @LogOperation("审核") +// @PreAuthorize("hasAuthority('sys:energyProject:check')") + public Result preview(@RequestParam Long id, @RequestParam String status,@RequestParam String remark){ + energyProjectService.preview(id,status,remark); + //将审核结果发邮件给实施人 + //新建模板 + SysMailTemplateDTO mailDTO=new SysMailTemplateDTO(); + String name="审核结果"; + if(status.equals("1")){ + String subject="您申请的节能项目审核已经通过"; + String content="您申请的节能项目审核已经通过"; + mailDTO.setSubject(subject); + mailDTO.setContent(content); + }else if(status.equals("2")){ + String subject="您申请的节能项目审核未通过"; + String content=remark; + mailDTO.setSubject(subject); + mailDTO.setContent(content); + } + mailDTO.setCreateDate(DateTimeUtils.dateToTimestamp(new Date())); + mailDTO.setName(name); + sysMailTemplateService.saveDto(mailDTO); +// SysMailTemplateDTO sysMailTemplateDTO = sysMailTemplateService.getName(); + //获取这个模板的id + Long mailId=mailDTO.getId(); + //查询需要发送的用户列表 + Long projectId=id; + List peopleDTOList=energyPeopleService.queryByProjectId(projectId); + //发送邮件 + energyProjectService.sendResultMail(peopleDTOList,mailId); + + //将审核结果通过站内消息发送给实施人 + //创建通知对象 + SysNoticeDTO noticeDTO=new SysNoticeDTO(); + if(status.equals("1")){ + noticeDTO.setTitle("您申请的节能项目审核已经通过"); + noticeDTO.setContent("您申请的节能项目审核已经通过"); + noticeDTO.setId(id); + }else if(status.equals("2")){ + noticeDTO.setTitle("您申请的节能项目审核未通过"); + noticeDTO.setContent(remark); + noticeDTO.setId(id); + } + //取用户id 集合 + List userIdList=new ArrayList<>(); + for(int i=0;i sysUserDTOList; + + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyPeopleDTO.java b/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyPeopleDTO.java new file mode 100644 index 0000000..25df4ca --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyPeopleDTO.java @@ -0,0 +1,52 @@ +package com.thing.eq.normalize.dto; + + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + + +/** + * 规章制度 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-15 + */ +@Data +@Schema(description = "实施人") +public class EnergyPeopleDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @NotBlank(message = "用户列表用户姓名不能为空",groups = DefaultGroup.class) + @Schema(description = "用户姓名/编号") + private String username; + + @NotBlank(message = "用户列表姓名不能为空",groups = DefaultGroup.class) + @Schema(description = "姓名") + private String realname; + + @NotBlank(message = "用户列表部门名字不能为空",groups = DefaultGroup.class) + @Schema(description = "部门名字") + private String deptName; + + @NotNull(message = "用户列表用户ID不能为空",groups = DefaultGroup.class) + @Schema(description = "用户ID ") + private Long userId; + + @NotBlank(message = "用户列表用户邮箱不能为空",groups = DefaultGroup.class) + @Schema(description = "用户邮箱 ") + private String email; + + /** + * 租户编码 + */ + private Long tenantCode; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyProjectDTO.java b/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyProjectDTO.java new file mode 100644 index 0000000..13aed61 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/dto/EnergyProjectDTO.java @@ -0,0 +1,81 @@ +package com.thing.eq.normalize.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.eq.normalize.dto.EnergyPeopleDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * 节能项目 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-19 + */ +@Data +@Schema(description = "节能项目") +public class EnergyProjectDTO implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + @Schema(description = "id") + private Long id; + + @NotBlank(message = "项目名称不能为空",groups = DefaultGroup.class) + @Schema(description = "项目名称") + private String projectName; + + @NotBlank(message = "部门不能为空",groups = DefaultGroup.class) + @Schema(description = "部门") + private String department; + + @NotBlank(message = "实施人不能为空",groups = DefaultGroup.class) + @Schema(description = "实施人") + private String people; + + @NotNull(message = "节能量不能为空",groups = DefaultGroup.class) + @Schema(description = "节能量") + private BigDecimal saveEnergy; + + @NotNull(message = "节约费用不能为空",groups = DefaultGroup.class) + @Schema(description = "节约费用") + private BigDecimal saveCost; + + @NotNull(message = "实施时间不能为空",groups = DefaultGroup.class) + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd") + @Schema(description = "实施时间") + private Date implementTime; + + @NotBlank(message = "实施详情不能为空",groups = DefaultGroup.class) + @Schema(description = "详情") + private String detail; + + + @Schema(description = "审核状态") + private String status; + + @Schema(description = "审核未通过备注") + private String remark; + +// @NotNull(message = "用户列表不能为空!!!!!!!!!!!",groups = DefaultGroup.class) + @Schema(description = "用户list") + private List energyPeopleDTOList; + + + private Long tenantCode; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyCheckEntity.java b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyCheckEntity.java new file mode 100644 index 0000000..e8b9722 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyCheckEntity.java @@ -0,0 +1,95 @@ +package com.thing.eq.normalize.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.apache.commons.lang3.time.DateUtils; + +import java.io.Serial; +import java.util.Date; + +/** + * 能源检查 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("sys_energy_check") +public class EnergyCheckEntity { + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + /** + * 设备位置 + */ + private String location; + /** + * 责任部门 + */ + private String deptName; + /** + * 检查时间 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd") + private Date checkTime; + /** + * 检查人员 + */ + private String checkPeople; + /** + * 存在问题 + */ + private String problem; + /** + * 整改要求 + */ + private String requirement; + /** + * 图片地址 + */ + private String imageUrl; + /** + * 整改状态 0:待整改 1:已整改 + */ + private String status; + /** + * 整改时间 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd") + private Date changeTime; + + /** + * 设备名称 + */ + private String deviceName; + /** + * 备注 + */ + private String remarks; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyPeopleEntity.java b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyPeopleEntity.java new file mode 100644 index 0000000..d60b0ad --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyPeopleEntity.java @@ -0,0 +1,73 @@ +package com.thing.eq.normalize.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 节能项目-用户关联 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-15 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("sys_energy_people") +public class EnergyPeopleEntity { + + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + /** + * 用户编号/用户姓名 + */ + private String username; + /** + * 姓名 + */ + private String realname; + /** + * 部门名称 + */ + private String deptName; +// /** +// * id +// */ +// @Id +// private Long id; + /** + * 用户id + */ + private Long userId; + /** + * 用户邮箱 + */ + private String email; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyProjectEntity.java b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyProjectEntity.java new file mode 100644 index 0000000..f777a4c --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/entity/EnergyProjectEntity.java @@ -0,0 +1,91 @@ +package com.thing.eq.normalize.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 节能项目 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-19 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table(value = "sys_energy_project") +public class EnergyProjectEntity { + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + +// /** +// * id +// */ +// @Id +// private Long id; + /** + * 项目名称 + */ + private String projectName; + /** + * 部门 + */ + private String department; + /** + * 实施人 + */ + private String people; + /** + * 节能量 + */ + private BigDecimal saveEnergy; + /** + * 节约费用 + */ + private BigDecimal saveCost; + /** + * 实施时间 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd") + private Date implementTime; + /** + * 详情 + */ + private String detail; + /** + * 审核状态 0:待审核 1:审核通过 2:审核未通过 + */ + private String status; + /** + * 审核未通过备注 + */ + private String remark; +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyCheckExcel.java b/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyCheckExcel.java new file mode 100644 index 0000000..e43f12a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyCheckExcel.java @@ -0,0 +1,52 @@ +package com.thing.eq.normalize.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; + +import java.util.Date; + +/** + * 能源检查 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EnergyCheckExcel { + @Excel(name = "设备位置", orderNum = "1") + private String location; + + @Excel(name = "责任部门", orderNum = "2") + private String deptName; + + @Excel(name = "检查时间", orderNum = "3" ) + private Date checkTime; + + @Excel(name = "检查人员", orderNum = "4") + private String checkPeople; + + @Excel(name = "存在问题", orderNum = "5") + private String problem; + + @Excel(name = "整改要求", orderNum = "6") + private String requirement; + + @Excel(name = "图片", orderNum = "7",type = 2,savePath = "/upload") + private String imageUrl; + + @Excel(name = "整改状态 0:待整改 1:已整改", orderNum = "8") + private String status; + + @Excel(name = "整改时间", orderNum = "9") + private String changeTime; + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyProjectExcel.java b/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyProjectExcel.java new file mode 100644 index 0000000..53633ad --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/excel/EnergyProjectExcel.java @@ -0,0 +1,46 @@ +package com.thing.eq.normalize.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 节能项目 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-19 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class EnergyProjectExcel { + @Excel(name = "项目名称", orderNum = "1") + private String projectName; + @Excel(name = "部门", orderNum = "2") + private String department; + @Excel(name = "实施人", orderNum = "3") + private String people; + @Excel(name = "节能量(tce)", orderNum = "4") + private BigDecimal saveEnergy; + @Excel(name = "节约费用(元)", orderNum = "5") + private BigDecimal saveCost; + @Excel(name = "实施时间", orderNum = "6") + @DateTimeFormat(pattern = "yyyy-MM-dd") + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd") + private String implementTime; + @Excel(name = "状态" , orderNum = "7") + private String status; + @Excel(name = "详情", orderNum = "8") + private String detail; + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyCheckMapper.java b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyCheckMapper.java new file mode 100644 index 0000000..6470901 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyCheckMapper.java @@ -0,0 +1,48 @@ +package com.thing.eq.normalize.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.normalize.dto.EnergyCheckDTO; +import com.thing.eq.normalize.entity.EnergyCheckEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @Description 能源检查 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@Mapper +public interface EnergyCheckMapper extends PowerBaseMapper { + + + /** + * 根据部门名字查询部门负责人 + * + * @param deptName + * @param tenantCode + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUser(@Param("deptName") String deptName, @Param("tenantCode") Long tenantCode); + /** + * 查询 + * + * @param params + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getList(Map params); + + List selectAdminUser(Long tenantCode); + + String getDeptName(Long newId); + + EnergyCheckDTO getName(String deviceName); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyPeopleMapper.java b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyPeopleMapper.java new file mode 100644 index 0000000..cc96387 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyPeopleMapper.java @@ -0,0 +1,22 @@ +package com.thing.eq.normalize.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * EnergyPeopleDao + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +@Mapper +public interface EnergyPeopleMapper extends PowerBaseMapper { + + List queryByProjectId(Long projectId); + + SysUserEntity queryUserById(Long userId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyProjectMapper.java b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyProjectMapper.java new file mode 100644 index 0000000..583825a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/mapper/EnergyProjectMapper.java @@ -0,0 +1,101 @@ +package com.thing.eq.normalize.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.eq.normalize.dto.EnergyProjectDTO; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.eq.normalize.entity.EnergyProjectEntity; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @Description 节能项目 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@Mapper +public interface EnergyProjectMapper extends PowerBaseMapper { + + + /** + * 根据权限查找具有审核权限的人 + * + * @param power + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUser(@Param("power") String power, @Param("tenantCode") Long tenantCode); +// and id in( +// select user_id from sys_role_user) where role_id in (select role_id from sys_role_menu) where menu_id in ( +// select id from sys_menu where url=#{power}) + + List selectUserByMenu(@Param("power") String power,@Param("tenantCode") Long tenantCode); + + + /** + * 根据id查询集合 + * + * @param ids + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getList(Long[] ids); + + + /** + * 查询全部数据 + * + * @return EnergyProjectEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getAllList(Long tenantCode); + + + /** + * 查询某个项目的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getUser(Long id); + + + /** + * 审核 + * + * @param id + * @param status + * @param remark + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + void preview(@Param("id") Long id, @Param("status") String status,@Param("remark") String remark); + + + /** + * 根据项目id查找所有的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List queryAllPeopleById(Long id); + + + List getResultList(Map params); + + List queryDeptIdList(@Param("department") String department, @Param("tenantCode") Long tenantCode); + + EnergyProjectDTO getNameList(@Param("projectName") String projectName); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyCheckService.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyCheckService.java new file mode 100644 index 0000000..d2bb0d1 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyCheckService.java @@ -0,0 +1,63 @@ +package com.thing.eq.normalize.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.normalize.dto.EnergyCheckDTO; +import com.thing.eq.normalize.entity.EnergyCheckEntity; +import com.thing.sys.biz.entity.SysUserEntity; + +import java.util.List; +import java.util.Map; + +/** + * 能源检查 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +public interface EnergyCheckService extends IBaseService { + + + /** + * 删除 + * + * @param ids + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + void deleteCheck(Long[] ids); + + + /** + * 根据部门名字查询部门负责人 + * + * @param deptName + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUser(String deptName); + + /** + * 查询个人数据或者所有数据 + * + * @param params + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + PageData page(Map params); + + /** + * 查询超级管理员 + * + * @param tenantCode + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectAdminUser(Long tenantCode); + + EnergyCheckDTO getName(String deviceName); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyPeopleService.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyPeopleService.java new file mode 100644 index 0000000..062b780 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyPeopleService.java @@ -0,0 +1,22 @@ +package com.thing.eq.normalize.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.normalize.dto.EnergyPeopleDTO; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.sys.biz.entity.SysUserEntity; + +import java.util.List; + +/** + * 节能项目用户关联 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +public interface EnergyPeopleService extends IBaseService { + + List queryByProjectId(Long projectId); + + SysUserEntity queryUserById(Long userId); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyProjectService.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyProjectService.java new file mode 100644 index 0000000..1a2bc8f --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/EnergyProjectService.java @@ -0,0 +1,168 @@ +package com.thing.eq.normalize.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.eq.normalize.dto.EnergyPeopleDTO; +import com.thing.eq.normalize.dto.EnergyProjectDTO; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.eq.normalize.entity.EnergyProjectEntity; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.notice.dto.SysNoticeDTO; +import org.springframework.scheduling.annotation.Async; + +import java.util.List; +import java.util.Map; + +/** + * @Description 节能项目 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +public interface EnergyProjectService extends IBaseService { + + PageData page(Map params); + + + Result updateStatus(EnergyProjectDTO dto); + + + /** + * 根据权限查找具有审核权限的人 + * + * @param power + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUser(String power); + + /** + * 发送邮件 + * + * @param sysUserEntityList + * @param mailId + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + + @Async + void sendCheckMail(List sysUserEntityList,Long mailId) ; + + + @Async + void sendAsyncMail(EnergyProjectDTO dto); + + /** + * 发送通知 + * + * @param noticeDTO + * @param userIdList + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + void sendNotic(SysNoticeDTO noticeDTO, List userIdList); + + + /** + * 发送结果邮件 + * + * @param peopleDTOList + * @param mailId + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + void sendResultMail(List peopleDTOList, Long mailId); + + /** + * 根据id查询集合 + * + * @param ids + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getList(Long[] ids); + + + /** + * 查询全部数据 + * + * @return EnergyProjectEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getAllList(Long tenantCode); + + /** + * 查询某个项目的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getUser(Long id); + + /** + * 审核 + * + * @param id + * @param status + * @param remark + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + void preview(Long id,String status,String remark); + + /** + * 根据项目id查找所有的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List queryAllPeopleById(Long id); + + /** + * 转换dto + * + * @param peopleEntities + * @return EnergyPeopleDTO + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List getDTOList(List peopleEntities); + + /** + * 查询用户 + * + * @param power + * @param tenantCode + * @return EnergyPeopleDTO + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUserByMenu(String power, Long tenantCode); + + /** + * 根据权限和租户编码查找具有审核权限的人 + * + * @param power + * @param tenantCode + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + List selectUserByPowerAndTenant(String power, Long tenantCode); + + List queryDeptIdList(String department, Long tenantCode); + + EnergyProjectDTO getNameList(String projectName); +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyCheckServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyCheckServiceImpl.java new file mode 100644 index 0000000..053ad4d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyCheckServiceImpl.java @@ -0,0 +1,193 @@ +package com.thing.eq.normalize.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.normalize.dto.EnergyCheckDTO; +import com.thing.eq.normalize.entity.EnergyCheckEntity; +import com.thing.eq.normalize.mapper.EnergyCheckMapper; +import com.thing.eq.normalize.service.EnergyCheckService; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysRoleDataScopeService; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Description 能源检查 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@Service +public class EnergyCheckServiceImpl extends BaseServiceImpl implements EnergyCheckService { + @Autowired + private EnergyCheckMapper energyCheckDao; + @Autowired + private SysRoleUserService sysRoleUserService; + @Autowired + private SysRoleDataScopeService sysRoleDataScopeService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysUserService sysUserService; + + @Override + public QueryWrapper getWrapper(Map params){ + String deptName = (String)params.get("deptName"); + String checkTime = (String)params.get("checkTime"); + + Date date= DateTimeUtils.parse(checkTime,DateTimeUtils.DATE_TIME_PATTERN_STR); + + String status = (String)params.get("status"); + String ids = (String) params.get("ids"); + QueryWrapper queryWrapper = new QueryWrapper(); + if(StringUtils.isNotBlank(deptName)){ + queryWrapper.like("dept_name", deptName); + } + queryWrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + if(StringUtils.isNotBlank(checkTime)){ + //工具累 字符串转为日期格式 + if(StringUtils.isNotBlank(checkTime)){ + queryWrapper.eq("check_time", DateTimeUtils.parse(checkTime,DateTimeUtils.DATE_PATTERN_STR)); + } + } + if(StringUtils.isNotBlank(status)){ + queryWrapper.eq("status", status); + } + if(StringUtils.isNotBlank(ids)){ + queryWrapper.in(EnergyCheckEntity::getId,Arrays.asList(ids.split(",")) + .stream() + .map(Long::valueOf) + .collect(Collectors.toList())); + } + return queryWrapper; + } + + @Override + public PageData page(Map params) { + + String deptId2= (String) params.get("deptName"); + if(StringUtils.isNotBlank(deptId2)){ + Long newId=Long.valueOf(deptId2); + String name=energyCheckDao.getDeptName(newId); + params.put("name",name); + } + + String ids= (String) params.get("ids"); + if(StringUtils.isNotBlank(ids)){ + List idsList=Arrays.asList(ids.split(",")); + List idsLongList=new ArrayList<>(); + idsList.forEach(v->{ + idsLongList.add(Long.valueOf(v)); + }); + params.put("id",idsLongList); + } + + //租户 + UserDetail user = SecurityUser.getUser(); + params.put(Constant.TENANT_CODE, TenantContext.getTenantCode(user)); +// params.put(Constant.TENANT_CODE, Constant.TENANT_CODE_NT); + //普通管理员,需要组织机构权限过滤 + List filterUserIdList = null; +// if (user.getSuperAdmin() == SuperAdminEnum.NO.value() && +// user.getSuperTenant() == SuperTenantEnum.NO.value()) { +// // 获取用户部门 +// Long deptId = user.getDeptId(); +// // 获取用户角色 +// List rolIdList = sysRoleUserService.getRoleIdList(user.getId()); +// if(!rolIdList.isEmpty()){ +// Long rolId = rolIdList.get(0); +// // 判断数据权限是自身还是所有 +// SysRoleDataScopeEntity dataScopeEntity = sysRoleDataScopeService.getOrgDataByRoleDept(rolId, deptId); +// if(dataScopeEntity != null) { +// filterUserIdList = new ArrayList<>(); +// // 仅查询自身审批数据 +// if(SysDataOwnEnum.PRIVATE == dataScopeEntity.getDataOwn()) { +// filterUserIdList.add(user.getId()); +// } else { +// // 查询部门+子部门人员所有审批数据 +// List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); +// deptIdList.add(deptId); +// filterUserIdList = sysUserService.getUserIdListByDeptId(deptIdList); +// } +// } +// } +// if(filterUserIdList==null){ +// return getPageData(Collections.emptyList(), 0, EnergyCheckDTO.class); +// } +// } + + //分页 + Page page = getPage(params); + // 获取用户部门 + Long deptId = user.getDeptId(); + // 查询部门+子部门人员所有审批数据 + List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); + deptIdList.add(deptId); + filterUserIdList = sysUserService.getUserIdListByDeptId(deptIdList); + params.put("filterUser", filterUserIdList); + //查询 + List list = mapper.getList(params); + return getPageData(list, list.size(), EnergyCheckDTO.class); + } + + @Override + public List selectAdminUser(Long tenantCode) { + return energyCheckDao.selectAdminUser(tenantCode); + } + + @Override + public EnergyCheckDTO getName(String deviceName) { + return mapper.getName(deviceName); + } + + /** + * 删除 + * + * @param ids + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public void deleteCheck(Long[] ids) { + for (Long id : ids) { + EnergyCheckDTO data = this.getByIdAs(id,EnergyCheckDTO.class); + if(data != null) { + //删除资源 + File file = new File(data.getImageUrl()); + if (file.exists()) { + file.delete(); + } + } + } + this.batchDelete(ids); + } + + + /** + * 根据部门名字查询部门负责人 + * + * @param deptName + * @return Result + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List selectUser(String deptName) { + return energyCheckDao.selectUser(deptName,SecurityUser.getTenantCode()); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyPeopleServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyPeopleServiceImpl.java new file mode 100644 index 0000000..1da954a --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyPeopleServiceImpl.java @@ -0,0 +1,51 @@ +package com.thing.eq.normalize.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.eq.normalize.mapper.EnergyPeopleMapper; +import com.thing.eq.normalize.service.EnergyPeopleService; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 节能项目用户关联 + * + * @author wzf wzf@gmail.com + * @since 1.0.0 2020-08-20 + */ +@Service +public class EnergyPeopleServiceImpl extends BaseServiceImpl implements EnergyPeopleService { + @Autowired + private EnergyPeopleMapper energyPeopleDao; + + @Override + public QueryWrapper getWrapper(Map params){ + String id = (String)params.get("id"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + if(StringUtils.isNotBlank(id)){ + wrapper.eq("id", id); + } + + return wrapper; + } + + + @Override + public List queryByProjectId(Long projectId) { + return energyPeopleDao.queryByProjectId(projectId) ; + } + + @Override + public SysUserEntity queryUserById(Long userId) { + return energyPeopleDao.queryUserById(userId); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyProjectServiceImpl.java b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyProjectServiceImpl.java new file mode 100644 index 0000000..f350d41 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/normalize/service/impl/EnergyProjectServiceImpl.java @@ -0,0 +1,442 @@ +package com.thing.eq.normalize.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.message.MessageData; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.ResultCommonUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.eq.normalize.dto.EnergyPeopleDTO; +import com.thing.eq.normalize.dto.EnergyProjectDTO; +import com.thing.eq.normalize.entity.EnergyPeopleEntity; +import com.thing.eq.normalize.entity.EnergyProjectEntity; +import com.thing.eq.normalize.mapper.EnergyProjectMapper; +import com.thing.eq.normalize.service.EnergyCheckService; +import com.thing.eq.normalize.service.EnergyProjectService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysRoleDataScopeEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.biz.service.SysRoleDataScopeService; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.service.SysMailTemplateService; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.notice.entity.SysNoticeEntity; +import com.thing.sys.notice.entity.SysNoticeUserEntity; +import com.thing.sys.notice.enums.NoticeReadStatusEnum; +import com.thing.sys.notice.service.SysNoticeService; +import com.thing.sys.notice.service.SysNoticeUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.websocket.WebSocketServer; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @Description 节能项目 + * @Author wzf + * @Date 2020/8/28 下午1:53 + **/ +@Slf4j +@Service +public class EnergyProjectServiceImpl extends BaseServiceImpl implements EnergyProjectService { + + @Autowired + private EnergyProjectMapper energyProjectDao; + @Autowired + private SysMailTemplateService sysMailTemplateService; + @Autowired + private WebSocketServer webSocketServer; + @Autowired + private SysNoticeService sysNoticeService; + @Autowired + private SysNoticeUserService sysNoticeUserService; + @Autowired + private SysRoleUserService sysRoleUserService; + @Autowired + private SysRoleDataScopeService sysRoleDataScopeService; + @Autowired + private SysDeptService sysDeptService; + @Autowired + private SysUserService sysUserService; + @Autowired + private EnergyCheckService energyCheckService; + + + @Override + public QueryWrapper getWrapper(Map params){ + String projectName = (String) params.get("projectName"); + String ids = (String) params.get("ids"); + QueryWrapper queryWrapper = new QueryWrapper(); + if(StringUtils.isNotBlank(projectName)){ + queryWrapper.like("project_name", projectName); + } + queryWrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + if(StringUtils.isNotBlank(ids)){ + queryWrapper.in(EnergyProjectEntity::getId, Arrays.asList(ids.split(",")) + .stream() + .map(Long::valueOf) + .collect(Collectors.toList())); + } + return queryWrapper; + } + + + + @Override + public PageData page(Map params) { + //转换成like + paramsToLike(params, "projectName"); + + //租户 + UserDetail user = SecurityUser.getUser(); + params.put(Constant.TENANT_CODE, TenantContext.getTenantCode(user)); + //普通管理员,需要组织机构权限过滤 + List filterUserIdList = null; +// if (user.getSuperAdmin() == SuperAdminEnum.NO.value() && +// user.getSuperTenant() == SuperTenantEnum.NO.value()) { +// // 获取用户部门 +// Long deptId = user.getDeptId(); +// // 获取用户角色 +// List rolIdList = sysRoleUserService.getRoleIdList(user.getId()); +// if(!rolIdList.isEmpty()){ +// Long rolId = rolIdList.get(0); +// // 判断数据权限是自身还是所有 +// SysRoleDataScopeEntity dataScopeEntity = sysRoleDataScopeService.getOrgDataByRoleDept(rolId, deptId); +// if(dataScopeEntity != null) { +// filterUserIdList = new ArrayList<>(); +// // 仅查询自身审批数据 +// if(SysDataOwnEnum.PRIVATE == dataScopeEntity.getDataOwn()) { +// filterUserIdList.add(user.getId()); +// } else { +// // 查询部门+子部门人员所有审批数据 +// List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); +// deptIdList.add(deptId); +// filterUserIdList = sysUserService.getUserIdListByDeptId(deptIdList); +// } +// } +// } +// if(filterUserIdList==null){ +// return getPageData(Collections.emptyList(), 0, EnergyProjectDTO.class); +// } +// } + //分页 + Page page = getPage(params); + // 获取用户部门 + Long deptId = user.getDeptId(); + // 查询部门+子部门人员所有审批数据 + List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); + deptIdList.add(deptId); + filterUserIdList = sysUserService.getUserIdListByDeptId(deptIdList); + params.put("filterUser", filterUserIdList); + //查询 + List list = mapper.getResultList(params); + return getPageData(list, list.size(), EnergyProjectDTO.class); + } + + + @Override + public Result updateStatus(EnergyProjectDTO dto) { + // 仅更新特定数据 + EnergyProjectDTO newDto = new EnergyProjectDTO(); + newDto.setId(dto.getId()); + newDto.setStatus(dto.getStatus()); + // 如果审核未通过,需要检查是否有备注信息 + if(dto.getStatus().equals("2")) { + if(StringUtils.isEmpty(dto.getRemark())) { + return ResultCommonUtils.error("请注明未通过原因"); + } + newDto.setRemark(dto.getRemark()); + } + //发送审核结果邮件 + this.updateDto(newDto); + return new Result(); + } + + + /** + * 根据权限查找具有审核权限的人 + * + * @param power + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List selectUser(String power) { + return energyProjectDao.selectUser(power,SecurityUser.getUser().getTenantCode()); + } + + public List selectUserTwo(String power,Long tenantCode) { + return energyProjectDao.selectUser(power,tenantCode); + } + + @Override + public List selectUserByMenu(String power, Long tenantCode) { + return energyProjectDao.selectUserByMenu(power,tenantCode); + } + + @Override + public List selectUserByPowerAndTenant(String power, Long tenantCode) { + return energyProjectDao.selectUserByMenu(power,tenantCode); + } + + @Override + public List queryDeptIdList(String department, Long tenantCode) { + return energyProjectDao.queryDeptIdList(department,tenantCode); + } + + @Override + public EnergyProjectDTO getNameList(String projectName) { + return energyProjectDao.getNameList(projectName); + } + + + /** + * 发送邮件 + * + * @param sysUserEntityList + * @param mailId + * @return dto + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public void sendCheckMail(List sysUserEntityList,Long mailId){ + + for(int i=0;i userEntityList=selectUserTwo(power,tenantCode); + //根据部门名字查询出部门id + + List deptList=queryDeptIdList(dto.getDepartment(),tenantCode); + SysDeptEntity entity=deptList.get(0); + //先筛选出本部门或者是父部门具有审核权限的人 + userEntityList=userEntityList.stream().filter(v->v.getDeptId().equals(entity.getPid())||v.getDeptId().equals(entity.getId())).collect(Collectors.toList()); + //然后再筛选出是否是所有数据的用户 + List resultUser=new ArrayList<>(); + for (SysUserEntity v : userEntityList) { + List rolIdList = sysRoleUserService.getRoleIdList(v.getId()); + Long rolId = rolIdList.get(0); + SysRoleDataScopeEntity dataScopeEntity = sysRoleDataScopeService.getOrgDataByRoleDept(rolId,v.getDeptId()); + if(dataScopeEntity!=null){ + resultUser.add(v); + } + } + //查询超级管理员 + List adminList=energyCheckService.selectAdminUser(SecurityUser.getTenantCode()); + //去重 并且得到最终需要发送的用户列表 + resultUser.addAll(adminList); + Set set=new HashSet<>(resultUser); + List userList=new ArrayList<>(set); + + + //发送邮件 + sendCheckMail(userList,mailId); + + + //发送站内消息 + //创建通知对象 + SysNoticeDTO noticeDTO=new SysNoticeDTO(); + noticeDTO.setTitle("您有新的节能项目待审核"); + noticeDTO.setContent("您有新的节能项目待审核"); + //取用户id 集合 + List userIdList=new ArrayList<>(); + for(int i=0;i userIdList) { + SysNoticeEntity entity = ConvertUtils.sourceToTarget(noticeDTO, SysNoticeEntity.class); + //更新发送者信息 + entity.setType(1); + entity.setSenderName(SecurityUser.getUser().getRealName()); + entity.setSenderDate(new Date()); + //消息通知入库 + sysNoticeService.saveDto(entity); + //我的消息通知入库 + noticeDTO.setId(entity.getId()); + userIdList.forEach(userId -> { + SysNoticeUserEntity noticeUser = new SysNoticeUserEntity() + .setNoticeId(noticeDTO.getId()) + .setReceiverId(userId) + .setReadStatus(NoticeReadStatusEnum.UNREAD.value()); + sysNoticeUserService.save(noticeUser); + }); + //通过WebSocket,提示选中用户,有新通知 + MessageData message = new MessageData().msg(noticeDTO.getTitle()); + webSocketServer.sendMessage(userIdList, message); + } + + /** + * 发送结果邮件 + * + * @param peopleDTOList + * @param mailId + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public void sendResultMail(List peopleDTOList, Long mailId) { + for(int i=0;i getList(Long[] ids) { + return energyProjectDao.getList(ids); + } + + /** + * 查询全部数据 + * + * @return EnergyProjectEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List getAllList(Long tenantCode) { + return energyProjectDao.getAllList(tenantCode); + } + + + /** + * 查询某个项目的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List getUser(Long id) { + return energyProjectDao.getUser(id); + } + + + /** + * 审核 + * + * @param id + * @param status + * @param remark + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public void preview(Long id,String status,String remark) { + energyProjectDao.preview(id,status,remark); + } + + /** + * 根据项目id查找所有的用户 + * + * @param id + * @return EnergyPeopleEntity + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List queryAllPeopleById(Long id) { + return energyProjectDao.queryAllPeopleById(id); + } + + + /** + * 转换dto + * + * @param peopleEntities + * @return EnergyPeopleDTO + * @Author wzf + * @Date 2020/9/17 下午1:30 + */ + @Override + public List getDTOList(List peopleEntities) { + List dtoList=new ArrayList<>(); + peopleEntities.forEach(v->{ + EnergyPeopleDTO dto=new EnergyPeopleDTO(); + dto.setId(v.getId()); + dto.setDeptName(v.getDeptName()); + dto.setRealname(v.getRealname()); + dto.setUsername(v.getUsername()); + dto.setEmail(v.getEmail()); + dto.setUserId(v.getUserId()); + dtoList.add(dto); + }); + + return dtoList; + } + + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/task/DeleteFileTask.java b/modules/equipment/src/main/java/com/thing/eq/task/DeleteFileTask.java new file mode 100644 index 0000000..f9f5a29 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/task/DeleteFileTask.java @@ -0,0 +1,77 @@ +package com.thing.eq.task; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.constants.Constant; +import com.thing.eq.eqfilemanage.entity.EqFileDeleteEntity; +import com.thing.eq.eqfilemanage.mapper.EqFileDeleteMapper; +import com.thing.eq.eqfilemanage.service.EqFileDeleteService; +import com.thing.sys.biz.service.SysParamsService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * @author zy + * @version 1.0 + * @date 2021/10/20 0020 17:17:19 + */ +@Component +@EnableScheduling +@Slf4j +public class DeleteFileTask { + + @Autowired + private EqFileDeleteService eqFileDeleteService; + + @Autowired + private SysParamsService sysParamsService; + + @Autowired + private EqFileDeleteMapper eqFileDeleteDao; + + //@Scheduled(cron = "0 0 0/1 * * ?") + public void run() { + log.info("定时查询待删除的文件列表"); + start(); + } + + public void start() { + + Map valueObject = sysParamsService.getValueObject(Constant.CLOUD_STORAGE_CONFIG_KEY, Map.class); + if (Objects.isNull(valueObject)) { + log.info("云存储配置参数:CLOUD_STORAGE_CONFIG_KEY不存在"); + return; + } + String localDomain = String.valueOf(valueObject.get("localDomain")); + if (Objects.isNull(valueObject.get("localDomain")) || StringUtils.isBlank(localDomain)) { + log.info("云存储配置参数-域名未配置"); + } + String localPath = String.valueOf(valueObject.get("localPath")); + if (Objects.isNull(valueObject.get("localPath")) || StringUtils.isBlank(localPath)) { + log.info("云存储配置参数-存储目录未配置"); + } + + List deleteEntityList = eqFileDeleteService.selectAll(); + if (CollectionUtil.isEmpty(deleteEntityList)) { + return; + } + + deleteEntityList.forEach(deleteEntity -> { + String url = StringUtils.isNotBlank(deleteEntity.getUrl()) ? deleteEntity.getUrl().replace(localDomain, "") : deleteEntity.getUrl(); + if (FileUtils.deleteQuietly(new File(localPath + url))) { + eqFileDeleteService.removeById(deleteEntity.getId()); + eqFileDeleteDao.deleteFile(deleteEntity.getUrl()); + } + }); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/task/EqRemindRecordTask.java b/modules/equipment/src/main/java/com/thing/eq/task/EqRemindRecordTask.java new file mode 100644 index 0000000..5c7445e --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/task/EqRemindRecordTask.java @@ -0,0 +1,167 @@ +/** + * Copyright (c) 2018 人人开源 All rights reserved. + *

+ * https://www.lrd.io + *

+ * 版权所有,侵权必究! + */ + +package com.thing.eq.task; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.extra.mail.MailUtil; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqmanager.service.IotThingsService; +import com.thing.eq.eqremindrecord.dto.EqPlanRemindRecordDTO; +import com.thing.eq.eqremindrecord.service.EqPlanRemindRecordService; +import com.thing.sys.biz.service.SysUserService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; + + +@Slf4j +@Component +@Configuration +@EnableScheduling +public class EqRemindRecordTask { + @Autowired + private EqByPlanService eqByPlanService; + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + @Autowired + private SysUserService sysUserService; + @Autowired + private IotThingsService iotThingsService; + @Autowired + private EqCheckSettingService eqCheckSettingService; + @Autowired + private EqPlanRemindRecordService eqPlanRemindRecordService; + /** + * 每2小时检查计划是否需要发送提醒消息(巡检计划,保养计划) + */ + //@Scheduled(cron = "0 0 0/2 * * ?") + public void sendMessage() { + Date curDate = new Date(); + log.info("计划告警记录推送开始:" + curDate); + //保养计划(未保养,启用) + String byStatus = "0"; + String planStatus = "0"; + List eqByPlansDTOS = eqByPlanService.getPlanList(byStatus,planStatus); + if (CollectionUtil.isNotEmpty(eqByPlansDTOS)){ + for (EqByPlanDTO eqByPlanDTO:eqByPlansDTOS){ + if (eqByPlanDTO.getPlanEndTime() !=null && System.currentTimeMillis() > eqByPlanDTO.getPlanEndTime().getTime()){ + continue; + } + if (StringUtils.isBlank(eqByPlanDTO.getRemindTime()) || eqByPlanDTO.getByPlanTime() == null){ + continue; + } + //处理保养计划 + handleByPlan(eqByPlanDTO); + } + } + //巡检计划启用 + String patrolPlanStatus = "ACTIVE"; + //未提醒过的计划 + String isRemind = "0"; + List list = eqPatrolCheckPlanService.getCheckPlanByStatus(patrolPlanStatus,isRemind); + if (CollectionUtil.isNotEmpty(list)){ + for (EqPatrolCheckPlanEntity plan:list){ + if (plan.getCheckEndTime() !=null && System.currentTimeMillis() > plan.getCheckEndTime().getTime()){ + continue; + } + if (StringUtils.isBlank(plan.getRemindTime()) || plan.getPlanStartTime() == null){ + continue; + } + //处理巡检计划 + handlePatrolPlan(plan); + } + } + log.info("计划告警记录推送结束:" + curDate); + } + + private void handlePatrolPlan(EqPatrolCheckPlanEntity plan) { + + int remindTime = Integer.valueOf(plan.getRemindTime()); + Long planStartTime = plan.getPlanStartTime().getTime(); + Long planEndTime = 0L; + if (plan.getCheckEndTime() != null){ + planEndTime = plan.getCheckEndTime().getTime(); + } + Long endTime; + if (StringUtils.equals("hour",plan.getRemindTimeUnit())){ + String endTimes = DateTimeUtils.addDateHours(plan.getPlanStartTime(),-remindTime); + endTime = DateTimeUtils.stringToDate(endTimes, DateTimeUtils.DATE_TIME_PATTERN_STR).getTime(); + }else{ + String endTimes = DateTimeUtils.addDateDays(plan.getPlanStartTime(),-remindTime); + endTime = DateTimeUtils.stringToDate(endTimes, DateTimeUtils.DATE_TIME_PATTERN_STR).getTime(); + } + //当前时间大于计划时间往前推设置的提醒时间(例如提醒时间2天,计划时间2021-11-01 00:00:00,则往前推2天即2021-10-30 00:00:00) + if (System.currentTimeMillis()> endTime && planStartTime < planEndTime){ + EqPlanRemindRecordDTO dto = new EqPlanRemindRecordDTO(); + dto.setType("1"); + dto.setNo(plan.getPatrolCheckPlanNo()); + dto.setStartTime(plan.getPlanStartTime()); + dto.setEndTime(plan.getCheckEndTime()); + dto.setUserId(plan.getUserId()); + dto.setTenantCode(plan.getTenantCode()); + dto.setDeptId(plan.getDeptId()); + eqPlanRemindRecordService.saveDto(dto); + eqPatrolCheckPlanService.updateIsRemind(plan.getId(),"1"); + } + } + + private void handleByPlan(EqByPlanDTO eqByPlanDTO) { + int remindTime = Integer.valueOf(eqByPlanDTO.getRemindTime()); + Long planStartTime = eqByPlanDTO.getByPlanTime().getTime(); + Long planEndTime = 0L; + if (eqByPlanDTO.getPlanEndTime() != null){ + planEndTime = eqByPlanDTO.getPlanEndTime().getTime(); + } + Long endTime; + if (StringUtils.equals("hour",eqByPlanDTO.getRemindTimeUnit())){ + String endTimes = DateTimeUtils.addDateHours(eqByPlanDTO.getByPlanTime(), -remindTime); + endTime = DateTimeUtils.stringToDate(endTimes, DateTimeUtils.DATE_TIME_PATTERN_STR).getTime(); + }else{ + String endTimes = DateTimeUtils.addDateDays(eqByPlanDTO.getByPlanTime(),-remindTime); + endTime = DateTimeUtils.stringToDate(endTimes, DateTimeUtils.DATE_TIME_PATTERN_STR).getTime(); + } + //当前时间大于计划时间往前推设置的提醒时间(例如提醒时间2天,计划时间2021-11-01 00:00:00,则往前推2天即2021-10-30 00:00:00) + if (System.currentTimeMillis()> endTime && planStartTime < planEndTime){ + EqPlanRemindRecordDTO dto = new EqPlanRemindRecordDTO(); + dto.setType("0"); + dto.setNo(eqByPlanDTO.getByNo()); + dto.setStartTime(eqByPlanDTO.getByPlanTime()); + dto.setEndTime(eqByPlanDTO.getPlanEndTime()); + dto.setUserId(eqByPlanDTO.getByUser()); + dto.setTenantCode(eqByPlanDTO.getTenantCode()); + dto.setDeptId(eqByPlanDTO.getDeptId()); + eqPlanRemindRecordService.saveDto(dto); + //更新计划为已提醒 + eqByPlanService.updateIsRemind(eqByPlanDTO.getId(),"1"); + } + } + + @Async + void sendPatrolPlanMessageToUser(String sb, String eqName) { + MailUtil.send(sb,"有巡检计划即将到期,请及时查看","设备:"+eqName,false,null); + } + + @Async + void sendByMessageToUser(String sb, String eqName) { + MailUtil.send(sb,"有保养计划即将到期,请及时查看","设备:"+eqName,false,null); + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/task/PlanTimeTask.java b/modules/equipment/src/main/java/com/thing/eq/task/PlanTimeTask.java new file mode 100644 index 0000000..c20d189 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/task/PlanTimeTask.java @@ -0,0 +1,280 @@ +package com.thing.eq.task; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.eq.checkresult.dto.CheckResultDTO; +import com.thing.eq.checkresult.service.CheckResultService; +import com.thing.eq.eqby.dto.EqByDTO; +import com.thing.eq.eqby.dto.EqByPlanDTO; +import com.thing.eq.eqby.mapper.EqByMapper; +import com.thing.eq.eqby.service.EqByPlanService; +import com.thing.eq.eqcheck.dto.EqCheckSettingDTO; +import com.thing.eq.eqcheck.entity.EqPatrolCheckPlanEntity; +import com.thing.eq.eqcheck.service.EqCheckSettingService; +import com.thing.eq.eqcheck.service.EqPatrolCheckPlanService; +import com.thing.eq.eqpalnrecord.dto.EqPlanRecordDTO; +import com.thing.eq.eqpalnrecord.service.EqPlanRecordService; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Component +@Configuration +@EnableScheduling +public class PlanTimeTask { + @Autowired + private EqByPlanService eqByPlanService; + @Autowired + private EqPatrolCheckPlanService eqPatrolCheckPlanService; + @Autowired + private EqByMapper eqByDao; + @Autowired + private EqCheckSettingService eqCheckSettingService; + @Autowired + private CheckResultService checkResultService; + @Autowired + private EqPlanRecordService eqPlanRecordService; + /** + * 每小时定时更新保养/巡检计划(多次)的计划开始时间,(单次)检查是否过期 + */ + //@Scheduled(cron = "0 0 0/1 * * ?") + public void send() { + Date curDate = new Date(); + log.info("计划定时更新时间开始:" + curDate); + //保养计划(启用) + String planStatus = "0"; + List eqByPlansDTOS = eqByPlanService.getPlanListByPlanStatusAndType(planStatus); + if (CollectionUtil.isNotEmpty(eqByPlansDTOS)){ + for (EqByPlanDTO eqByPlanDTO:eqByPlansDTOS){ + if (eqByPlanDTO.getByPlanTime()==null || eqByPlanDTO.getPlanEndTime() == null){ + continue; + } + List eqByDTOS = eqByDao.findByEqByPlanId(eqByPlanDTO.getId()); + //单次,检查保养记录是否存在 + if (StringUtils.equals("0",eqByPlanDTO.getLoopType())){ + Boolean byResult = true; + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime()==null){ + continue; + } + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < eqByPlanDTO.getPlanEndTime().getTime() ){ + byResult = false; + } + } + } + //如果单次计划在计划时间内未保养,则计划更新为禁用,并且插入数据 + if (curDate.getTime() > eqByPlanDTO.getPlanEndTime().getTime()){ + eqByPlanDTO.setPlanStatus("1"); + eqByPlanService.updateDto(eqByPlanDTO); + if (byResult){ + EqPlanRecordDTO dto = new EqPlanRecordDTO(); + dto.setPlanId(eqByPlanDTO.getId()); + dto.setPlanNo(eqByPlanDTO.getByNo()); + dto.setThingId(eqByPlanDTO.getThingsId()); + dto.setUserId(eqByPlanDTO.getByUser()); + dto.setPlanStartTime(eqByPlanDTO.getByPlanTime()); + dto.setPlanEndTime(eqByPlanDTO.getPlanEndTime()); + dto.setHandle("0"); + dto.setType("0"); + dto.setDeptId(eqByPlanDTO.getDeptId()); + dto.setTenantCode(eqByPlanDTO.getTenantCode()); + eqPlanRecordService.saveDto(dto); + } + } + }else{ + if (eqByPlanDTO.getByPlanTime().getTime() >= eqByPlanDTO.getPlanEndTime().getTime()){ + eqByPlanDTO.setPlanStatus("1"); + eqByPlanService.updateDto(eqByPlanDTO); + continue; + } + //判断是否到期,到期更新下次计划开始时间,并且更新提醒状态为未提醒,如果下次计划开始时间超过项目截止时间,则停用 + Date nextPlanTime = null; + if (StringUtils.isNotBlank(eqByPlanDTO.getCycleUnit()) && StringUtils.isNotBlank(eqByPlanDTO.getLoopCycle())) { + if (StringUtils.equals(eqByPlanDTO.getCycleUnit(), "day")){ + String nextPlanTimes = DateTimeUtils.addDateDays(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + }else{ + String nextPlanTimes = DateTimeUtils.addDateHours(eqByPlanDTO.getByPlanTime(), Integer.parseInt(eqByPlanDTO.getLoopCycle())); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + + //如果本次保养该设备没有保养,则插入数据 + Boolean byResult = true; + if (CollectionUtil.isNotEmpty(eqByDTOS)){ + for (EqByDTO eqByDTO : eqByDTOS){ + if (eqByDTO.getByStartTime()==null){ + continue; + } + if (eqByDTO.getByStartTime().getTime() >= eqByPlanDTO.getByPlanTime().getTime() && eqByDTO.getByStartTime().getTime() < nextPlanTime.getTime() ){ + byResult = false; + } + } + } + if (byResult && curDate.getTime()>= nextPlanTime.getTime()){ + EqPlanRecordDTO dto = new EqPlanRecordDTO(); + dto.setPlanId(eqByPlanDTO.getId()); + dto.setPlanNo(eqByPlanDTO.getByNo()); + dto.setThingId(eqByPlanDTO.getThingsId()); + dto.setUserId(eqByPlanDTO.getByUser()); + dto.setPlanStartTime(eqByPlanDTO.getByPlanTime()); + dto.setPlanEndTime(nextPlanTime); + dto.setHandle("0"); + dto.setType("0"); + dto.setDeptId(eqByPlanDTO.getDeptId()); + dto.setTenantCode(eqByPlanDTO.getTenantCode()); + eqPlanRecordService.saveDto(dto); + } + //当前时间大于等于下一次开始时间则更新 + if (curDate.getTime() >= nextPlanTime.getTime()){ + eqByPlanDTO.setByPlanTime(nextPlanTime); + eqByPlanDTO.setIsRemind("0"); + if (nextPlanTime.getTime() >= eqByPlanDTO.getPlanEndTime().getTime()){ + eqByPlanDTO.setPlanStatus("1"); + } + eqByPlanService.updateDto(eqByPlanDTO); + } + } + } + } + } + + //巡检计划(启用) + String patrolPlanStatus = "ACTIVE"; + List list = eqPatrolCheckPlanService.getCheckPlanByStatusAndType(patrolPlanStatus,""); + if (CollectionUtil.isNotEmpty(list)){ + for (EqPatrolCheckPlanEntity eqPatrolCheckPlanEntity : list){ + if (eqPatrolCheckPlanEntity.getPlanStartTime()==null || eqPatrolCheckPlanEntity.getCheckEndTime()==null){ + continue; + } + List eqCheckSettingDTOS = eqCheckSettingService.getSettingListByPatrolPlanId(eqPatrolCheckPlanEntity.getId()); + //单次 + if (StringUtils.equals("0",eqPatrolCheckPlanEntity.getType())){ + //如果单次巡检在计划时间内有设备存在没有巡检,则插入数据 + //检查该计划其他设备是否巡检过 + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + for (EqCheckSettingDTO settingDTO : eqCheckSettingDTOS){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanEntity.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanEntity.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < eqPatrolCheckPlanEntity.getCheckEndTime().getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + } + } + } + totalCheckIds.removeAll(checkIds); + LinkedHashSet noCheckIds = new LinkedHashSet<>(); + noCheckIds.addAll(totalCheckIds); + //在计划时间内巡检记录小于总设备数,记录 + if (curDate.getTime() >= eqPatrolCheckPlanEntity.getCheckEndTime().getTime()){ + eqPatrolCheckPlanEntity.setPlanStatus("INACTIVE"); + eqPatrolCheckPlanService.updateById(eqPatrolCheckPlanEntity); + if (noCheckIds.size()>0 ){ + for (Long thingId : noCheckIds){ + EqPlanRecordDTO dto = new EqPlanRecordDTO(); + dto.setPlanId(eqPatrolCheckPlanEntity.getId()); + dto.setPlanName(eqPatrolCheckPlanEntity.getName()); + dto.setPlanNo(eqPatrolCheckPlanEntity.getPatrolCheckPlanNo()); + dto.setThingId(thingId); + dto.setUserId(eqPatrolCheckPlanEntity.getUserId()); + dto.setPlanStartTime(eqPatrolCheckPlanEntity.getPlanStartTime()); + dto.setPlanEndTime(eqPatrolCheckPlanEntity.getCheckEndTime()); + dto.setHandle("0"); + dto.setType("1"); + dto.setDeptId(eqPatrolCheckPlanEntity.getDeptId()); + dto.setTenantCode(eqPatrolCheckPlanEntity.getTenantCode()); + eqPlanRecordService.saveDto(dto); + } + } + } + }else{ + if (eqPatrolCheckPlanEntity.getPlanStartTime().getTime() >= eqPatrolCheckPlanEntity.getCheckEndTime().getTime()){ + eqPatrolCheckPlanEntity.setPlanStatus("INACTIVE"); + eqPatrolCheckPlanService.updateById(eqPatrolCheckPlanEntity); + continue; + } + //判断是否到期,到期更新下次计划开始时间,并且更新提醒状态为未提醒,如果下次计划开始时间超过项目截止时间,则停用 + Date nextPlanTime = null; + if (!Objects.isNull(eqPatrolCheckPlanEntity.getCheckCycle()) && StringUtils.isNotBlank(eqPatrolCheckPlanEntity.getCheckCycleUnit())){ + Integer cycle = eqPatrolCheckPlanEntity.getCheckCycle(); + String unit = eqPatrolCheckPlanEntity.getCheckCycleUnit(); + if ("day".equals(unit)) { + String nextPlanTimes = DateTimeUtils.addDateDays(eqPatrolCheckPlanEntity.getPlanStartTime(), cycle); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } else if ("hour".equals(unit)) { + String nextPlanTimes = DateTimeUtils.addDateHours(eqPatrolCheckPlanEntity.getPlanStartTime(), cycle); + nextPlanTime = DateTimeUtils.stringToDate(nextPlanTimes, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + } + //如果本次巡检存在没有巡检,则插入数据 + LinkedHashSet totalCheckIds = new LinkedHashSet<>(); + LinkedHashSet checkIds = new LinkedHashSet<>(); + //检查该计划其他设备是否巡检过 + for (EqCheckSettingDTO settingDTO : eqCheckSettingDTOS){ + totalCheckIds.add(settingDTO.getThingId()); + List otherCount = checkResultService.getResultByThingIdAndCheckPlanId(settingDTO.getThingId(),eqPatrolCheckPlanEntity.getId()); + if (CollectionUtil.isNotEmpty(otherCount)){ + for (CheckResultDTO checkResultDTO:otherCount){ + if (checkResultDTO.getCheckStart()==null){ + continue; + } + if (checkResultDTO.getCheckStart().getTime() >= eqPatrolCheckPlanEntity.getPlanStartTime().getTime() && + checkResultDTO.getCheckStart().getTime() < nextPlanTime.getTime() ){ + checkIds.add(checkResultDTO.getThingId()); + } + } + } + } + totalCheckIds.removeAll(checkIds); + LinkedHashSet noCheckIds = new LinkedHashSet<>(); + noCheckIds.addAll(totalCheckIds); + //本次巡检记录小于总设备数,记录 + if (noCheckIds.size()>0 && curDate.getTime() >= nextPlanTime.getTime()){ + for (Long thingId : noCheckIds){ + EqPlanRecordDTO dto = new EqPlanRecordDTO(); + dto.setPlanId(eqPatrolCheckPlanEntity.getId()); + dto.setPlanName(eqPatrolCheckPlanEntity.getName()); + dto.setPlanNo(eqPatrolCheckPlanEntity.getPatrolCheckPlanNo()); + dto.setThingId(thingId); + dto.setUserId(eqPatrolCheckPlanEntity.getUserId()); + dto.setPlanStartTime(eqPatrolCheckPlanEntity.getPlanStartTime()); + dto.setPlanEndTime(nextPlanTime); + dto.setHandle("0"); + dto.setType("1"); + dto.setDeptId(eqPatrolCheckPlanEntity.getDeptId()); + dto.setTenantCode(eqPatrolCheckPlanEntity.getTenantCode()); + eqPlanRecordService.saveDto(dto); + } + } + //当前时间大于等于下一次开始时间则更新 + if (curDate.getTime() >= nextPlanTime.getTime()){ + eqPatrolCheckPlanEntity.setPlanStartTime(nextPlanTime); + eqPatrolCheckPlanEntity.setIsRemind("0"); + eqPatrolCheckPlanEntity.setExecutePlanStatus("0"); + if (nextPlanTime.getTime() >= eqPatrolCheckPlanEntity.getCheckEndTime().getTime()){ + eqPatrolCheckPlanEntity.setPlanStatus("INACTIVE"); + } + eqPatrolCheckPlanService.updateById(eqPatrolCheckPlanEntity); + } + } + } + } + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/tree/controller/DeviceController.java b/modules/equipment/src/main/java/com/thing/eq/tree/controller/DeviceController.java new file mode 100644 index 0000000..4241b32 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/tree/controller/DeviceController.java @@ -0,0 +1,158 @@ +package com.thing.eq.tree.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.paginate.Page; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.eq.eqbxwx.dto.CommonDTO; +import com.thing.eq.eqmanager.dto.EqDTO; +import com.thing.eq.eqmanager.dto.IotThingBaseInfoDTO; +import com.thing.eq.eqmanager.excel.IotThingsExcel; +import com.thing.eq.eqmanager.excel.IotThingsPartExcel; +import com.thing.eq.eqmanager.service.IotThingBaseInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import java.util.*; + +/** + * @author zy + * @version 1.0 + * @date 2021/9/29 0029 14:54:05 + */ +@RestController +@RequestMapping("device/tree") +@Tag(name = "设备-设备树") +public class DeviceController { +// +// @Autowired +// private TreeService treeService; + + @Autowired + private IotThingBaseInfoService iotThingBaseInfoService; + + @GetMapping("/getDeviceList") + @Operation(summary = "分页查询一个区域的树(设备台账列表||部件列表)") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "toId", description = "树节点id(父级id)"), + @Parameter(name = "keyWord", description = "关键字(设备名称/编号/型号)"), + @Parameter(name = "eqTypeName", description = "设备类型"), + @Parameter(name = "useDeptId", description = "使用部门id"), + @Parameter(name = "relationTypeId", description = "结构类型id") + }) + public Result getDeviceList(@Parameter(hidden = true) @RequestParam Map params) { + if (Objects.isNull(params.get("toId"))) { + throw new SysException("父级toId不能为空"); + } + if (Objects.isNull(params.get("page")) || Objects.isNull(params.get("limit"))) { + throw new SysException("分页参数page、limit不能为空"); + } + String relationTypeId = Objects.isNull(params.get("relationTypeId"))?"":String.valueOf(params.get("relationTypeId")); + if(StringUtils.isBlank(relationTypeId)){ + throw new SysException("结构类型id不能为空"); + } + + List eqDTOS = new ArrayList<>(); + long total = 0L; + Page page = iotThingBaseInfoService.queryDeviceListByThingsId(params); + if(Objects.nonNull(page)){ + eqDTOS = page.getRecords(); + total = page.getTotalPage(); + } + PageData page1 = new PageData(eqDTOS, CollectionUtil.isEmpty(eqDTOS) ? 0 : total); + return new Result().ok(page1); + } + + @PostMapping("export") + @Operation(summary ="导出设备") + @LogOperation("导出设备") +// @Parameters({ +// @Parameter(name = "toId", value = "树节点id", paramType = "query", dataType = "String"), +// @Parameter(name = "keyWord", value = "关键字(设备名称/编号/型号)", paramType = "query", dataType = "String"), +// @Parameter(name = "eqTypeName", value = "设备类型", paramType = "query", dataType = "String"), +// @Parameter(name = "useDeptId", value = "使用部门id", paramType = "query", dataType = "String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query", required = true, dataType = "String") +// }) +// @RequiresPermissions("equipment:equipmentStatus:export") + public void export(HttpServletResponse response, @RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + if (Objects.isNull(params.get("toId"))) { + throw new SysException("父级toId不能为空"); + } + String relationTypeId = Objects.isNull(params.get("relationTypeId"))?"":String.valueOf(params.get("relationTypeId")); + if(StringUtils.isBlank(relationTypeId)){ + throw new SysException("结构类型id不能为空"); + } + + List list = iotThingBaseInfoService.listExport(params,ids); + + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (IotThingsExcel iotThingsExcel :list){ + if (idList.contains(iotThingsExcel.getId())){ + newList.add(iotThingsExcel); + } + } + } +// else{ +// newList.addAll(list); +// } + ExcelUtils.exportExcel(newList,null, "设备台账", IotThingsExcel.class,"设备台账.xls",response); + + } + + @PostMapping("export/part") + @Operation(summary ="导出部件") + @LogOperation("导出部件") +// @Parameters({ +// @Parameter(name = "group", value = "结构类型group", paramType = "query", dataType = "String"), +// @Parameter(name = "type", value = "结构类型", paramType = "query", dataType = "String"), +// @Parameter(name = "toId", value = "树节点id", paramType = "query", dataType = "String"), +// @Parameter(name = "keyWord", value = "关键字(设备名称/编号/型号)", paramType = "query", dataType = "String"), +// @Parameter(name = "eqTypeName", value = "设备类型", paramType = "query", dataType = "String"), +// @Parameter(name = "useDeptId", value = "使用部门id", paramType = "query", dataType = "String"), +// @Parameter(name = "relationTypeId", value = "结构类型id", paramType = "query", required = true, dataType = "String") +// }) + public void exportPart(HttpServletResponse response,@RequestBody CommonDTO dto) throws Exception { + Map params = dto.getParams(); + Long[] ids = dto.getIds(); + if (Objects.isNull(params.get("toId"))) { + throw new SysException("父级toId不能为空"); + } + String relationTypeId = Objects.isNull(params.get("relationTypeId"))?"":String.valueOf(params.get("relationTypeId")); + if(StringUtils.isBlank(relationTypeId)){ + throw new SysException("结构类型id不能为空"); + } + List list = iotThingBaseInfoService.listExportPart(params,ids); + + List newList = new ArrayList<>(); + List idList = new ArrayList<>(Arrays.asList(ids)); + if (CollectionUtil.isNotEmpty(idList)){ + for (IotThingsPartExcel iotThingsPartExcel : list ){ + if (idList.contains(iotThingsPartExcel.getId())){ + newList.add(iotThingsPartExcel); + } + } + } +// else{ +// newList.addAll(list); +// } + ExcelUtils.exportExcel(newList,null, "备件库", IotThingsPartExcel.class,"备件库.xls",response); + } + +} diff --git a/modules/equipment/src/main/java/com/thing/eq/utils/DateSplitUtils.java b/modules/equipment/src/main/java/com/thing/eq/utils/DateSplitUtils.java new file mode 100644 index 0000000..3c3f15d --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/utils/DateSplitUtils.java @@ -0,0 +1,431 @@ +package com.thing.eq.utils; + + +import com.thing.common.core.utils.DateTimeUtils; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.stream.Collectors; + +public class DateSplitUtils { + public enum IntervalType { + MONTH, + DAY, + HOUR, + MINUTE, + SECOND, + ; + } + + /** + * 时间切割 + * @param startTimeTs 被切割的开始时间 + * @param endTimeTs 被切割的结束时间 + * @param intervalType + * @param interval >0 + * @return + */ + public static List splitDate(Long startTimeTs, Long endTimeTs, IntervalType intervalType, int interval) { + Date startTime = new Date(startTimeTs); + Date endTime = new Date(endTimeTs); + if (interval < 0) { + return null; + } + if (endTime.getTime() <= startTime.getTime()) { + return null; + } + if (intervalType == IntervalType.MONTH) { + return splitByMonth(startTime, endTime, interval); + } + if (intervalType == IntervalType.DAY) { + return splitByDay(startTime, endTime, interval); + } + if (intervalType == IntervalType.HOUR) { + return splitByHour(startTime, endTime, interval); + } + if (intervalType == IntervalType.MINUTE) { + return splitByMinute(startTime, endTime, interval); + } + if (intervalType == IntervalType.SECOND) { + return splitBySecond(startTime, endTime, interval); + } + return null; + } + + /** + * 按照小时切割时间区间 + */ + public static List splitByHour(Date startTime, Date endTime, int intervalHours) { + if (endTime.getTime() <= startTime.getTime()) { + return null; + } + + List dateSplits = new ArrayList<>(256); + + DateSplit param = new DateSplit(); + param.setStartDateTime(startTime); + param.setEndDateTime(endTime); + param.setEndDateTime(addSeconds(addHours(startTime, intervalHours),-1)); + while (true) { + param.setStartDateTime(startTime); + Date tempEndTime = addSeconds(addHours(startTime, intervalHours),-1); + if (tempEndTime.getTime() >= endTime.getTime()) { + tempEndTime = endTime; + } + param.setEndDateTime(tempEndTime); + + dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime())); + + startTime = addHours(startTime, intervalHours); + if (startTime.getTime() >= endTime.getTime()) { + break; + } + if (param.getEndDateTime().getTime() >= endTime.getTime()) { + break; + } + } + return dateSplits; + } + + /** + * 按照秒切割时间区间 + */ + public static List splitBySecond(Date startTime, Date endTime, int intervalSeconds) { + if (endTime.getTime() <= startTime.getTime()) { + return null; + } + List dateSplits = new ArrayList<>(256); + + DateSplit param = new DateSplit(); + param.setStartDateTime(startTime); + param.setEndDateTime(endTime); + param.setEndDateTime(addSeconds(startTime, intervalSeconds)); + while (true) { + param.setStartDateTime(startTime); + Date tempEndTime = addSeconds(startTime, intervalSeconds); + if (tempEndTime.getTime() >= endTime.getTime()) { + tempEndTime = endTime; + } + param.setEndDateTime(tempEndTime); + + dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime())); + + startTime = addSeconds(startTime, intervalSeconds); + if (startTime.getTime() >= endTime.getTime()) { + break; + } + if (param.getEndDateTime().getTime() >= endTime.getTime()) { + break; + } + } + return dateSplits; + } + + /** + * 按照天切割时间区间 + */ + public static List splitByDay(Date startTime, Date endTime, int intervalDays) { + if (endTime.getTime() < startTime.getTime()) { + return null; + } + List dateSplits = new ArrayList<>(256); + endTime = addSeconds(addDays(endTime, intervalDays),-1); + DateSplit param = new DateSplit(); + param.setStartDateTime(startTime); + param.setEndDateTime(endTime); + param.setEndDateTime(addDays(startTime, intervalDays)); + while (true) { + param.setStartDateTime(startTime); + Date tempEndTime = addSeconds(addDays(startTime, intervalDays),-1); + if (tempEndTime.getTime() >= endTime.getTime()) { + tempEndTime = endTime; + } + param.setEndDateTime(tempEndTime); + + dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime())); + + startTime = addDays(startTime, intervalDays); + if (startTime.getTime() >= endTime.getTime()) { + break; + } + if (param.getEndDateTime().getTime() >= endTime.getTime()) { + break; + } + } + return dateSplits; + } + + /** + * 按照月切割时间区间 + */ + public static List splitByMonth(Date startTime, Date endTime, int intervalDays) { + if (endTime.getTime() < startTime.getTime()) { + return null; + } + List dateSplits = new ArrayList<>(); + //endTime = addSeconds(addMonth(endTime, intervalDays),-1); + DateSplit param = new DateSplit(); + param.setStartDateTime(startTime); + param.setEndDateTime(endTime); + param.setEndDateTime(addMonth(startTime, intervalDays)); + while (true) { + param.setStartDateTime(startTime); + Date tempEndTime = addSeconds(addMonth(startTime, intervalDays),-1); + if (tempEndTime.getTime() >= endTime.getTime()) { + tempEndTime = endTime; + } + param.setEndDateTime(tempEndTime); + + dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime())); + + startTime = addMonth(startTime, intervalDays); + if (startTime.getTime() >= endTime.getTime()) { + break; + } + if (param.getEndDateTime().getTime() >= endTime.getTime()) { + break; + } + } + return dateSplits; + } + + /** + * 按照分钟切割时间区间 + * + * @param startTime + * @param endTime + * @param intervalMinutes + * @return + */ + public static List splitByMinute(Date startTime, Date endTime, int intervalMinutes) { + if (endTime.getTime() <= startTime.getTime()) { + return null; + } + List dateSplits = new ArrayList<>(256); + + DateSplit param = new DateSplit(); + param.setStartDateTime(startTime); + param.setEndDateTime(endTime); + param.setEndDateTime(addMinute(startTime, intervalMinutes)); + while (true) { + param.setStartDateTime(startTime); + Date tempEndTime = addMinute(startTime, intervalMinutes); + if (tempEndTime.getTime() >= endTime.getTime()) { + tempEndTime = endTime; + } + param.setEndDateTime(tempEndTime); + + dateSplits.add(new DateSplit(param.getStartDateTime(), param.getEndDateTime())); + + startTime = addMinute(startTime, intervalMinutes); + if (startTime.getTime() >= endTime.getTime()) { + break; + } + if (param.getEndDateTime().getTime() >= endTime.getTime()) { + break; + } + } + return dateSplits; + } + + private static Date addMonth(Date date, int month) { + return add(date, Calendar.MONTH, month); + } + + private static Date addDays(Date date, int days) { + return add(date, Calendar.DAY_OF_MONTH, days); + } + + private static Date addHours(Date date, int hours) { + return add(date, Calendar.HOUR_OF_DAY, hours); + } + + private static Date addMinute(Date date, int minute) { + return add(date, Calendar.MINUTE, minute); + } + private static Date addSeconds(Date date, int second) { + return add(date, Calendar.SECOND, second); + } + + private static Date add(final Date date, final int calendarField, final int amount) { + final Calendar c = Calendar.getInstance(); + c.setTime(date); + c.add(calendarField, amount); + return c.getTime(); + } + + private static String formatDateTime(Date date) { + if (date == null) { + return ""; + } + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return simpleDateFormat.format(date); + } + + @Data + @AllArgsConstructor + @NoArgsConstructor + public static class DateSplit { + private Date startDateTime; + private Date endDateTime; + + public String getStartDateTimeStr() { + return formatDateTime(startDateTime); + } + + public String getEndDateTimeStr() { + return formatDateTime(endDateTime); + } + } + + //时间工具封装 + + /** + * 获取每天的开始时间 00:00:00:00 SnailJian + * + * @param date + * @return + */ + public static Date getStartTime(Date date) { + Calendar dateStart = Calendar.getInstance(); + dateStart.setTime(date); + dateStart.set(Calendar.HOUR_OF_DAY, 0); + dateStart.set(Calendar.MINUTE, 0); + dateStart.set(Calendar.SECOND, 0); + return dateStart.getTime(); + } + + /** + * 获取每天的开始时间 23:59:59:999 Snailjian + * + * @param date + * @return + */ + public static Date getEndTime(Date date) { + Calendar dateEnd = Calendar.getInstance(); + dateEnd.setTime(date); + dateEnd.set(Calendar.HOUR_OF_DAY, 23); + dateEnd.set(Calendar.MINUTE, 59); + dateEnd.set(Calendar.SECOND, 59); + return dateEnd.getTime(); + } + + /** + * 获取指定年月的第一天 snailjian + * @return + */ + public static Date getStartDayOfMonth(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH); + + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + int firstDay = calendar.getMinimum(Calendar.DATE); + calendar.set(Calendar.DAY_OF_MONTH,firstDay); + return calendar.getTime(); + } + + /** + * 获取指定年月的最后一天 SnailJian + * @return + */ + public static Date getEndDayOfMonth(Date date) { + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + int month = calendar.get(Calendar.MONTH); + + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.set(Calendar.MONTH, month); + int lastDay = calendar.getActualMaximum(Calendar.DATE); + calendar.set(Calendar.DAY_OF_MONTH, lastDay); + return calendar.getTime(); + } + + /** + * 获取某年第一天日期 SnailJian + * @return Date + */ + public static Date getStartDayOfYear(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + + calendar.clear(); + calendar.set(Calendar.YEAR, year); + return calendar.getTime(); + } + + /** + * 获取某年最后一天日期 SnailJian + * @return Date + */ + public static Date getEndDayOfYear(Date date){ + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int year = calendar.get(Calendar.YEAR); + + calendar.clear(); + calendar.set(Calendar.YEAR, year); + calendar.roll(Calendar.DAY_OF_YEAR, - 1); + + return calendar.getTime(); + } + + + /** + * 根据起止时间获得分割的天 + * @param startTm + * @param endTm + * @return + */ + public static List getSplitDay(String startTm, String endTm) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(DateTimeUtils.parse(startTm, DateTimeUtils.DATE_TIME_PATTERN_STR)); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(DateTimeUtils.parse(endTm, DateTimeUtils.DATE_TIME_PATTERN_STR)); + + startCalendar.set(Calendar.HOUR_OF_DAY, 0); + startCalendar.set(Calendar.MINUTE, 0); + startCalendar.set(Calendar.SECOND, 0); + + endCalendar.set(Calendar.HOUR_OF_DAY, 0); + endCalendar.set(Calendar.MINUTE, 0); + endCalendar.set(Calendar.SECOND, 0); + + List dateSplits = DateSplitUtils.splitByDay(startCalendar.getTime() + , endCalendar.getTime(), 1); + List dateList = dateSplits.stream().map(DateSplit::getStartDateTimeStr).collect(Collectors.toList()); + return dateList; + } + + public static List getSplitDayNew(String startTm, String endTm) { + Calendar startCalendar = Calendar.getInstance(); + startCalendar.setTime(DateTimeUtils.parse(startTm, DateTimeUtils.DATE_TIME_PATTERN_STR)); + Calendar endCalendar = Calendar.getInstance(); + endCalendar.setTime(DateTimeUtils.parse(endTm, DateTimeUtils.DATE_TIME_PATTERN_STR)); + + startCalendar.set(Calendar.HOUR_OF_DAY, 9); + startCalendar.set(Calendar.MINUTE, 0); + startCalendar.set(Calendar.SECOND, 0); + + endCalendar.set(Calendar.HOUR_OF_DAY, 9); + endCalendar.set(Calendar.MINUTE, 0); + endCalendar.set(Calendar.SECOND, 0); + + List dateSplits = DateSplitUtils.splitByDay(startCalendar.getTime() + , endCalendar.getTime(), 1); + List dateList = dateSplits.stream().map(DateSplit::getStartDateTimeStr).collect(Collectors.toList()); + return dateList; + } +} diff --git a/modules/equipment/src/main/java/com/thing/eq/utils/SerialNumberUnit.java b/modules/equipment/src/main/java/com/thing/eq/utils/SerialNumberUnit.java new file mode 100644 index 0000000..dcbf7f0 --- /dev/null +++ b/modules/equipment/src/main/java/com/thing/eq/utils/SerialNumberUnit.java @@ -0,0 +1,74 @@ +package com.thing.eq.utils; + +import org.apache.commons.lang3.StringUtils; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * @author zy + * @version 1.0 + * @date 2021/11/2 0002 9:29:20 + */ +public class SerialNumberUnit { + + public static String generateNumber(String preType, String curNum) { + SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd"); + Date curDate = new Date(); + String curDateStr = simpleDateFormat.format(curDate); + + String numStr = ""; + // 如果是空则返回当天第一条流水号 + if (StringUtils.isBlank(curNum)) { + numStr = String.format("%03d", 1); + } else { + // 如果不是空 且流水号中的日期与当前日期一致 则对流水号进行+ 1,不一致则从1重新开始 + if (!curNum.contains(curDateStr)) { + numStr = String.format("%03d", 1); + }else{ + String num = ""; + int index = curNum.indexOf("-"); + if (index >= 0) { + num = curNum.substring(index + 8 + 1, curNum.length()); + } + numStr = String.format("%03d", Integer.parseInt(num) + 1); + } + } + + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(preType); + stringBuilder.append("-"); + stringBuilder.append(curDateStr); + stringBuilder.append(numStr); + + return stringBuilder.toString(); + } + + + public static String generateChildNumber(String parentNum, String curNumStr) { + String num = ""; + if(StringUtils.isBlank(curNumStr)){ + num = "1"; + }else{ + if(curNumStr.contains(parentNum)){ + int index = curNumStr.lastIndexOf("-"); + if (index > 2) { + num = curNumStr.substring(index + 1, curNumStr.length()); + num = String.valueOf(Integer.parseInt(num) + 1); + } else { + num = "1"; + } + }else{ + num = "1"; + } + } + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(parentNum); + stringBuilder.append("-"); + stringBuilder.append(num); + + return stringBuilder.toString(); + } +} diff --git a/modules/equipment/src/main/resources/mapper/checkresult/CheckResultMapper.xml b/modules/equipment/src/main/resources/mapper/checkresult/CheckResultMapper.xml new file mode 100644 index 0000000..656810e --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/checkresult/CheckResultMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqbxwx/EqBxMapper.xml b/modules/equipment/src/main/resources/mapper/eqbxwx/EqBxMapper.xml new file mode 100644 index 0000000..be0966b --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqbxwx/EqBxMapper.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT eb.*, + sd.NAME AS dept_name, + itb.NAME AS device_name, + itb.code AS device_code, + itb.standard AS device_standard, + itb.device_type, + itb.device_position, + itb.eq_code + FROM eq_bx eb + LEFT JOIN sys_dept sd ON eb.dept_id = sd.ID + LEFT JOIN ( + SELECT itb.ID, + it.NAME, + it.code, + it.real_type as device_type, + itb.standard, + itb.eq_position as device_position, + itb.eq_code as eq_code + FROM iot_thing_base_info itb + LEFT JOIN iot_thing_entity it ON itb.thing_id = it.ID + ) itb ON eb.eq_id = itb.ID + where 1 = 1 + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxMapper.xml b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxMapper.xml new file mode 100644 index 0000000..2002977 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxPlanMapper.xml b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxPlanMapper.xml new file mode 100644 index 0000000..303d36d --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxPlanMapper.xml @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxReplacementMapper.xml b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxReplacementMapper.xml new file mode 100644 index 0000000..e5f3c96 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqbxwx/EqWxReplacementMapper.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByDetailMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByDetailMapper.xml new file mode 100644 index 0000000..b83f1ad --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByDetailMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + delete from eq_by_detail where by_id=#{byId} + + + delete from eq_by_detail + + by_id in + + #{byId} + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByMapper.xml new file mode 100644 index 0000000..1809a04 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByPlanMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByPlanMapper.xml new file mode 100644 index 0000000..a0b008b --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByPlanMapper.xml @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + update eq_by_plan set plan_status=#{planStatus} where id=#{id} + + + update eq_by_plan set is_remind =#{isRemind} where id=#{id} + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByPlanPartMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByPlanPartMapper.xml new file mode 100644 index 0000000..ef59f8f --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByPlanPartMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + DELETE FROM eq_by_plan_part WHERE eq_by_plan_id =#{eqByPlanId} + + + delete from eq_by_plan_part + + eq_by_plan_id in + + #{eqByPlanId} + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateDetailMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateDetailMapper.xml new file mode 100644 index 0000000..e5ba9a8 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateDetailMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + delete FROM eq_by_template_detail WHERE by_template_id=#{templateId} + + + delete from eq_by_template_detail + + by_template_id in + + #{byTemplateId} + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateMapper.xml b/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateMapper.xml new file mode 100644 index 0000000..b010834 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqby/EqByTemplateMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckSettingMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckSettingMapper.xml new file mode 100644 index 0000000..a80a9ca --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckSettingMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardDetailMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardDetailMapper.xml new file mode 100644 index 0000000..0fe7e5f --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardDetailMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardMapper.xml new file mode 100644 index 0000000..8868ebc --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqCheckStandardMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckPlanMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckPlanMapper.xml new file mode 100644 index 0000000..aa28db4 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckPlanMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update eq_patrol_check_plan set is_remind =#{isRemind} where id=#{id} + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckRecordMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckRecordMapper.xml new file mode 100644 index 0000000..0c3a9fa --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqPatrolCheckRecordMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckPlanMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckPlanMapper.xml new file mode 100644 index 0000000..f1eb2a6 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckPlanMapper.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckRecordMapper.xml b/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckRecordMapper.xml new file mode 100644 index 0000000..2c6e0f4 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqcheck/EqSpotCheckRecordMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileDeleteMapper.xml b/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileDeleteMapper.xml new file mode 100644 index 0000000..52aa1f3 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileDeleteMapper.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + delete from sys_oss where url = #{url} + + + diff --git a/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileManageMapper.xml b/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileManageMapper.xml new file mode 100644 index 0000000..a6ec623 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqfilemanage/EqFileManageMapper.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqmanager/EqAttacmentMapper.xml b/modules/equipment/src/main/resources/mapper/eqmanager/EqAttacmentMapper.xml new file mode 100644 index 0000000..9d8efb7 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqmanager/EqAttacmentMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + delete from eq_attacment where thing_Id =#{thingId} + + + delete from eq_attacment + + thing_id in + + #{thingId} + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqmanager/EqScrapMapper.xml b/modules/equipment/src/main/resources/mapper/eqmanager/EqScrapMapper.xml new file mode 100644 index 0000000..6b1ebf8 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqmanager/EqScrapMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + delete from eq_scrap where eq_id=#{eqId} + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqmanager/EqThingsRelationMapper.xml b/modules/equipment/src/main/resources/mapper/eqmanager/EqThingsRelationMapper.xml new file mode 100644 index 0000000..49651c3 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqmanager/EqThingsRelationMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqmanager/IotThingBaseInfoMapper.xml b/modules/equipment/src/main/resources/mapper/eqmanager/IotThingBaseInfoMapper.xml new file mode 100644 index 0000000..1e730b3 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqmanager/IotThingBaseInfoMapper.xml @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + update iot_thing_base_info set scrap=#{scrap},scrap_disposal=#{scrapDisposal} where thing_id=#{eqId} + + + + delete from iot_thing_base_info + + thing_id in + + #{thingId} + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqmanager/IotThingsMapper.xml b/modules/equipment/src/main/resources/mapper/eqmanager/IotThingsMapper.xml new file mode 100644 index 0000000..d28f817 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqmanager/IotThingsMapper.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqpalnrecord/EqPlanRecordMapper.xml b/modules/equipment/src/main/resources/mapper/eqpalnrecord/EqPlanRecordMapper.xml new file mode 100644 index 0000000..a3a91fe --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqpalnrecord/EqPlanRecordMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqpartrecord/EqPartRecordMapper.xml b/modules/equipment/src/main/resources/mapper/eqpartrecord/EqPartRecordMapper.xml new file mode 100644 index 0000000..0df5f33 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqpartrecord/EqPartRecordMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/eqremindrecord/EqPlanRemindRecordMapper.xml b/modules/equipment/src/main/resources/mapper/eqremindrecord/EqPlanRemindRecordMapper.xml new file mode 100644 index 0000000..93a4722 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/eqremindrecord/EqPlanRemindRecordMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/equsergroup/EqUserGroupMapper.xml b/modules/equipment/src/main/resources/mapper/equsergroup/EqUserGroupMapper.xml new file mode 100644 index 0000000..0af3e00 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/equsergroup/EqUserGroupMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/file/FileMapper.xml b/modules/equipment/src/main/resources/mapper/file/FileMapper.xml new file mode 100644 index 0000000..5993d4d --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/file/FileMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/monitor/DeviceMonitorMapper.xml b/modules/equipment/src/main/resources/mapper/monitor/DeviceMonitorMapper.xml new file mode 100644 index 0000000..02b2783 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/monitor/DeviceMonitorMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/normailze/EnergyCheckMapper.xml b/modules/equipment/src/main/resources/mapper/normailze/EnergyCheckMapper.xml new file mode 100644 index 0000000..0f4b256 --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/normailze/EnergyCheckMapper.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/normailze/EnergyPeopleMapper.xml b/modules/equipment/src/main/resources/mapper/normailze/EnergyPeopleMapper.xml new file mode 100644 index 0000000..c1a322c --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/normailze/EnergyPeopleMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/equipment/src/main/resources/mapper/normailze/EnergyProjectMapper.xml b/modules/equipment/src/main/resources/mapper/normailze/EnergyProjectMapper.xml new file mode 100644 index 0000000..6b82b7b --- /dev/null +++ b/modules/equipment/src/main/resources/mapper/normailze/EnergyProjectMapper.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update sys_energy_project set status=#{status},remark=#{remark} where id=#{id} + + + + + + + + diff --git a/modules/filter-rule/pom.xml b/modules/filter-rule/pom.xml new file mode 100644 index 0000000..14f28b5 --- /dev/null +++ b/modules/filter-rule/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + filter-rule + jar + ThingBI Server Modules filter-rule + + UTF-8 + + + + com.thing.modules + thing + + + \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/cache/FilterRuleCacheInitializer.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/cache/FilterRuleCacheInitializer.java new file mode 100644 index 0000000..6d0b416 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/cache/FilterRuleCacheInitializer.java @@ -0,0 +1,33 @@ +package com.thing.filter.rule.cache; + +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; +import com.thing.filter.rule.service.FilterRuleService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.stereotype.Component; + +/** + * @author siyang + * @date 2024-03-19 + * @description 过滤规则缓存初始化器 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class FilterRuleCacheInitializer extends AbstractCacheService { + + private final FilterRuleService filterRuleService; + + @Override + public String getCacheName() { + return CacheNameEnum.FILTER_RULE; + } + + @Override + public void initCache() { + filterRuleService.getAllEnabled(); + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterLogController.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterLogController.java new file mode 100644 index 0000000..cd1f961 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterLogController.java @@ -0,0 +1,61 @@ +package com.thing.filter.rule.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.filter.rule.dto.FilterLogDTO; +import com.thing.filter.rule.service.FilterLogService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 过滤日志 +* +* @author siyang 2337720667@qq.com +* @since 5.1 2024-03-21 +*/ +@RestController +@RequestMapping("v2/filter/log") +@Tag(name="过滤日志") +@RequiredArgsConstructor +public class FilterLogController { + + private final FilterLogService filterLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "filterRuleName", description = "过滤规则名称"), + @Parameter(name = "status", description = "状态:1-未匹配,2-已匹配,3-计算成功,4-计算异常,5-已通知"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = filterLogService.handlePage(params); + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + filterLogService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterRuleController.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterRuleController.java new file mode 100644 index 0000000..ee2048f --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/controller/FilterRuleController.java @@ -0,0 +1,140 @@ +package com.thing.filter.rule.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.filter.rule.dto.FilterRuleDTO; +import com.thing.filter.rule.service.FilterRuleService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 过滤规则 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +@RestController +@RequestMapping("v2/filter/rule") +@Tag(name = "过滤规则") +@RequiredArgsConstructor +public class FilterRuleController { + + private final FilterRuleService filterRuleService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeyword", description = "物关键词:用于物名称、属性的模糊搜索"), + @Parameter(name = "attrKeyword", description = "属性关键词:用于属性名称、属性的模糊搜索"), + @Parameter(name = "name", description = "过滤规则名称"), + }) + public Result> page( + @Parameter(hidden = true) @RequestParam Map params) { + PageData page = filterRuleService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeyword", description = "物关键词:用于物名称、属性的模糊搜索"), + @Parameter(name = "attrKeyword", description = "属性关键词:用于属性名称、属性的模糊搜索"), + }) + public Result> list( + @Parameter(hidden = true) @RequestParam Map params) { + List data = filterRuleService.handleList(params); + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + FilterRuleDTO data = filterRuleService.handleDetail(id); + return new Result().ok(data); + } + + @GetMapping("name/check") + @Operation(summary = "检测名称是否可用:true(可用), false(规则名称已存在)") + public Result checkNameValid(String name) { + Boolean valid = filterRuleService.checkNameValid(name); + return new Result().ok(valid); + } + + @GetMapping("name/list") + @Operation(summary = "获取现有规则名称,用于下拉选择") + public Result> getNameList() { + List names = filterRuleService.getNameList(); + return new Result>().ok(names); + } + + @GetMapping("cache") + @Operation(summary = "获取缓存数据【该接口用于调试,前端不要调用】") + public Result> getAllEnabled() { + List data = filterRuleService.getAllEnabled(); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@RequestBody FilterRuleDTO dto) { + // 效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + filterRuleService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody FilterRuleDTO dto) { + // 效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + filterRuleService.handleUpdate(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + // 效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + filterRuleService.handleDelete(List.of(ids)); + return new Result<>(); + } + + @DeleteMapping("detail/delete") + @Operation(summary = "删除规则详情") + @LogOperation("删除规则详情") + public Result deleteDetail(@RequestBody Long[] detailIds) { + if (detailIds.length == 0) { + return new Result<>(); + } + filterRuleService.deleteDetail(detailIds[0]); + return new Result<>(); + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterLogDTO.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterLogDTO.java new file mode 100644 index 0000000..b535bb2 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterLogDTO.java @@ -0,0 +1,58 @@ +package com.thing.filter.rule.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 过滤日志 +* +* @author siyang 2337720667@qq.com +* @since 5.1 2024-03-21 +*/ +@Data +@Schema(description = "过滤日志") +public class FilterLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "过滤规则配置表id") + private Long filterRuleId; + @Schema(description = "过滤条件") + private String formula; + @Schema(description = "数据信息,json格式") + private String sourceInfo; + @Schema(description = "日志状态:1-未匹配, 2-已匹配, 3-已通知") + private Integer status; + @Schema(description = "数据时间") + private Long time; + @Schema(description = "计算结果,只有已计算的记录才有值") + private Boolean result; + @Schema(description = "错误信息") + private String errorInfo; + @Schema(description = "计算次数") + private Integer calcCount; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "过滤规则名称") + private String filterRuleName; + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDTO.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDTO.java new file mode 100644 index 0000000..d1be2cf --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDTO.java @@ -0,0 +1,50 @@ +package com.thing.filter.rule.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotEmpty; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 过滤规则表 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-03-14 +*/ +@Data +@Schema(description = "过滤规则") +public class FilterRuleDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "过滤规则名称") + private String name; + @Schema(description = "过滤条件") + private String formula; + @Schema(description = "过滤条件文描述") + private String formulaDescription; + @Schema(description = "备注") + private String remark; + @Schema(description = "是否启用") + private Object enable; + + @Schema(description = "规则详情") + @NotEmpty( + message = "规则详情不能为空", + groups = {AddGroup.class, UpdateGroup.class}) + private List details; + + private Long tenantCode; + private Long companyId; + private Long deptId; +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDetailDTO.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDetailDTO.java new file mode 100644 index 0000000..802f038 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/FilterRuleDetailDTO.java @@ -0,0 +1,56 @@ +package com.thing.filter.rule.dto; + +import com.thing.filter.rule.enumeration.DataType; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 过滤规则详情表 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-03-14 +*/ +@Data +@Schema(description = "过滤规则详情") +public class FilterRuleDetailDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "过滤规则id") + private Long filterRuleId; + @Schema(description = "物名称") + private String thingName; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "属性编码") + private String attrCode; + @Schema(description = "属性在计算公式中的别名") + private String attrAlias; + @Schema(description = "数据类型:1-最新,2-最近, 3-区间") + private Integer dataType; + @Schema(description = "聚合类型:SUM|AVG|MAX|MIN|COUNT") + private String aggType; + @Schema(description = "聚合时间间隔") + private Integer aggInterval; + @Schema(description = "聚合时间单位:MINUTE|HOUR|DAY|MONTH") + private String timeunit; + + private Long tenantCode; + private Long companyId; + private Long deptId; + + /** + * 是否为整点时间 + */ + public Boolean getOnTime(){ + return DataType.isRange(dataType); + } +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/LogSourceInfoValue.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/LogSourceInfoValue.java new file mode 100644 index 0000000..125d255 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/dto/LogSourceInfoValue.java @@ -0,0 +1,60 @@ +package com.thing.filter.rule.dto; + +import com.thing.common.core.exception.SysException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.Objects; + +/** + * @author siyang + * @date 2024-03-21 + * @description 过滤日志sourceInfo为map形式的jsonString + * 该类为map中的value格式 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LogSourceInfoValue { + private String thingCode; + private String attrCode; + private String value; + private Long ts; + + private Integer dataType; + private String aggType; + private Integer aggInterval; + private String timeunit; + private Boolean onTime; + + public String combineCode() { + return thingCode + ":" + attrCode; + } + + public String combineAggInfo() { + return aggType + ":" + aggInterval + ":" + timeunit + ":" + onTime; + } + + public BigDecimal convertVal() { + if(Objects.isNull(value)){ + String errorMsg = + "数据缺失:" + .concat(thingCode) + .concat("_") + .concat(attrCode) + .concat(" in "); + if (Objects.nonNull(aggInterval) && Objects.nonNull(timeunit)) { + errorMsg = + errorMsg.concat(aggInterval.toString()) + .concat(" ") + .concat(timeunit) + .concat(" ago to "); + } + errorMsg = errorMsg.concat(ts.toString()); + throw new SysException(errorMsg); + } + return new BigDecimal(value); + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterLogEntity.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterLogEntity.java new file mode 100644 index 0000000..bc75bb7 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterLogEntity.java @@ -0,0 +1,346 @@ +package com.thing.filter.rule.entity; + +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.orm.entity.BaseInfoEntity; +import com.thing.filter.rule.dto.FilterRuleDTO; +import com.thing.filter.rule.dto.FilterRuleDetailDTO; +import com.thing.filter.rule.dto.LogSourceInfoValue; +import com.thing.filter.rule.enumeration.DataType; + +import com.thing.filter.rule.enumeration.LogStatus; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.util.CollectionUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 过滤日志 + * + * @author siyang 2337720667@qq.com + * @since 5.1 2024-03-21 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("filter_log") +public class FilterLogEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 过滤规则配置表id + */ + private Long filterRuleId; + /** + * 过滤条件 + */ + private String formula; + /** + * 数据信息,json格式 + */ + private String sourceInfo; + /** + * 日志状态:1-未匹配, 2-已匹配, 3-已通知 + */ + private Integer status; + /** + * 数据时间 + */ + private Long time; + /** + * 计算结果,只有已计算的记录才有值 + */ + private Boolean result; + /** + * 错误信息 + */ + private String errorInfo; + /** + * 计算次数 + */ + private Integer calcCount; + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 企业id + */ + private Long companyId; + /** + * 部门id + */ + private Long deptId; + + @Column(ignore = true) + private FilterRuleDTO filterRule; + + @Column(ignore = true) + private Map sourceInfoMap; + + /** + * ruleDetails中定义的物属性,格式为【thingCode:attrCode】 + */ + @Column(ignore = true) + private Set defThingAttrs; + + @Column(ignore = true) + private Set formulaVariables; + + private static final TypeReference> typeRef = new TypeReference<>() {}; + + public Integer getCalcCount() { + return Optional.ofNullable(calcCount).orElse(0); + } + + public Map getSourceInfoMap() { + if (sourceInfoMap == null) { + sourceInfoMap = JSONObject.parseObject(sourceInfo, typeRef); + } + return sourceInfoMap; + } + + public Set getFormulaVariables() { + if (formulaVariables == null) { + formulaVariables = FormulaUtil.getFormulaVariables(formula); + } + return formulaVariables; + } + + public Map>> getSameAggInfoThingAttrPairs() { + return getSourceInfoMap().values().stream() + .filter(e -> DataType.isAgg(e.getDataType())) + .collect( + Collectors.groupingBy( + LogSourceInfoValue::combineAggInfo, + Collectors.mapping( + e -> Pair.of(e.getThingCode(), e.getAttrCode()), + Collectors.toList()))); + } + + public static Map> groupLogByAggInfo(List sameTimeLogs) { + Map> aggLogGroup = new HashMap<>(); + for (FilterLogEntity sameTimeLog : sameTimeLogs) { + Map> logMap = sameTimeLog.groupByAggInfo(); + logMap.forEach((k, v) -> aggLogGroup.computeIfAbsent(k, s -> new ArrayList<>()).addAll(v)); + } + return aggLogGroup; + } + + private Map> groupByAggInfo() { + return getSourceInfoMap().values().stream() + .filter(e -> DataType.isAgg(e.getDataType())) + .collect( + Collectors.groupingBy( + LogSourceInfoValue::combineAggInfo, + Collectors.mapping(e -> this, Collectors.toList()))); + } + + /** 校验log的公式与ruleDetails是否能够对应 */ + public boolean validate() { + Set aliases = + getRuleDetails().stream() + .map(FilterRuleDetailDTO::getAttrAlias) + .collect(Collectors.toSet()); + return aliases.containsAll(getFormulaVariables()); + } + + public boolean checkComplete() { + boolean isComplete = getMissingVariables().isEmpty(); + if (isComplete) { + setStatus(LogStatus.MATCHED.getCode()); + } + return isComplete; + } + + + /** 执行计算,返回计算结果 */ + public boolean calculate() { + try { + setCalcCount(getCalcCount() + 1); + Map variableMap = new HashMap<>(); + getSourceInfoMap().forEach((k, v) -> variableMap.put(k, v.convertVal())); + Object res = FormulaUtil.executeFormula(formula, variableMap); + if (Objects.nonNull(res)) { + setResult((Boolean) res); + setStatus(LogStatus.CALC_SUCCESS.getCode()); + return result; + } + } catch (Exception e) { + setStatus(LogStatus.CALC_EXCEPTION.getCode()); + setErrorInfo(e.getMessage()); + } + return false; + } + + public void updateValue(Map tskvMap) { + boolean updated = false; + Map sourceInfoMap = getSourceInfoMap(); + for (String alias : sourceInfoMap.keySet()) { + LogSourceInfoValue sourceInfo = sourceInfoMap.get(alias); + String thingAttrTag = sourceInfo.getThingCode() + ":" + sourceInfo.getAttrCode(); + if (tskvMap.containsKey(thingAttrTag)) { + sourceInfo.setValue(tskvMap.get(thingAttrTag)); + updated = true; + } + } + if (updated) { + refreshSourceInfo(sourceInfoMap); + } + } + + public FilterLogEntity appendSourceInfo(FilterRuleDetailDTO detail, Long ts, String value) { + if (Objects.isNull(detail)) { + return this; + } + Map data = parseSourceInfo(); + putData(data, detail, ts, value); + sourceInfo = JSONObject.toJSONString(data); + return this; + } + + private void putData( + Map data, FilterRuleDetailDTO detail, Long ts, String value) { + Integer dataType = detail.getDataType(); + data.put( + detail.getAttrAlias(), + new LogSourceInfoValue( + detail.getThingCode(), + detail.getAttrCode(), + DataType.isLatest(dataType) ? value : null, + ts, + dataType, + detail.getAggType(), + detail.getAggInterval(), + detail.getTimeunit(), + detail.getOnTime())); + } + + /** + * 合并两个log: 只要another中存在一个可以被当前log合并的属性,就执行合并,否则不合并 + * + * @param another 另一个log + * @return 是否执行了merge + */ + public boolean merge(FilterLogEntity another) { + if (Objects.isNull(another) + || !Objects.equals(filterRuleId, another.getFilterRuleId()) + || Objects.isNull(another.getSourceInfo())) { + return false; + } + Map anotherData = parseSourceInfo(another.getSourceInfo()); + + boolean merged = false; + for (String alias : anotherData.keySet()) { + LogSourceInfoValue sourceInfoObj = anotherData.get(alias); + String thingAttr = sourceInfoObj.combineCode(); + if (getDefThingAttrs().contains(thingAttr) && checkToMerge(alias, sourceInfoObj)) { + merged = true; + } + } + return merged; + } + + private boolean checkToMerge(String alias, LogSourceInfoValue another) { + Map sourceInfoMap = getSourceInfoMap(); + // 当前log目前为止是否包含latest类型的变量 + boolean currentContainsLatest = + sourceInfoMap.values().stream() + .map(LogSourceInfoValue::getDataType) + .collect(Collectors.toSet()) + .contains(DataType.LATEST.getCode()); + // 待合并log当前变量是否为latest类型 + boolean anotherIsLatest = DataType.isLatest(another.getDataType()); + + // 判断是否需要合并 + boolean shouldMerge = + anotherIsLatest + ? (currentContainsLatest + ? Objects.equals(time, another.getTs()) + : time <= another.getTs()) + : (!currentContainsLatest || time >= another.getTs()); + + if (shouldMerge) { + doMerge(sourceInfoMap, alias, another); + return true; + } else { + return false; + } + } + + private void doMerge( + Map data, String alias, LogSourceInfoValue another) { + data.put(alias, another); + this.time = Math.max(time, another.getTs()); + refreshSourceInfo(data); + } + + private Set getMissingVariables() { + Set aliases = getSourceInfoMap().keySet(); + return getFormulaVariables().stream() + .filter(variable -> !aliases.contains(variable)) + .collect(Collectors.toSet()); + } + + public Set getExistThingAttrs() { + return getSourceInfoMap().values().stream() + .map(e -> e.getThingCode() + ":" + e.getAttrCode()) + .collect(Collectors.toSet()); + } + + public Set getMissingThingAttrs() { + Set missingVariables = getMissingVariables(); + return getRuleDetails().stream() + .filter(detail -> missingVariables.contains(detail.getAttrAlias())) + .map(e -> e.getThingCode() + ":" + e.getAttrCode()) + .collect(Collectors.toSet()); + } + + public Set getDefThingAttrs() { + if (!CollectionUtils.isEmpty(defThingAttrs)) { + return defThingAttrs; + } + defThingAttrs = + getRuleDetails().stream() + .map(e -> e.getThingCode() + ":" + e.getAttrCode()) + .collect(Collectors.toSet()); + return defThingAttrs; + } + + public Map parseSourceInfo() { + if (StringUtils.isBlank(sourceInfo)) { + return new HashMap<>(); + } + this.sourceInfoMap = JSONObject.parseObject(getSourceInfo(), typeRef); + return sourceInfoMap; + } + + private Map parseSourceInfo(String sourceInfo) { + if (StringUtils.isBlank(sourceInfo)) { + return new HashMap<>(); + } + return JSONObject.parseObject(sourceInfo, typeRef); + } + + public void refreshSourceInfo(Map data){ + this.sourceInfoMap = data; + setSourceInfo(JSONObject.toJSONString(data)); + } + + private List getRuleDetails(){ + return filterRule.getDetails(); + } + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleDetailEntity.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleDetailEntity.java new file mode 100644 index 0000000..31a3409 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleDetailEntity.java @@ -0,0 +1,68 @@ +package com.thing.filter.rule.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 过滤规则详情表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("filter_rule_detail") +public class FilterRuleDetailEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 过滤规则id + */ + private Long filterRuleId; + /** + * 物名称 + */ + private String thingName; + /** + * 物编码 + */ + private String thingCode; + /** + * 属性名称 + */ + private String attrName; + /** + * 属性编码 + */ + private String attrCode; + /** + * 属性在计算公式中的别名 + */ + private String attrAlias; + /** + * 数据类型:1-最新,2-最近, 3-区间 + */ + private Integer dataType; + /** + * 聚合类型:SUM|AVG|MAX|MIN|COUNT + */ + private String aggType; + /** + * 聚合时间间隔 + */ + private Integer aggInterval; + /** + * 聚合时间单位:MINUTE|HOUR|DAY|MONTH + */ + private String timeunit; + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleEntity.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleEntity.java new file mode 100644 index 0000000..0b5e2fe --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/entity/FilterRuleEntity.java @@ -0,0 +1,47 @@ +package com.thing.filter.rule.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 过滤规则表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("filter_rule") +public class FilterRuleEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 过滤规则名称 + */ + private String name; + /** + * 计算公式 + */ + private String formula; + /** + * 计算公式中文描述 + */ + private String formulaDescription; + /** + * 备注 + */ + private String remark; + /** + * 是否启用 + */ + private Object enable; +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/DataType.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/DataType.java new file mode 100644 index 0000000..7b3ff16 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/DataType.java @@ -0,0 +1,30 @@ +package com.thing.filter.rule.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author siyang + * @date 2024-03-21 + * @description 规律规则详情中的数据类型 + */ +@Getter +@AllArgsConstructor +public enum DataType { + LATEST(1), + AGG(2), + RANGE(3); + private final int code; + + public static boolean isLatest(int code) { + return code == 1; + } + + public static boolean isAgg(int code) { + return code == 2; + } + + public static boolean isRange(int code) { + return code == 3; + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/LogStatus.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/LogStatus.java new file mode 100644 index 0000000..8499881 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/enumeration/LogStatus.java @@ -0,0 +1,26 @@ +package com.thing.filter.rule.enumeration; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author siyang + * @date 2024-03-21 + * @description 过滤日志状态 + */ +@Getter +@AllArgsConstructor +public enum LogStatus { + /** 不匹配 */ + MISMATCH(1), + /** 已匹配 */ + MATCHED(2), + /** 计算成功 */ + CALC_SUCCESS(3), + /** 计算异常 */ + CALC_EXCEPTION(4), + /** 已通知 */ + NOTIFIED(5); + + private final int code; +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/event/FilterSuccessEvent.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/event/FilterSuccessEvent.java new file mode 100644 index 0000000..fd03eff --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/event/FilterSuccessEvent.java @@ -0,0 +1,27 @@ +package com.thing.filter.rule.event; + +import com.thing.filter.rule.entity.FilterLogEntity; + +import lombok.Getter; + +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +/** + * @author siyang + * @date 2024-03-26 + * @description 过滤成功事件 + */ +@Getter +public class FilterSuccessEvent extends ApplicationEvent { + + public static final String FILTER_SUCCESS = "filter_success"; + + private final List filterLogs; + + public FilterSuccessEvent(Object source, List filterLogs) { + super(source); + this.filterLogs = filterLogs; + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/listener/FilterLogSaveEventListener.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/listener/FilterLogSaveEventListener.java new file mode 100644 index 0000000..5e51953 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/listener/FilterLogSaveEventListener.java @@ -0,0 +1,383 @@ +package com.thing.filter.rule.listener; + +import static com.thing.filter.rule.event.FilterSuccessEvent.FILTER_SUCCESS; + +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; + +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.MapUtil; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; +import com.thing.common.tskv.event.FilterLogSaveEvent; +import com.thing.common.tskv.service.TsKvService; +import com.thing.filter.rule.dto.FilterRuleDTO; +import com.thing.filter.rule.dto.FilterRuleDetailDTO; +import com.thing.filter.rule.entity.FilterLogEntity; +import com.thing.filter.rule.enumeration.LogStatus; +import com.thing.filter.rule.event.FilterSuccessEvent; +import com.thing.filter.rule.service.FilterLogService; +import com.thing.filter.rule.service.FilterRuleService; +import com.thing.queue.util.Topics; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024-03-21 + * @description 过滤日志存储事件监听 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class FilterLogSaveEventListener { + + private final TsKvService tsKvService; + private final FilterLogService filterLogService; + private final FilterRuleService filterRuleService; + private final ApplicationEventPublisher appEventPublisher; + + @EventListener(FilterLogSaveEvent.class) + public void handleEvent(FilterLogSaveEvent event) { + // 初步校验 + Topics topic = Topics.match(event.getSource().toString()); + if (topic != Topics.V1_TSKV_FILTER_LOG_SAVE) { + return; + } + List tsKvList = event.parse2Tskv(); + if (CollectionUtils.isEmpty(tsKvList)) { + return; + } + + List allEnabledRules = filterRuleService.getAllEnabled(); + List dbLogs = filterLogService.findPendingLogs(); + Set dbLogIds = dbLogs.stream().map(FilterLogEntity::getId).collect(Collectors.toSet()); + List filterLogs = matchAndConvert(tsKvList, allEnabledRules, dbLogs); + if (CollectionUtils.isEmpty(filterLogs)) { + return; + } + + // 校验是否匹配完成, 已匹配的修改状态 + List matchedLogs = filterLogs.stream().filter(FilterLogEntity::checkComplete).toList(); + + // 查询tskv,设置聚合值 + fetchAndSetAggValue(matchedLogs); + + // 执行计算、更新result、过滤出待通知的log + List preNotifyLogs = matchedLogs.stream().filter(FilterLogEntity::calculate).toList(); + + // 通知,更新状态 + if (!CollectionUtils.isEmpty(preNotifyLogs)) { + appEventPublisher.publishEvent(new FilterSuccessEvent(FILTER_SUCCESS, preNotifyLogs)); + preNotifyLogs.forEach(log -> log.setStatus(LogStatus.NOTIFIED.getCode())); + } + + saveOrUpdate(dbLogIds, filterLogs); + } + + /** 对每个tskv匹配过滤规则,满足条件的tskv转为log对象 */ + private List matchAndConvert( + List tsKvList, + List allEnabledRules, + List dbLogs) { + if (allEnabledRules.isEmpty()) return null; + // 规则详情 + List ruleDetails = + allEnabledRules.stream() + .map(FilterRuleDTO::getDetails) + .flatMap(List::stream) + .toList(); + // 规则map + Map ruleMap = + allEnabledRules.stream() + .collect(Collectors.toMap(FilterRuleDTO::getId, Function.identity())); + + List logList = loopToGenerateLogs(tsKvList, ruleDetails, ruleMap); + + // 合并日志列表 + return mergeLogs(logList, ruleMap, dbLogs); + } + + private List mergeLogs( + List newLogs, + Map ruleMap, + List dbLogs) { + // 对新数据进来形成的log进行merge + List mergedNewLogs = new ArrayList<>(); + Map> newLogMap = MapUtil.group(newLogs, FilterLogEntity::getFilterRuleId); + newLogMap.forEach( + (ruleId, filterLogs) -> { + Map> thingAttrLogMap = groupLogByThingAttr(filterLogs); + Set logs = findAndMergeLog(filterLogs, thingAttrLogMap, true); + mergedNewLogs.addAll(logs); + }); + + // 对数据库中的log进行merge + if (dbLogs.isEmpty()) { + return mergedNewLogs; + } + + // 设置规则信息,校验log基本信息是否合规 + dbLogs.forEach(log -> log.setFilterRule(ruleMap.get(log.getFilterRuleId()))); + List validateDbLogs = dbLogs.stream().filter(FilterLogEntity::validate).toList(); + if (validateDbLogs.isEmpty()) { + return mergedNewLogs; + } + Map> dbLogMap = MapUtil.group(validateDbLogs, FilterLogEntity::getFilterRuleId); + + // 将数据库中的log与新log进行merge + List resList = new ArrayList<>(); + dbLogMap.forEach( + (ruleId, filterLogs) -> { + Map> thingAttrLogMap = groupLogByThingAttr(mergedNewLogs); + Set logs = findAndMergeLog(filterLogs, thingAttrLogMap, false); + resList.addAll(logs); + }); + + return resList; + } + + /** 循环每个tskv,如果有匹配的规律规则,则生成一批log对象 */ + private List loopToGenerateLogs( + List tsKvList, + List ruleDetails, + Map ruleMap) { + // 配置按物和属性依次分组 + Map>> thingAttrConfigGroup = + ruleDetails.stream() + .collect( + Collectors.groupingBy( + FilterRuleDetailDTO::getThingCode, + Collectors.groupingBy(FilterRuleDetailDTO::getAttrCode))); + + // 循环生成日志对象 + List logList = new ArrayList<>(); + for (TsKvDTO tskv : tsKvList) { + String thingCode = tskv.getThingCode(); + String attrCode = tskv.getAttrKey(); + if (!thingAttrConfigGroup.containsKey(thingCode)) { + continue; + } + Map> attrConfigMap = thingAttrConfigGroup.get(thingCode); + if (!attrConfigMap.containsKey(attrCode)) { + continue; + } + // 一个物及其属性在多个ruleDetail中都存在说明:在不同过滤规则中被用到了 + List detailList = attrConfigMap.get(attrCode); + List currentThingAttrLogs = generateLogs(detailList, ruleMap, tskv); + if (!CollectionUtils.isEmpty(currentThingAttrLogs)) { + logList.addAll(currentThingAttrLogs); + } + } + return logList; + } + + private List generateLogs(List detailList, Map ruleMap, TsKvDTO tskv) { + long now = System.currentTimeMillis(); + return detailList.stream() + .map( + detail -> { + Long filterRuleId = detail.getFilterRuleId(); + FilterRuleDTO rule = ruleMap.get(filterRuleId); + FilterLogEntity filterLog = + new FilterLogEntity() + .setFilterRuleId(filterRuleId) + .setFilterRule(rule) + .setFormula(rule.getFormula()) + .setTime(tskv.getTs()) + .appendSourceInfo(detail, tskv.getTs(), tskv.getVal()) + .setStatus(LogStatus.MISMATCH.getCode()); + filterLog + .setId(new SnowFlakeIDKeyGenerator().nextId()) + .setTenantCode(detail.getTenantCode()) + .setCompanyId(detail.getCompanyId()) + .setDeptId(detail.getDeptId()) + .setCreator(0L) + .setCreateDate(now) + .setUpdater(0L) + .setUpdateDate(now); + return filterLog; + }) + .toList(); + } + + /** + * 在同一过滤规则下,基于sourceInfo中的物属性信息对日志进行分组 + * + * @param logList 日志列表 + * @return key:"thingCode:attrCode" value: logs + */ + private Map> groupLogByThingAttr(List logList) { + Map> resMap = new HashMap<>(); + logList.forEach( + log -> + log.getExistThingAttrs() + .forEach(tag -> resMap.computeIfAbsent(tag, k -> new ArrayList<>()).add(log))); + return resMap; + } + + /** + * 循环log, 借助thingAttrLogMap寻找到可能需要merge的log列表,进行merge + * 注:log与thingAttrLogMap需要在同一过滤规则下 + * + * @param logs 日志列表 + * @param thingAttrLogMap 按物、属性分组后的日志列表 + * @param onlyMergeMissThingAttr 是否只合并缺少的属性物属性 + * @return 合并后的日志列表, 合并逻辑: A merge B 若合并成功, 返回A; 合并失败, 返回A、B; + */ + private Set findAndMergeLog( + List logs, + Map> thingAttrLogMap, + boolean onlyMergeMissThingAttr) { + Set mergedLogs = new HashSet<>(); + Set unMergedLogs = new HashSet<>(); + for (FilterLogEntity currentLog : logs) { + Set thingAttrs; + if(onlyMergeMissThingAttr){ + thingAttrs = currentLog.getMissingThingAttrs(); + if (CollectionUtils.isEmpty(thingAttrs)) { + continue; + } + }else { + thingAttrs = currentLog.getDefThingAttrs(); + } + thingAttrs.forEach( + thingAttr -> { + List toMergeLogs = thingAttrLogMap.get(thingAttr); + if (CollectionUtils.isEmpty(toMergeLogs)) { + return; + } + for (int i = 0; i < toMergeLogs.size(); i++) { + FilterLogEntity toMergeLog = toMergeLogs.get(i); + if (Objects.isNull(toMergeLog) || Objects.equals(currentLog, toMergeLog)) { + continue; + } + boolean merged = currentLog.merge(toMergeLog); + if (merged) { + mergedLogs.add(currentLog); + toMergeLogs.set(i, null); + } else { + unMergedLogs.add(currentLog); + unMergedLogs.add(toMergeLog); + } + } + }); + } + mergedLogs.addAll(unMergedLogs); + return mergedLogs.stream().filter(Objects::nonNull).collect(Collectors.toSet()); + } + + /** + * 查询聚合数据值 + * + * @param filterLogs 日志列表 + */ + private void fetchAndSetAggValue(List filterLogs) { + Map> sameTimeLogMap = MapUtil.group(filterLogs, FilterLogEntity::getTime); + sameTimeLogMap.forEach( + (time, logs) -> { + Map>> map = new HashMap<>(); + logs.forEach(log -> map.putAll(log.getSameAggInfoThingAttrPairs())); + map.forEach( + (aggTag, pairs) -> { + Map tskvMap = findTskv(time, aggTag, pairs); + FilterLogEntity.groupLogByAggInfo(logs) + .get(aggTag) + .forEach(log -> log.updateValue(tskvMap)); + }); + }); + } + + /** 获取整点时间,当前时间四舍五入 */ + private Long getRoundTime(Long ts, String timeUnit) { + LocalDateTime time = DateTimeUtils.parseDateTime(ts); + switch (timeUnit) { + case "HOUR" -> { + if (time.getMinute() >= 30) { + time = time.withHour(time.getHour() + 1); + } + } + case "DAY" -> { + if (time.getHour() >= 12) { + time = time.withDayOfMonth(time.getDayOfMonth() + 1).withHour(0); + } + } + case "MONTH" -> { + int dayCount = time.toLocalDate().lengthOfMonth(); + if (time.getDayOfMonth() >= (dayCount + 1) >> 1) { + time = time.withMonth(time.getMonthValue() + 1).withDayOfMonth(1).withHour(0); + } + } + } + + time = time.withMinute(0).withSecond(0).withNano(0); + return DateTimeUtils.parse(time); + } + + private Long getStartTimeByInterval(Long endTs, Integer interval, String timeUnit) { + LocalDateTime endTime = DateTimeUtils.parseDateTime(endTs); + LocalDateTime startTime; + switch (timeUnit) { + case "MONTH" -> startTime = endTime.minusMonths(interval); + case "DAY" -> startTime = endTime.minusDays(interval); + case "HOUR" -> startTime = endTime.minusHours(interval); + default -> startTime = endTime.minusMinutes(interval); + } + return DateTimeUtils.parse(startTime); + } + + /** + * 查询tskv + * + * @param aggTag 聚合标记,格式为 "interval:aggType" + * @param pairs 物、属性的pair列表 + * @return tskv按物、属性分组的map + */ + private Map findTskv(Long endTs, String aggTag, List> pairs) { + String[] aggInfoArr = aggTag.split(":"); + String aggType = aggInfoArr[0]; + Integer interval = Integer.parseInt(aggInfoArr[1]); + String timeunit = aggInfoArr[2]; + boolean onTime = Boolean.parseBoolean(aggInfoArr[3]); + if (onTime) { + endTs = getRoundTime(endTs, timeunit); + } + Long startTs = getStartTimeByInterval(endTs, interval, timeunit); + Set thingCodes = pairs.stream().map(Pair::getLeft).collect(Collectors.toSet()); + Set attrCodes = pairs.stream().map(Pair::getRight).collect(Collectors.toSet()); + List tskvList = + tsKvService.findTsKvAggByCodesAndAttrs( + thingCodes, attrCodes, startTs, endTs, true, AggType.valueOf(aggType)); + return tskvList.stream() + .collect( + Collectors.toMap( + e -> e.getThingCode() + ":" + e.getAttrKey(), + TsKvEntity::getVal, + (e1, e2) -> e1)); + } + + private void saveOrUpdate(Set dbLogIds, Collection filterLogs) { + Map> saveOrUpdateMap = + filterLogs.stream() + .collect(Collectors.groupingBy(log -> dbLogIds.contains(log.getId()))); + if (!CollectionUtils.isEmpty(saveOrUpdateMap.get(true))) { + filterLogService.updateBatch(saveOrUpdateMap.get(true)); + } + if (!CollectionUtils.isEmpty(saveOrUpdateMap.get(false))) { + // 使用insert update,避免多jvm运行时导致的重复数据插入 + filterLogService.batchSaveOrUpdate(saveOrUpdateMap.get(false)); + } + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterLogMapper.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterLogMapper.java new file mode 100644 index 0000000..5ae8604 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterLogMapper.java @@ -0,0 +1,20 @@ +package com.thing.filter.rule.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.filter.rule.entity.FilterLogEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 过滤日志 +* +* @author siyang 2337720667@qq.com +* @since 5.1 2024-03-21 +*/ +@Mapper +public interface FilterLogMapper extends PowerBaseMapper { + + void batchSaveOrUpdate(@Param("logs") List logs); +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleDetailMapper.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleDetailMapper.java new file mode 100644 index 0000000..c31ce3b --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleDetailMapper.java @@ -0,0 +1,16 @@ +package com.thing.filter.rule.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.filter.rule.entity.FilterRuleDetailEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 过滤规则详情表 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-03-14 +*/ +@Mapper +public interface FilterRuleDetailMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleMapper.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleMapper.java new file mode 100644 index 0000000..2e99831 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/mapper/FilterRuleMapper.java @@ -0,0 +1,16 @@ +package com.thing.filter.rule.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.filter.rule.entity.FilterRuleEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 过滤规则表 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-03-14 +*/ +@Mapper +public interface FilterRuleMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterLogService.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterLogService.java new file mode 100644 index 0000000..099c5b7 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterLogService.java @@ -0,0 +1,24 @@ +package com.thing.filter.rule.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.filter.rule.dto.FilterLogDTO; +import com.thing.filter.rule.entity.FilterLogEntity; + +import java.util.List; +import java.util.Map; + +/** + * 过滤日志 + * + * @author siyang 2337720667@qq.com + * @since 5.1 2024-03-21 + */ +public interface FilterLogService extends IBaseService { + + PageData handlePage(Map params); + + List findPendingLogs(); + + void batchSaveOrUpdate(List logs); +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleDetailService.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleDetailService.java new file mode 100644 index 0000000..e6c6d2c --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleDetailService.java @@ -0,0 +1,18 @@ +package com.thing.filter.rule.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.filter.rule.entity.FilterRuleDetailEntity; + +import java.util.Collection; +import java.util.List; + +/** + * 过滤规则详情表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +public interface FilterRuleDetailService extends IBaseService { + + void deleteByFilterRuleIds(Collection filterRuleIds); +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleService.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleService.java new file mode 100644 index 0000000..fdac79c --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/FilterRuleService.java @@ -0,0 +1,38 @@ +package com.thing.filter.rule.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.filter.rule.dto.FilterRuleDTO; +import com.thing.filter.rule.entity.FilterRuleEntity; + +import java.util.List; +import java.util.Map; + +/** + * 过滤规则表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +public interface FilterRuleService extends IBaseService { + + PageData handlePage(Map params); + + List handleList(Map params); + + FilterRuleDTO handleDetail(Long id); + + List getAllEnabled(); + + void handleSave(FilterRuleDTO filterRuleDTO); + + void handleUpdate(FilterRuleDTO filterRuleDTO); + + void handleDelete(List ids); + + void deleteDetail(Long detailId); + + Boolean checkNameValid(String name); + + List getNameList(); +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterLogServiceImpl.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterLogServiceImpl.java new file mode 100644 index 0000000..7a221ee --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterLogServiceImpl.java @@ -0,0 +1,80 @@ +package com.thing.filter.rule.service.impl; + +import cn.hutool.core.map.MapUtil; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.filter.rule.dto.FilterLogDTO; +import com.thing.filter.rule.entity.FilterLogEntity; +import com.thing.filter.rule.entity.FilterRuleEntity; +import com.thing.filter.rule.enumeration.LogStatus; +import com.thing.filter.rule.mapper.FilterLogMapper; +import com.thing.filter.rule.service.FilterLogService; +import com.thing.sys.security.context.UserContext; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.thing.filter.rule.entity.table.FilterLogEntityTableDef.FILTER_LOG_ENTITY; +import static com.thing.filter.rule.entity.table.FilterRuleEntityTableDef.FILTER_RULE_ENTITY; + +/** + * 过滤日志 + * + * @author siyang 2337720667@qq.com + * @since 5.1 2024-03-21 + */ +@Service +public class FilterLogServiceImpl extends BaseServiceImpl implements FilterLogService { + + /** 计算失败后,计算异常状态下的最大计算次数 */ + private static final Integer MAX_CALC_COUNT = 10; + + @Override + public QueryWrapper getWrapper(Map params) { + String filterRuleName = MapUtil.getStr(params, "filterRuleName"); + Integer status = MapUtil.getInt(params, "status"); + return QueryWrapper.create() + .select( + FILTER_RULE_ENTITY.NAME.as("filter_rule_name"), + FILTER_LOG_ENTITY.ALL_COLUMNS) + .from(FILTER_RULE_ENTITY) + .innerJoin(FILTER_LOG_ENTITY) + .on(FILTER_RULE_ENTITY.ID.eq(FILTER_LOG_ENTITY.FILTER_RULE_ID)) + .eq(FilterRuleEntity::getTenantCode, UserContext.getTenantCode()) + .eq(FilterRuleEntity::getName, filterRuleName, StringUtils.isNotBlank(filterRuleName)) + .eq(FilterLogEntity::getStatus, status, Objects.nonNull(status)) + .orderBy(FilterLogEntity::getCreateDate) + .desc(); + } + + @Override + public PageData handlePage(Map params) { + Page page = getPage(params, FilterLogDTO.class); + Page paginate = mapper.paginateAs(page, getWrapper(params), FilterLogDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public List findPendingLogs() { + return mapper.selectListByQuery( + QueryWrapper.create() + .eq(FilterLogEntity::getStatus, LogStatus.MISMATCH.getCode()) + .or( + FILTER_LOG_ENTITY + .STATUS + .eq(LogStatus.CALC_EXCEPTION.getCode()) + .and(FILTER_LOG_ENTITY.CALC_COUNT.le(MAX_CALC_COUNT)))); + } + + @Override + public void batchSaveOrUpdate(List logs) { + mapper.batchSaveOrUpdate(logs); + } +} diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleDetailServiceImpl.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleDetailServiceImpl.java new file mode 100644 index 0000000..8e38ce0 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleDetailServiceImpl.java @@ -0,0 +1,38 @@ +package com.thing.filter.rule.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.filter.rule.entity.FilterRuleDetailEntity; +import com.thing.filter.rule.mapper.FilterRuleDetailMapper; +import com.thing.filter.rule.service.FilterRuleDetailService; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.Map; + +/** + * 过滤规则详情表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +@Service +public class FilterRuleDetailServiceImpl extends BaseServiceImpl implements FilterRuleDetailService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public void deleteByFilterRuleIds(Collection filterRuleIds) { + if(CollectionUtils.isEmpty(filterRuleIds)){ + return; + } + mapper.deleteByQuery( + QueryWrapper.create().in(FilterRuleDetailEntity::getFilterRuleId, filterRuleIds)); + } +} \ No newline at end of file diff --git a/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleServiceImpl.java b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleServiceImpl.java new file mode 100644 index 0000000..855e2c7 --- /dev/null +++ b/modules/filter-rule/src/main/java/com/thing/filter/rule/service/impl/FilterRuleServiceImpl.java @@ -0,0 +1,234 @@ +package com.thing.filter.rule.service.impl; + +import static com.thing.filter.rule.entity.table.FilterRuleDetailEntityTableDef.FILTER_RULE_DETAIL_ENTITY; +import static com.thing.filter.rule.entity.table.FilterRuleEntityTableDef.FILTER_RULE_ENTITY; + +import cn.hutool.core.map.MapUtil; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.expression.functions.AggFunction; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.filter.rule.dto.FilterRuleDTO; +import com.thing.filter.rule.dto.FilterRuleDetailDTO; +import com.thing.filter.rule.entity.FilterRuleDetailEntity; +import com.thing.filter.rule.entity.FilterRuleEntity; +import com.thing.filter.rule.mapper.FilterRuleMapper; +import com.thing.filter.rule.service.FilterRuleDetailService; +import com.thing.filter.rule.service.FilterRuleService; + +import com.thing.sys.security.context.UserContext; +import lombok.RequiredArgsConstructor; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 过滤规则表 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-03-14 + */ +@Service +@RequiredArgsConstructor +public class FilterRuleServiceImpl extends BaseServiceImpl + implements FilterRuleService { + + private final FilterRuleDetailService filterRuleDetailService; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = MapUtil.getStr(params, "name"); + String thingKeyword = MapUtil.getStr(params, "thingKeyword"); + String attrKeyword = MapUtil.getStr(params, "attrKeyword"); + QueryWrapper wrapper = + QueryWrapper.create() + .select(FILTER_RULE_ENTITY.ALL_COLUMNS) + .from(FILTER_RULE_ENTITY) + .innerJoin(FILTER_RULE_DETAIL_ENTITY) + .on(FILTER_RULE_ENTITY.ID.eq(FILTER_RULE_DETAIL_ENTITY.FILTER_RULE_ID)) + .eq(FilterRuleEntity::getName, name, StringUtils.isNotBlank(name)); + if (StringUtils.isNotBlank(thingKeyword)) { + wrapper.and( + FILTER_RULE_DETAIL_ENTITY + .THING_NAME + .like(thingKeyword) + .or(FILTER_RULE_DETAIL_ENTITY.THING_CODE.like(thingKeyword))); + } + if (StringUtils.isNotBlank(attrKeyword)) { + wrapper.and( + FILTER_RULE_DETAIL_ENTITY + .ATTR_NAME + .like(attrKeyword) + .or(FILTER_RULE_DETAIL_ENTITY.ATTR_CODE.like(attrKeyword))); + } + return wrapper; + } + + @Override + public PageData handlePage(Map params) { + PageData page = selectPage(params); + List list = page.getList(); + fillData(list); + return new PageData<>(list, page.getTotal()); + } + + @Override + public List handleList(Map params) { + List list = listAs(getWrapper(params), FilterRuleDTO.class); + fillData(list); + return list; + } + + @Override + public FilterRuleDTO handleDetail(Long id) { + FilterRuleDTO filterRule = getByIdAs(id, FilterRuleDTO.class); + fillData(Collections.singletonList(filterRule)); + return filterRule; + } + + @Override + @Cacheable(value = CacheNameEnum.FILTER_RULE, key = "'all'") + public List getAllEnabled() { + List list = + listAs( + QueryWrapper.create().eq(FilterRuleEntity::getEnable, true), + FilterRuleDTO.class); + fillData(list); + return list; + } + + @Override + @CacheEvict(value = CacheNameEnum.FILTER_RULE, key = "'all'") + public void handleSave(FilterRuleDTO filterRuleDTO) { + generateDefaultInfo(filterRuleDTO); + // 存储过滤规则 + saveDto(filterRuleDTO); + + // 存储过滤规则详情 + List details = + filterRuleDTO.getDetails().stream() + .map(e -> ConvertUtils.sourceToTarget(e, FilterRuleDetailEntity.class)) + .collect(Collectors.toList()); + filterRuleDetailService.saveBatch(details); + } + + @Override + @CacheEvict(value = CacheNameEnum.FILTER_RULE, key = "'all'") + public void handleUpdate(FilterRuleDTO filterRuleDTO) { + generateDefaultInfo(filterRuleDTO); + // 更新过滤规则 + updateDto(filterRuleDTO); + + // 更新过滤规则详情 + List details = + filterRuleDTO.getDetails().stream() + .map(e -> ConvertUtils.sourceToTarget(e, FilterRuleDetailEntity.class)) + .collect(Collectors.toList()); + filterRuleDetailService.saveOrUpdateBatch(details); + + } + + @Override + @CacheEvict(value = CacheNameEnum.FILTER_RULE, key = "'all'") + public void handleDelete(List ids) { + // 删除过滤规则 + filterRuleDetailService.deleteByFilterRuleIds(ids); + + // 删除过滤规则详情 + mapper.deleteBatchByIds(ids); + } + + @Override + @CacheEvict(value = CacheNameEnum.FILTER_RULE, key = "'all'") + public void deleteDetail(Long detailId) { + FilterRuleDetailEntity detail = filterRuleDetailService.getById(detailId); + FilterRuleDTO filterRule = getByIdAs(detail.getFilterRuleId(), FilterRuleDTO.class); + filterRuleDetailService.getMapper().deleteById(detailId); + List details = + filterRuleDetailService.listAs( + QueryWrapper.create() + .eq(FilterRuleDetailEntity::getFilterRuleId, filterRule.getId()), + FilterRuleDetailDTO.class); + filterRule.setDetails(details); + generateDefaultInfo(filterRule); + updateDto(filterRule); + } + + @Override + public Boolean checkNameValid(String name) { + return mapper.selectCountByQuery(QueryWrapper.create().eq(FilterRuleEntity::getName, name)) == 0; + } + + @Override + @SuppressWarnings("all") + public List getNameList() { + return mapper + .selectListByQuery( + QueryWrapper.create() + .select(FilterRuleEntity::getName) + .eq(FilterRuleEntity::getTenantCode, UserContext.getTenantCode())) + .stream() + .map(FilterRuleEntity::getName) + .collect(Collectors.toList()); + } + + /** + * 当属性是聚合类型时,如果依旧像普通属性一样放在公式描述中,就会显得很别扭。 + * 之前与领导商量采用 {@link AggFunction} 示例中给出的公式写法,不过被否定了,认为用户是傻子,无法理解那种写法【摊手.jpg】 + */ + private void generateDefaultInfo(FilterRuleDTO filterRule) { + filterRule.setId(Optional.ofNullable(filterRule.getId()).orElse(new SnowFlakeIDKeyGenerator().nextId())); + filterRule.setEnable(Optional.ofNullable(filterRule.getEnable()).orElse(true)); + // 生成过滤规则描述 + List details = filterRule.getDetails(); + + String formula = filterRule.getFormula(); + Map variableMap = new HashMap<>(); + for (FilterRuleDetailDTO detail : details) { + detail.setFilterRuleId(filterRule.getId()); + if (!FormulaUtil.validateFormula(formula)) { + throw new SysException("过滤规则填写不合法:" + formula); + } + variableMap.put( + detail.getAttrAlias(), detail.getThingName() + "_" + detail.getAttrName()); + } + filterRule.setFormulaDescription(FormulaUtil.replaceVariable(formula, variableMap)); + } + + private void fillData(List filterRules) { + if (CollectionUtils.isEmpty(filterRules)) { + return; + } + List ruleIds = filterRules.stream().map(FilterRuleDTO::getId).toList(); + List filterRuleDetails = + filterRuleDetailService.listAs( + QueryWrapper.create().in(FilterRuleDetailEntity::getFilterRuleId, ruleIds), + FilterRuleDetailDTO.class); + + Map> detailMap = + filterRuleDetails.stream() + .collect(Collectors.groupingBy(FilterRuleDetailDTO::getFilterRuleId)); + + filterRules.forEach(e -> e.setDetails(detailMap.get(e.getId()))); + } + + private PageData selectPage(Map params) { + Page page = getPage(params, FilterRuleDTO.class); + QueryWrapper queryWrapper = getWrapper(params); + Page paginate = mapper.paginateAs(page, queryWrapper, FilterRuleDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } +} diff --git a/modules/filter-rule/src/main/resources/mapper/FilterLogMapper.xml b/modules/filter-rule/src/main/resources/mapper/FilterLogMapper.xml new file mode 100644 index 0000000..3e3ab43 --- /dev/null +++ b/modules/filter-rule/src/main/resources/mapper/FilterLogMapper.xml @@ -0,0 +1,19 @@ + + + + + + + INSERT INTO filter_log + (id, filter_rule_id, formula, source_info, status, time, result ,error_info, calc_count, + tenant_code, company_id, dept_id, creator, create_date, updater, update_date) + VALUES + + (#{item.id},#{item.filterRuleId},#{item.formula},#{item.sourceInfo},#{item.status},#{item.time},#{item.result} ,#{item.errorInfo}, #{item.calcCount}, + #{item.tenantCode},#{item.companyId},#{item.deptId},#{item.creator},#{item.createDate},#{item.updater},#{item.updateDate}) + + ON CONFLICT (filter_rule_id, time) + DO UPDATE SET create_date = excluded.create_date + + + \ No newline at end of file diff --git a/modules/fix/pom.xml b/modules/fix/pom.xml new file mode 100644 index 0000000..628016f --- /dev/null +++ b/modules/fix/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + fix + jar + ThingBI Server Modules fix + + UTF-8 + + + + + com.thing.common + orm + + + + \ No newline at end of file diff --git a/modules/fix/src/main/java/com/thing/fix/controller/FixRelationController.java b/modules/fix/src/main/java/com/thing/fix/controller/FixRelationController.java new file mode 100644 index 0000000..2b027b9 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/controller/FixRelationController.java @@ -0,0 +1,27 @@ +package com.thing.fix.controller; + +import com.thing.common.core.web.response.Result; +import com.thing.fix.service.FixRelationService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * @author SiYang + * @date 2023/12/28 14:42 + * @description 物关系数据修复控制器 + */ +@RestController +@RequestMapping("v2/fix/relation") +@RequiredArgsConstructor +public class FixRelationController { + + private final FixRelationService fixRelationService; + + @PostMapping("add/rootThingId") + public Result fixAddRootThingId(){ + fixRelationService.fixAddRootThingId(); + return new Result<>(); + } +} diff --git a/modules/fix/src/main/java/com/thing/fix/entity/RelationNode.java b/modules/fix/src/main/java/com/thing/fix/entity/RelationNode.java new file mode 100644 index 0000000..ebbbdc6 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/entity/RelationNode.java @@ -0,0 +1,35 @@ +package com.thing.fix.entity; + + +import com.thing.common.core.utils.params.TreeNode; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * @author SiYang + * @date 2023/12/28 16:51 + * @description 对应 relation_detail 实体 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class RelationNode extends TreeNode { + @Serial + private static final long serialVersionUID = 978787763568355946L; + private Long fromId; + private String fromCode; + private String fromName; + private Long toId; + private String toCode; + private String toName; + private Long rootId; + private String config; + private String remark; + private Long sort; + private Long creator; + private Long createDate; + private Long updater; + private Long updateDate; + private Long rootThingId; +} diff --git a/modules/fix/src/main/java/com/thing/fix/mapper/BaseFixMapper.java b/modules/fix/src/main/java/com/thing/fix/mapper/BaseFixMapper.java new file mode 100644 index 0000000..71b90bd --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/mapper/BaseFixMapper.java @@ -0,0 +1,29 @@ +package com.thing.fix.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * @author SiYang + * @date 2023/12/28 15:10 + * @description 数据修复基础Mapper + */ +@Mapper +public interface BaseFixMapper { + + void addColumn( + @Param("tableName") String tableName, + @Param("columnName") String columnName, + @Param("dataType") String dataType); + + void addComment( + @Param("tableName") String tableName, + @Param("columnName") String columnName, + @Param("comment") String comment); + + void copyTableForBak(@Param("tableName") String tableName, @Param("date") String date); + + void truncateTable(@Param("tableName") String tableName); + + void dropBakTable(@Param("tableName") String tableName); +} diff --git a/modules/fix/src/main/java/com/thing/fix/mapper/FixRelationMapper.java b/modules/fix/src/main/java/com/thing/fix/mapper/FixRelationMapper.java new file mode 100644 index 0000000..e3587e8 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/mapper/FixRelationMapper.java @@ -0,0 +1,23 @@ +package com.thing.fix.mapper; + +import com.thing.fix.entity.RelationNode; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * @author SiYang + * @date 2023/12/28 15:02 + * @description + */ +@Mapper +public interface FixRelationMapper { + + List selectAll(); + + void batchInsertRelationDetail(@Param("rows") List rows); + + long selectAllCount(); + +} diff --git a/modules/fix/src/main/java/com/thing/fix/service/BaseFixService.java b/modules/fix/src/main/java/com/thing/fix/service/BaseFixService.java new file mode 100644 index 0000000..b61f90b --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/service/BaseFixService.java @@ -0,0 +1,36 @@ +package com.thing.fix.service; + +import java.util.Collection; + +/** + * @author SiYang + * @date 2023/12/28 15:19 + * @description + */ +public interface BaseFixService { + + /** + * 新增列 + */ + boolean addColumn(String tableName, String columnName, String dataType); + + /** + * 新增或修改注释 + */ + void addComment(String tableName, String columnName, String comment); + + /** + * copy 备份表,避免因不可抗力产生的数据丢失,那tmd已经不是赔钱能解决的事情了 + */ + boolean copyTableForBak(String tableName, String date); + + /** + * 清空表数据 + */ + void truncateTable(Collection tableNames); + + /** + * 删除备份表 + */ + void dropBakTable(Collection tableNames, String date); +} diff --git a/modules/fix/src/main/java/com/thing/fix/service/FixRelationService.java b/modules/fix/src/main/java/com/thing/fix/service/FixRelationService.java new file mode 100644 index 0000000..f4edaf5 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/service/FixRelationService.java @@ -0,0 +1,12 @@ +package com.thing.fix.service; + + +/** + * @author SiYang + * @date 2023/12/28 14:45 + * @description 物关系数据修复服务 + */ +public interface FixRelationService { + + void fixAddRootThingId(); +} diff --git a/modules/fix/src/main/java/com/thing/fix/service/impl/BaseFixServiceImpl.java b/modules/fix/src/main/java/com/thing/fix/service/impl/BaseFixServiceImpl.java new file mode 100644 index 0000000..97de961 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/service/impl/BaseFixServiceImpl.java @@ -0,0 +1,65 @@ +package com.thing.fix.service.impl; + +import com.thing.fix.mapper.BaseFixMapper; +import com.thing.fix.service.BaseFixService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author SiYang + * @date 2023/12/28 15:20 + * @description + */ +@Slf4j +@Service +public class BaseFixServiceImpl implements BaseFixService { + + @Resource private BaseFixMapper baseFixMapper; + + @Override + public boolean addColumn(String tableName, String columnName, String dataType) { + try { + baseFixMapper.addColumn(tableName, columnName, dataType); + return true; + } catch (Exception e) { + log.warn("data fix ==> add Column error: {}", e.getMessage()); + return false; + } + } + + @Override + public void addComment(String tableName, String columnName, String comment) { + baseFixMapper.addComment(tableName, columnName, comment); + } + + @Override + public boolean copyTableForBak(String tableName, String date) { + try { + String formatDate = date.replaceAll("-", "_"); + baseFixMapper.copyTableForBak(tableName, formatDate); + return true; + } catch (Exception e) { + log.warn("data fix ==> create bak table error: {}", e.getMessage()); + return false; + } + } + + @Override + public void truncateTable(Collection tableNames) { + String tables = String.join(",", tableNames); + baseFixMapper.truncateTable(tables); + } + + public void dropBakTable(Collection tableNames, String date) { + String formatDate = date.replaceAll("-", "_"); + List list = + tableNames.stream().map(t -> t + "_bak_" + formatDate).collect(Collectors.toList()); + String tables = String.join(",", list); + baseFixMapper.dropBakTable(tables); + } +} diff --git a/modules/fix/src/main/java/com/thing/fix/service/impl/FixRelationServiceImpl.java b/modules/fix/src/main/java/com/thing/fix/service/impl/FixRelationServiceImpl.java new file mode 100644 index 0000000..d3541a9 --- /dev/null +++ b/modules/fix/src/main/java/com/thing/fix/service/impl/FixRelationServiceImpl.java @@ -0,0 +1,101 @@ +package com.thing.fix.service.impl; + +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.fix.entity.RelationNode; +import com.thing.fix.mapper.FixRelationMapper; +import com.thing.fix.service.FixRelationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author SiYang + * @date 2023/12/28 14:45 + * @description 物关系数据修复服务 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class FixRelationServiceImpl extends BaseFixServiceImpl implements FixRelationService { + + private final FixRelationMapper fixRelationMapper; + + /** + * 表结构改动: relation_detail 表新增 root_thing_id 字段, 修改注释 + * 数据修复: + * 1. copy 备份表 + * 2. 基于现有表数据计算出需要新增的全量数据 + * 3. 清空现有表数据 + * 4. 将计算得到的最终数据入库 + * 5. 校验入库时是否有数据丢失 + * 6. 删除备份表 + * 7. 出现异常则回滚 + */ + @Transactional + public void fixAddRootThingId() { + String table = "iot_thing_relation_detail"; + + // 添加列 + boolean addColumnSuccess = addColumn(table, "root_thing_id", "bigint"); + if (!addColumnSuccess) { + return; + } + + // 修改注释 + addComment(table, "root_id", "relation_root表主键"); + addComment(table, "root_thing_id", "顶级物id"); + + // copy一份备份表 + LocalDate today = LocalDate.now(); + boolean copyTableSuccess = copyTableForBak(table, today.toString()); + if (!copyTableSuccess) { + return; + } + + // 查询根节点数据 + List fullList = fixRelationMapper.selectAll(); + Map> rootTreeMap = + fullList.stream().collect(Collectors.groupingBy(RelationNode::getRootId)); + + List treeList = new ArrayList<>(); + rootTreeMap.forEach( + (rootId, nodeList) -> { + treeList.addAll(TreeUtils.build(nodeList, rootId)); + }); + + List relationNodes = new ArrayList<>(); + treeList.forEach( + tree -> { + Long rootThingId = tree.getToId(); + List nodes = TreeUtils.flatTree(Collections.singletonList(tree)); + nodes.forEach( + t -> { + t.setId(new SnowFlakeIDKeyGenerator().nextId()); + t.setRootThingId(rootThingId); + }); + relationNodes.addAll(ConvertUtils.sourceToTarget(nodes, RelationNode.class)); + }); + + // 删库跑路 + truncateTable(Collections.singletonList(table)); + + // 挽尊 + fixRelationMapper.batchInsertRelationDetail(relationNodes); + + // 校验入库的数量 + long count = fixRelationMapper.selectAllCount(); + if (count != relationNodes.size()) { + throw new RuntimeException("data fix ==> insert data loss some rows"); + } + } +} diff --git a/modules/fix/src/main/resources/mapper/BaseFixMapper.xml b/modules/fix/src/main/resources/mapper/BaseFixMapper.xml new file mode 100644 index 0000000..51f7229 --- /dev/null +++ b/modules/fix/src/main/resources/mapper/BaseFixMapper.xml @@ -0,0 +1,25 @@ + + + + + + + alter table ${tableName} add ${columnName} ${dataType} + + + + comment on column ${tableName}.${columnName} is '${comment}' ; + + + + create table as (select * from ${tableName}) + + + + truncate table ${tableName} + + + drop table ${tableName} + + + \ No newline at end of file diff --git a/modules/fix/src/main/resources/mapper/FixRelationMapper.xml b/modules/fix/src/main/resources/mapper/FixRelationMapper.xml new file mode 100644 index 0000000..2336efe --- /dev/null +++ b/modules/fix/src/main/resources/mapper/FixRelationMapper.xml @@ -0,0 +1,39 @@ + + + + + + + from_id, from_code, from_name, + to_id, to_code, to_name, + root_id, root_thing_id, sort, + config, remark, + creator, create_date, updater, update_date + + + + + + + + insert into iot_thing_relation_detail(id, ) + values + + (#{row.id}, + #{row.fromId}, #{row.fromCode}, #{row.fromName}, + #{row.toId}, #{row.toCode}, #{row.toName}, + #{row.rootId}, #{row.rootThingId}, #{row.sort}, + #{row.config}, #{row.remark}, + #{row.creator}, #{row.createDate}, #{row.updater}, #{row.updateDate} + ) + + + + \ No newline at end of file diff --git a/modules/mock/pom.xml b/modules/mock/pom.xml new file mode 100644 index 0000000..c934962 --- /dev/null +++ b/modules/mock/pom.xml @@ -0,0 +1,37 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + mock + jar + ThingBI Server Modules mock + + UTF-8 + + + + + com.thing.modules + thing + + + com.thing.modules + quartz + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + + + + \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/controller/MockDataConfigController.java b/modules/mock/src/main/java/com/thing/mock/controller/MockDataConfigController.java new file mode 100644 index 0000000..208e48a --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/controller/MockDataConfigController.java @@ -0,0 +1,134 @@ +package com.thing.mock.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.mock.dto.MockDataConfigDTO; +import com.thing.mock.dto.ReMockRequest; +import com.thing.mock.excel.MockConfigExcel; +import com.thing.mock.service.MockDataConfigService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** +* 数据模拟配置表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@RestController +@RequestMapping("v2/mock/data/config") +@Tag(name="数据模拟配置") +@RequiredArgsConstructor +public class MockDataConfigController { + + private final MockDataConfigService mockDataConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeywords", description = "模糊搜索关键词:物编码、物名称"), + @Parameter(name = "attrKeywords", description = "模糊搜索关键词:物属性名称、物属性编码"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = mockDataConfigService.getPageData(params, MockDataConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + MockDataConfigDTO data = mockDataConfigService.getByIdAs(id, MockDataConfigDTO.class); + return new Result().ok(data); + } + + @PostMapping("batch/saveOrUpdate") + @Operation(summary="批量保存或更新") + @LogOperation("批量保存或更新") + public Result batchSaveOrUpdate(@RequestBody MockDataConfigDTO[] dtoArr){ + mockDataConfigService.batchSaveOrUpdate(List.of(dtoArr)); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody MockDataConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + mockDataConfigService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + mockDataConfigService.batchDelete(ids); + // 删除缓存 + for (Long id : ids) { + mockDataConfigService.removeCache(id); + } + return new Result<>(); + } + + @PostMapping("recalculate") + @Operation(summary="重新计算") + @LogOperation("重新计算") + public Result reCalculate(@RequestBody ReMockRequest request){ + mockDataConfigService.reCalculate(request); + return new Result<>(); + } + + @PostMapping("template/download") + @Operation(summary="模板下载") + @LogOperation("模板下载") + public void templateDownload(HttpServletResponse response) { + mockDataConfigService.templateDownload(response); + } + + @PostMapping("import") + @Operation(summary="导入excel") + public Result importExcel(@RequestParam("file") MultipartFile file) { + List mockConfigExcels = + ExcelUtils.importExcel(file, 0, 1, MockConfigExcel.class); + List configDTOList = MockConfigExcel.toConfigDTO(mockConfigExcels); + mockDataConfigService.batchSaveOrUpdate(configDTOList); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出excel") + @Parameters({ + @Parameter(name = "thingName", description = "物名称"), + @Parameter(name = "thingCode", description = "物编码"), + }) + public void exportExcel(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) { + mockDataConfigService.exportExcel(params, response); + } + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/controller/MockDataLogController.java b/modules/mock/src/main/java/com/thing/mock/controller/MockDataLogController.java new file mode 100644 index 0000000..3637efc --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/controller/MockDataLogController.java @@ -0,0 +1,91 @@ +package com.thing.mock.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.mock.dto.MockDataLogDTO; +import com.thing.mock.service.MockDataLogService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** +* 数据模拟日志表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@RestController +@RequestMapping("v2/mock/data/log") +@Tag(name="数据模拟日志表") +@RequiredArgsConstructor +public class MockDataLogController { + + private final MockDataLogService mockDataLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeywords", description = "模糊搜索关键词:物编码、物名称"), + @Parameter(name = "attrKeywords", description = "模糊搜索关键词:物属性名称、物属性编码"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + params.putIfAbsent(Constant.ORDER_FIELD, "create_date"); + PageData page = mockDataLogService.getPageData(params, MockDataLogDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + MockDataLogDTO data = mockDataLogService.getByIdAs(id, MockDataLogDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody MockDataLogDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + mockDataLogService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody MockDataLogDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + mockDataLogService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + mockDataLogService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/dto/MockDataConfigDTO.java b/modules/mock/src/main/java/com/thing/mock/dto/MockDataConfigDTO.java new file mode 100644 index 0000000..d8f2546 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/dto/MockDataConfigDTO.java @@ -0,0 +1,59 @@ +package com.thing.mock.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 数据模拟配置表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@Data +@Schema(description = "数据模拟配置表") +public class MockDataConfigDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "目标物名称") + private String thingName; + @Schema(description = "目标物编码") + private String thingCode; + @Schema(description = "目标属性名称") + private String attrName; + @Schema(description = "目标属性编码") + private String attrCode; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "数据模拟频率") + private Integer frequency; + @Schema(description = "频率单位:秒、分、时") + private String timeUnit; + @Schema(description = "是否整点:整点的秒=0,分=0、5、15、30、45") + private Boolean onTime; + @Schema(description = "是否启用计算") + private Boolean enable; + @Schema(description = "备注") + private String remark; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/dto/MockDataLogDTO.java b/modules/mock/src/main/java/com/thing/mock/dto/MockDataLogDTO.java new file mode 100644 index 0000000..f5ea5f2 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/dto/MockDataLogDTO.java @@ -0,0 +1,55 @@ +package com.thing.mock.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 数据模拟日志表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@Data +@Schema(description = "数据模拟日志表") +public class MockDataLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "数据模拟配置表id") + private Long mockConfigId; + @Schema(description = "目标物名称") + private String thingName; + @Schema(description = "目标物编码") + private String thingCode; + @Schema(description = "目标属性名称") + private String attrName; + @Schema(description = "目标属性编码") + private String attrCode; + @Schema(description = "计算公式") + private String formula; + @Schema(description = "数据时间") + private Long time; + @Schema(description = "计算结果") + private String result; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/dto/ReMockRequest.java b/modules/mock/src/main/java/com/thing/mock/dto/ReMockRequest.java new file mode 100644 index 0000000..cb7b974 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/dto/ReMockRequest.java @@ -0,0 +1,50 @@ +package com.thing.mock.dto; + +import com.thing.common.core.utils.DateTimeUtils; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * @author siyang + * @date 2024/7/23 14:12 + * @description 重新模拟数据请求 + */ +@Data +@Accessors(chain = true) +@Schema(description = "重新(手动)计算请求") +public class ReMockRequest { + @Schema(description = "计算范围:开始时间") + private String startTime; + + @Schema(description = "计算范围:结束时间") + private String endTime; + + @Schema(description = "模拟数据配置id列表") + private List mockDataConfigIds; + + public List getMockDataConfigIds() { + return mockDataConfigIds.stream().distinct().toList(); + } + + public LocalDateTime getStartDateTime() { + return DateTimeUtils.parseDateTime(startTime); + } + + public LocalDateTime getEndDateTime() { + return DateTimeUtils.parseDateTime(endTime); + } + + public Long getStartTs() { + return DateTimeUtils.convertTimeToLong(startTime); + } + + public Long getEndTs() { + return DateTimeUtils.convertTimeToLong(endTime); + } +} diff --git a/modules/mock/src/main/java/com/thing/mock/entity/MockDataConfigEntity.java b/modules/mock/src/main/java/com/thing/mock/entity/MockDataConfigEntity.java new file mode 100644 index 0000000..ecbe218 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/entity/MockDataConfigEntity.java @@ -0,0 +1,67 @@ +package com.thing.mock.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 数据模拟配置表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("mock_data_config") +public class MockDataConfigEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 目标物名称 + */ + private String thingName; + /** + * 目标物编码 + */ + private String thingCode; + /** + * 目标属性名称 + */ + private String attrName; + /** + * 目标属性编码 + */ + private String attrCode; + /** + * 计算公式 + */ + private String formula; + /** + * 数据模拟频率 + */ + private Integer frequency; + /** + * 频率单位:秒、分、时 + */ + private String timeUnit; + /** + * 是否整点:整点的秒=0,分=0、5、15、30、45 + */ + private Boolean onTime; + /** + * 是否启用计算 + */ + private Boolean enable; + /** + * 备注 + */ + private String remark; +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/entity/MockDataLogEntity.java b/modules/mock/src/main/java/com/thing/mock/entity/MockDataLogEntity.java new file mode 100644 index 0000000..0bab99c --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/entity/MockDataLogEntity.java @@ -0,0 +1,54 @@ +package com.thing.mock.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 数据模拟日志表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("mock_data_log") +public class MockDataLogEntity extends BaseInfoEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 数据模拟配置表id + */ + private Long mockConfigId; + /** + * 计算公式 + */ + private String formula; + /** + * 数据时间 + */ + private Long time; + /** + * 计算结果 + */ + private String result; + + @Column(ignore = true) + private String thingName; + @Column(ignore = true) + private String thingCode; + @Column(ignore = true) + private String attrName; + @Column(ignore = true) + private String attrCode; +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/excel/MockConfigExcel.java b/modules/mock/src/main/java/com/thing/mock/excel/MockConfigExcel.java new file mode 100644 index 0000000..8c401f6 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/excel/MockConfigExcel.java @@ -0,0 +1,86 @@ +package com.thing.mock.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.mock.dto.MockDataConfigDTO; + +import lombok.Data; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/7/24 08:55 + * @description 模拟数据配置excel + */ +@Data +@SuppressWarnings("Duplicates") +public class MockConfigExcel { + + @Excel(name = "目标物名称", orderNum = "1") + private String thingName; + + @Excel(name = "目标物编码", orderNum = "2") + private String thingCode; + + @Excel(name = "目标属性名称", orderNum = "3", width = 12) + private String attrName; + + @Excel(name = "目标属性编码", orderNum = "4", width = 12) + private String attrCode; + + @Excel(name = "计算公式", orderNum = "5", width = 25) + private String formula; + + @Excel(name = "数据模拟频率", orderNum = "6", width = 12) + private Integer frequency; + + @Excel(name = "频率单位(秒、分、时)", orderNum = "7", width = 21) + private String timeUnit; + + @Excel(name = "是否整点(是、否)", orderNum = "8", width = 17) + private String onTime; + + @Excel(name = "备注", orderNum = "9") + private String remark; + + public static List toConfigDTO(List excelList) { + return excelList.stream() + .map( + e -> { + MockDataConfigDTO dto = new MockDataConfigDTO(); + dto.setThingName(e.getThingName()); + dto.setThingCode(e.getThingCode()); + dto.setAttrName(e.getAttrName()); + dto.setAttrCode(e.getAttrCode()); + dto.setFormula(e.getFormula()); + dto.setFrequency(e.getFrequency()); + dto.setTimeUnit(e.getTimeUnit()); + dto.setOnTime("是".equals(e.getOnTime())); + dto.setEnable(true); + dto.setRemark(e.getRemark()); + return dto; + }) + .collect(Collectors.toList()); + } + + public static List fromConfigDTO(List configs) { + return configs.stream() + .map( + e -> { + MockConfigExcel excel = new MockConfigExcel(); + excel.setThingName(e.getThingName()); + excel.setThingCode(e.getThingCode()); + excel.setAttrName(e.getAttrName()); + excel.setAttrCode(e.getAttrCode()); + excel.setFormula(e.getFormula()); + excel.setFrequency(e.getFrequency()); + excel.setTimeUnit(e.getTimeUnit()); + excel.setOnTime(e.getOnTime() ? "是" : "否"); + excel.setRemark(e.getRemark()); + return excel; + }) + .collect(Collectors.toList()); + } +} diff --git a/modules/mock/src/main/java/com/thing/mock/handler/MockHandler.java b/modules/mock/src/main/java/com/thing/mock/handler/MockHandler.java new file mode 100644 index 0000000..ea8b0b9 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/handler/MockHandler.java @@ -0,0 +1,180 @@ +package com.thing.mock.handler; + +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.data.event.QueueConsumerEvent; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.proto.QueueProto.DataProto; +import com.thing.mock.entity.MockDataConfigEntity; +import com.thing.mock.entity.MockDataLogEntity; +import com.thing.mock.service.MockDataConfigService; +import com.thing.queue.util.Topics; + +import jakarta.annotation.Resource; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.math.NumberUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/7/23 14:38 + * @description 数据模拟处理器 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MockHandler { + private final ApplicationEventPublisher publisher; + @Resource @Lazy private MockDataConfigService mockDataConfigService; + + public boolean isOnTime(Long time) { + return isOnTime(DateTimeUtils.parseDateTime(time)); + } + + /** + * 判断给定时间是否整点 + * + * @param time 时间 + */ + public boolean isOnTime(LocalDateTime time) { + return time.getSecond() < 2 && time.getMinute() % 5 == 0; + } + + public MockDataLogEntity mockData(MockDataConfigEntity config, Long currentTs) { + return mockData(config, currentTs, false); + } + + /** + * 模拟数据 + * + * @param config 配置 + * @param currentTs 数据时间 + * @param reMock 是否重新模拟 + * @return 模拟数据日志 + */ + public MockDataLogEntity mockData(MockDataConfigEntity config, Long currentTs, boolean reMock) { + MockDataLogEntity logEntity = null; + Long latestLogTime = mockDataConfigService.getLatestLogTime(config.getId()); + Object result = FormulaUtil.executeFormula(config.getFormula(), null); + if (Objects.isNull(latestLogTime)) { + logEntity = generateLog(config, currentTs, result); + mockDataConfigService.updateLatestLogTime(config.getId(), currentTs); + } else { + // 基于频率判断是否需要mock: 当前时间 - 上次时间 >= 频率 + Long frequencyTime = getFrequencyMillis(config); + if (reMock || currentTs - latestLogTime >= frequencyTime) { + logEntity = generateLog(config, currentTs, result); + // 在重新模拟场景下,数据时间会比最新时间小,这时就不需要更新最新时间 + if(currentTs > latestLogTime){ + mockDataConfigService.updateLatestLogTime(config.getId(), currentTs); + } + } + } + return logEntity; + } + + private MockDataLogEntity generateLog(MockDataConfigEntity config, Long time, Object result) { + MockDataLogEntity logEntity = + new MockDataLogEntity() + .setMockConfigId(config.getId()) + .setFormula(config.getFormula()) + .setTime(time) + .setResult(convertResult(result)); + long currentTs = System.currentTimeMillis(); + logEntity + .setTenantCode(config.getTenantCode()) + .setCompanyId(config.getCompanyId()) + .setDeptId(config.getDeptId()) + .setCreateDate(currentTs) + .setCreator(0L) + .setUpdateDate(currentTs) + .setUpdater(0L); + return logEntity; + } + + private String convertResult(Object result) { + if (Objects.isNull(result)) { + return null; + } + // 控制结果的小数位数 + String strValue = result.toString(); + if (NumberUtils.isCreatable(strValue)) { + BigDecimal value = new BigDecimal(strValue).setScale(6, RoundingMode.HALF_UP); + return value.toPlainString(); + } else { + return strValue; + } + } + + private Long getFrequencyMillis(MockDataConfigEntity config) { + Integer frequency = config.getFrequency(); + Long unitTime = convert2Ms(config.getTimeUnit()); + return frequency * unitTime; + } + + public Long getFrequencySecond(MockDataConfigEntity config) { + Integer frequency = config.getFrequency(); + Long unitTime = convert2Second(config.getTimeUnit()); + return frequency * unitTime; + } + + private Long convert2Ms(String configTimeUnit) { + return switch (configTimeUnit) { + case "秒", "s", "second" -> 1000L; + case "分", "m", "min", "minute" -> 1000L * 60L; + case "时", "h", "hour" -> 1000L * 60L * 60L; + default -> throw new IllegalArgumentException("不支持的时间单位:" + configTimeUnit); + }; + } + + public Long convert2Second(String configTimeUnit) { + return switch (configTimeUnit) { + case "秒", "s", "second" -> 1L; + case "分", "m", "min", "minute" -> 60L; + case "时", "h", "hour" -> 60L * 60L; + default -> throw new IllegalArgumentException("不支持的时间单位:" + configTimeUnit); + }; + } + + public void pushTskv(List configs, List logs) { + List protoList = convertFromLog(configs, logs); + publisher.publishEvent( + new QueueConsumerEvent(Topics.V1_TSKV_HISTORY.getValue(), protoList)); + } + + private List convertFromLog( + List configs, List logs) { + Map configMap = + configs.stream() + .collect( + Collectors.toMap(MockDataConfigEntity::getId, Function.identity())); + return logs.stream() + .map( + l -> { + MockDataConfigEntity config = configMap.get(l.getMockConfigId()); + QueueProto.TsKvProto tsKvProto = + QueueProto.TsKvProto.newBuilder() + .setThingCode(config.getThingCode()) + .setKey(config.getAttrCode()) + .setTs(l.getTime()) + .setVal(l.getResult()) + .build(); + return DataProto.newBuilder().setTskvProto(tsKvProto).build(); + }) + .collect(Collectors.toList()); + } +} diff --git a/modules/mock/src/main/java/com/thing/mock/mapper/MockDataConfigMapper.java b/modules/mock/src/main/java/com/thing/mock/mapper/MockDataConfigMapper.java new file mode 100644 index 0000000..4a75034 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/mapper/MockDataConfigMapper.java @@ -0,0 +1,16 @@ +package com.thing.mock.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.mock.entity.MockDataConfigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 数据模拟配置表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@Mapper +public interface MockDataConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/mapper/MockDataLogMapper.java b/modules/mock/src/main/java/com/thing/mock/mapper/MockDataLogMapper.java new file mode 100644 index 0000000..326b9c9 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/mapper/MockDataLogMapper.java @@ -0,0 +1,16 @@ +package com.thing.mock.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.mock.entity.MockDataLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 数据模拟日志表 +* +* @author lrd dev@lrd.com +* @since 3.0 2024-07-19 +*/ +@Mapper +public interface MockDataLogMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/service/MockDataConfigService.java b/modules/mock/src/main/java/com/thing/mock/service/MockDataConfigService.java new file mode 100644 index 0000000..cc423d7 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/service/MockDataConfigService.java @@ -0,0 +1,37 @@ +package com.thing.mock.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.mock.dto.MockDataConfigDTO; +import com.thing.mock.dto.ReMockRequest; +import com.thing.mock.entity.MockDataConfigEntity; + +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +/** + * 数据模拟配置表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +public interface MockDataConfigService extends IBaseService { + + List getAllEnabled(); + + Long getLatestLogTime(Long configId); + + Long updateLatestLogTime(Long configId, Long time); + + void batchSaveOrUpdate(List dtoList); + + void reCalculate(ReMockRequest request); + + void templateDownload(HttpServletResponse response); + + + void exportExcel(Map params, HttpServletResponse response); + + void removeCache(Long id); +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/service/MockDataLogService.java b/modules/mock/src/main/java/com/thing/mock/service/MockDataLogService.java new file mode 100644 index 0000000..868d1c5 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/service/MockDataLogService.java @@ -0,0 +1,15 @@ +package com.thing.mock.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.mock.entity.MockDataLogEntity; + +/** + * 数据模拟日志表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +public interface MockDataLogService extends IBaseService { + + void deleteByTime(Long id, Long startTs, Long endTs); +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataConfigServiceImpl.java b/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataConfigServiceImpl.java new file mode 100644 index 0000000..7bbe2d4 --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataConfigServiceImpl.java @@ -0,0 +1,181 @@ +package com.thing.mock.service.impl; + +import static com.thing.mock.entity.table.MockDataConfigEntityTableDef.MOCK_DATA_CONFIG_ENTITY; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.FormulaUtil; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.mock.dto.MockDataConfigDTO; +import com.thing.mock.dto.ReMockRequest; +import com.thing.mock.entity.MockDataConfigEntity; +import com.thing.mock.entity.MockDataLogEntity; +import com.thing.mock.excel.MockConfigExcel; +import com.thing.mock.handler.MockHandler; +import com.thing.mock.mapper.MockDataConfigMapper; +import com.thing.mock.service.MockDataConfigService; +import com.thing.mock.service.MockDataLogService; + +import com.thing.sys.security.context.UserContext; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 数据模拟配置表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +@Service +@SuppressWarnings("Duplicates") +@RequiredArgsConstructor +public class MockDataConfigServiceImpl extends BaseServiceImpl implements MockDataConfigService { + private final MockHandler mockHandler; + private final MockDataLogService mockDataLogService; + + @Override + public QueryWrapper getWrapper(Map params) { + String thingKeywords = MapUtils.getString(params, "thingKeywords"); + String attrKeywords = MapUtils.getString(params, "attrKeywords"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(MockDataConfigEntity::getTenantCode, UserContext.getRealTenantCode()); + if (StringUtils.isNotBlank(thingKeywords)) { + wrapper.and(MOCK_DATA_CONFIG_ENTITY.THING_NAME.like(thingKeywords) + .or(MOCK_DATA_CONFIG_ENTITY.THING_CODE.like(thingKeywords))); + } + if (StringUtils.isNotBlank(attrKeywords)) { + wrapper.and(MOCK_DATA_CONFIG_ENTITY.ATTR_NAME.like(attrKeywords) + .or(MOCK_DATA_CONFIG_ENTITY.ATTR_CODE.like(attrKeywords))); + } + return wrapper; + } + + @Override + public void reCalculate(ReMockRequest request) { + List configs = listByIds(request.getMockDataConfigIds()); + LocalDateTime start = request.getStartDateTime(); + LocalDateTime end = request.getEndDateTime(); + if (!start.isBefore(end)) { + throw new IllegalArgumentException("时间范围错误"); + } + List logs = + configs.stream() + .map( + config -> { + List tsList = + DateTimeUtils.generateTs( + start, + end, + x -> x.plusSeconds(mockHandler.getFrequencySecond(config))); + return tsList.stream() + .map( + ts -> { + if (config.getOnTime() && mockHandler.isOnTime(ts)) { + return mockHandler.mockData(config, ts, true); + } else if (!config.getOnTime()) { + return mockHandler.mockData(config, ts, true); + } + return null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + }) + .flatMap(List::stream) + .filter(Objects::nonNull) + .toList(); + + // 先删除再保存 + configs.forEach( + cfg -> + mockDataLogService.deleteByTime( + cfg.getId(), request.getStartTs(), request.getEndTs())); + mockDataLogService.saveBatch(logs); + + // 推送数据 + mockHandler.pushTskv(configs, logs); + } + + @Override + public void templateDownload(HttpServletResponse response) { + ExcelUtils.exportExcel( + Collections.emptyList(), + null, + "模拟数据配置", + MockConfigExcel.class, + "模拟数据配置.xls", + response); + } + + @Override + public void exportExcel(Map params, HttpServletResponse response) { + List configs = listAs(params, MockDataConfigDTO.class); + List excelData = MockConfigExcel.fromConfigDTO(configs); + ExcelUtils.exportExcel( + excelData, null, "模拟数据配置", MockConfigExcel.class, "模拟数据配置.xls", response); + } + + @Override + @CacheEvict(value = CacheNameEnum.MOCK_DATA_CONFIG, key = "'all'") + public void batchSaveOrUpdate(List dtoList) { + List entities = + ConvertUtils.sourceToTarget(dtoList, MockDataConfigEntity.class); + List invalidFormula = new ArrayList<>(1); + entities.stream() + .peek( + e -> { + try { + FormulaUtil.executeFormula(e.getFormula(), null); + } catch (Exception ex) { + invalidFormula.add(e.getFormula()); + } + }) + .filter(e -> Objects.isNull(e.getId())) + .filter(Objects::nonNull) + .forEach(e -> e.setEnable(true)); + if (CollectionUtils.isNotEmpty(invalidFormula)) { + throw new SysException("公式填写错误:" + invalidFormula); + } + saveOrUpdateBatch(entities); + } + + @Override + @Cacheable(value = CacheNameEnum.MOCK_DATA_CONFIG, key = "'all'") + public List getAllEnabled() { + return list(QueryWrapper.create().eq(MockDataConfigEntity::getEnable, true)); + } + + @Override + @Cacheable(value = CacheNameEnum.MOCK_DATA_LATEST_TIME, key = "#configId") + public Long getLatestLogTime(Long configId) { + return null; + } + + @Override + @CachePut(value = CacheNameEnum.MOCK_DATA_LATEST_TIME, key = "#configId") + public Long updateLatestLogTime(Long configId, Long time) { + return time; + } + + @Override + @CacheEvict(value = CacheNameEnum.MOCK_DATA_LATEST_TIME, key = "#id") + public void removeCache(Long id) { + + } +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataLogServiceImpl.java b/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataLogServiceImpl.java new file mode 100644 index 0000000..12214bb --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/service/impl/MockDataLogServiceImpl.java @@ -0,0 +1,70 @@ +package com.thing.mock.service.impl; + +import static com.thing.mock.entity.table.MockDataConfigEntityTableDef.MOCK_DATA_CONFIG_ENTITY; +import static com.thing.mock.entity.table.MockDataLogEntityTableDef.MOCK_DATA_LOG_ENTITY; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.mock.entity.MockDataLogEntity; +import com.thing.mock.mapper.MockDataLogMapper; +import com.thing.mock.service.MockDataLogService; +import com.thing.sys.security.context.UserContext; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 数据模拟日志表 + * + * @author lrd dev@lrd.com + * @since 3.0 2024-07-19 + */ +@Service +public class MockDataLogServiceImpl extends BaseServiceImpl implements MockDataLogService { + + @Override + public QueryWrapper getWrapper(Map params){ + String thingKeywords = MapUtils.getString(params, "thingKeywords"); + String attrKeywords = MapUtils.getString(params, "attrKeywords"); + QueryWrapper queryWrapper = + QueryWrapper.create() + .select( + MOCK_DATA_CONFIG_ENTITY.THING_NAME, + MOCK_DATA_CONFIG_ENTITY.THING_CODE, + MOCK_DATA_CONFIG_ENTITY.ATTR_NAME, + MOCK_DATA_CONFIG_ENTITY.ATTR_CODE, + MOCK_DATA_LOG_ENTITY.ALL_COLUMNS) + .from(MOCK_DATA_CONFIG_ENTITY) + .innerJoin(MOCK_DATA_LOG_ENTITY) + .on(MOCK_DATA_CONFIG_ENTITY.ID.eq(MOCK_DATA_LOG_ENTITY.MOCK_CONFIG_ID)) + .eq(MockDataLogEntity::getTenantCode, UserContext.getRealTenantCode()); + if (StringUtils.isNotBlank(thingKeywords)) { + queryWrapper.and( + MOCK_DATA_CONFIG_ENTITY + .THING_NAME + .like(thingKeywords) + .or(MOCK_DATA_CONFIG_ENTITY.THING_CODE.like(thingKeywords))); + } + if (StringUtils.isNotBlank(attrKeywords)) { + queryWrapper.and( + MOCK_DATA_CONFIG_ENTITY + .ATTR_NAME + .like(attrKeywords) + .or(MOCK_DATA_CONFIG_ENTITY.ATTR_CODE.like(attrKeywords))); + } + return queryWrapper; + } + + + @Override + public void deleteByTime(Long configId, Long startTs, Long endTs) { + mapper.deleteByQuery( + QueryWrapper.create() + .eq(MockDataLogEntity::getMockConfigId, configId) + .ge(MockDataLogEntity::getTime, startTs) + .le(MockDataLogEntity::getTime, endTs)); + } +} \ No newline at end of file diff --git a/modules/mock/src/main/java/com/thing/mock/task/MockDataTask.java b/modules/mock/src/main/java/com/thing/mock/task/MockDataTask.java new file mode 100644 index 0000000..9e3f2ed --- /dev/null +++ b/modules/mock/src/main/java/com/thing/mock/task/MockDataTask.java @@ -0,0 +1,60 @@ +package com.thing.mock.task; + +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.mock.entity.MockDataConfigEntity; +import com.thing.mock.entity.MockDataLogEntity; +import com.thing.mock.handler.MockHandler; +import com.thing.mock.service.MockDataConfigService; +import com.thing.mock.service.MockDataLogService; +import com.thing.quartz.timetask.task.ITask; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Component; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * @author siyang + * @date 2024/7/22 11:27 + * @description 模拟数据生成任务 + */ +@Slf4j +@RequiredArgsConstructor +@Component("MockDataTask") +public class MockDataTask implements ITask { + + private final MockHandler mockHandler; + private final MockDataLogService mockDataLogService; + private final MockDataConfigService mockDataConfigService; + + + @Override + public void run(String params) { + List configs = mockDataConfigService.getAllEnabled(); + LocalDateTime now = LocalDateTime.now(); + long currentTs = DateTimeUtils.parse(now); + List logs = new ArrayList<>(); + configs.forEach( + c -> { + if (c.getOnTime() && mockHandler.isOnTime(now)) { + logs.add(mockHandler.mockData(c, currentTs)); + } else if (!c.getOnTime()) { + logs.add(mockHandler.mockData(c, currentTs)); + } + }); + List preSaveLogs = logs.stream().filter(Objects::nonNull).toList(); + if (CollectionUtils.isEmpty(preSaveLogs)) { + return; + } + mockDataLogService.saveBatch(preSaveLogs); + + // 推送tskv消息 + mockHandler.pushTskv(configs, preSaveLogs); + } +} diff --git a/modules/msg/pom.xml b/modules/msg/pom.xml new file mode 100644 index 0000000..ec817c4 --- /dev/null +++ b/modules/msg/pom.xml @@ -0,0 +1,74 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + msg + jar + ThingBI Server Modules msg + + UTF-8 + 3.2.0 + 7.2.27 + 4.6.3 + 1.6.2 + + + + + com.thing.common + core + + + com.thing.common + orm + + + com.thing.common + cache + + + com.qiniu + qiniu-java-sdk + ${qiniu.version} + + + com.aliyun + aliyun-java-sdk-core + ${aliyun.core.version} + + + com.aliyun + aliyun-java-sdk-dysmsapi + ${aliyun.dysmsapi.version} + + + com.github.qcloudsms + qcloudsms + ${qcloud.qcloudsms.version} + + + com.sun.mail + javax.mail + ${mail.version} + + + org.freemarker + freemarker + ${freemarker.version} + + + com.sun.mail + jakarta.mail + + + + + \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/controller/MsgCacheController.java b/modules/msg/src/main/java/com/thing/msg/cache/controller/MsgCacheController.java new file mode 100644 index 0000000..ff809cd --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/controller/MsgCacheController.java @@ -0,0 +1,99 @@ +package com.thing.msg.cache.controller; + + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.cache.dto.MsgCacheDTO; +import com.thing.msg.cache.service.MsgCacheService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Map; + + +/** +* 消息缓存 +* +* @author zy zy@gmail.com +* @since 3.0 2021-12-31 +*/ +@RestController +@RequestMapping("msgcache/msgcache") +@Tag(name="消息缓存") +public class MsgCacheController { + @Autowired + private MsgCacheService msgCacheService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = msgCacheService.getPageData(params,MsgCacheDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + MsgCacheDTO data = msgCacheService.getByIdAs(id,MsgCacheDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody MsgCacheDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + msgCacheService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody MsgCacheDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgCacheService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + msgCacheService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("syncCache") + @Operation(summary="立即更新模板缓存") + public Result syncCache(){ + msgCacheService.refreshToken(null); + return new Result(); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/dto/MsgCacheDTO.java b/modules/msg/src/main/java/com/thing/msg/cache/dto/MsgCacheDTO.java new file mode 100644 index 0000000..1018344 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/dto/MsgCacheDTO.java @@ -0,0 +1,46 @@ +package com.thing.msg.cache.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 消息缓存 +* +* @author zy zy@gmail.com +* @since 3.0 2021-12-31 +*/ +@Data +@Schema( name= "消息缓存") +public class MsgCacheDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "缓存键") + private String cacheKey; + @Schema(description = "键类型 0:token 1:模板") + private String cacheKeyType; + @Schema(description = "缓存内容") + private String cacheValue; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "企业ID") + private Long companyId; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/entity/MsgCacheEntity.java b/modules/msg/src/main/java/com/thing/msg/cache/entity/MsgCacheEntity.java new file mode 100644 index 0000000..c3c7b14 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/entity/MsgCacheEntity.java @@ -0,0 +1,39 @@ +package com.thing.msg.cache.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 消息缓存 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-31 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_cache") +public class MsgCacheEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 缓存键 + */ + private String cacheKey; + /** + * 键类型 0:token 1:模板 + */ + private String cacheKeyType; + /** + * 缓存内容 + */ + private String cacheValue; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/mapper/MsgCacheMapper.java b/modules/msg/src/main/java/com/thing/msg/cache/mapper/MsgCacheMapper.java new file mode 100644 index 0000000..d01da47 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/mapper/MsgCacheMapper.java @@ -0,0 +1,16 @@ +package com.thing.msg.cache.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.msg.cache.entity.MsgCacheEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 消息缓存 +* +* @author zy zy@gmail.com +* @since 3.0 2021-12-31 +*/ +@Mapper +public interface MsgCacheMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/service/MsgCacheService.java b/modules/msg/src/main/java/com/thing/msg/cache/service/MsgCacheService.java new file mode 100644 index 0000000..a3168e1 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/service/MsgCacheService.java @@ -0,0 +1,21 @@ +package com.thing.msg.cache.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.cache.entity.MsgCacheEntity; +import com.thing.msg.push.entity.MsgPushEntity; + +import java.util.List; + +/** + * 消息缓存 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-31 + */ +public interface MsgCacheService extends IBaseService { + void syncCache(String key, String value, String cacheType); + + String getCache(String key, String cacheType); + + void refreshToken(List msgPushEntities); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/cache/service/impl/MsgCacheServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/cache/service/impl/MsgCacheServiceImpl.java new file mode 100644 index 0000000..296ff11 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/cache/service/impl/MsgCacheServiceImpl.java @@ -0,0 +1,204 @@ +package com.thing.msg.cache.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.cache.entity.MsgCacheEntity; +import com.thing.msg.cache.mapper.MsgCacheMapper; +import com.thing.msg.cache.service.MsgCacheService; +import com.thing.msg.param.redis.MsgSysParamsRedis; +import com.thing.msg.push.dto.WeChatParams; +import com.thing.msg.push.entity.MsgPushEntity; +import com.thing.msg.push.mapper.MsgPushMapper; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * 消息缓存 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-31 + */ +@Slf4j +@Service +public class MsgCacheServiceImpl extends BaseServiceImpl implements MsgCacheService { + + @Value("${spring.cache.type}") + private String cacheType; + + @Autowired + private MsgPushMapper msgPushMapper; + + @Autowired + private RestTemplate restTemplate; + + private static String ACCESS_TOKEN = "?access_token="; + private static String APPID = "&appid="; + private static String APPSECRET = "&secret="; + + + private MsgSysParamsRedis getMsgSysParamsRedis(){ + return SpringContextUtils.getBean(MsgSysParamsRedis.class); + } + + private boolean inUsingRedis(){ + return "redis".equals(cacheType); + } + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void syncCache(String key, String value, String cacheType) { + // 如果库不存在则新增并加入缓存 + MsgCacheEntity msgCacheEntity = getMsgCache(key, cacheType); + if (Objects.isNull(msgCacheEntity)) { + msgCacheEntity = new MsgCacheEntity(); + msgCacheEntity.setCacheKey(key); + msgCacheEntity.setCacheValue(value); + msgCacheEntity.setCacheKeyType(cacheType); + mapper.insert(msgCacheEntity); + } else { + msgCacheEntity.setCacheKey(key); + msgCacheEntity.setCacheValue(value); + msgCacheEntity.setCacheKeyType(cacheType); + mapper.update(msgCacheEntity); + } + + // 不设置默认为1800秒 + // 放入缓存 过期时间设置小一点避免和token的真正过期时间冲突 + if (inUsingRedis()) { + getMsgSysParamsRedis().setByKey(key, value, null); + } + } + + + @Override + public String getCache(String key, String cacheType) { + String value = inUsingRedis() ? getMsgSysParamsRedis().getByKey(key) : null; + if (StringUtils.isBlank(value)) { + MsgCacheEntity msgCacheEntity = getMsgCache(key, cacheType); + // 定时刷新token 查不到数据库数据的情况很少 + if (!Objects.isNull(msgCacheEntity)) { + value = msgCacheEntity.getCacheValue(); + } + } + + return value; + } + + public MsgCacheEntity getMsgCache(String key, String cacheType) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("cache_key", key); + queryWrapper.eq("cache_key_type", cacheType); + MsgCacheEntity msgCacheEntity = mapper.selectOneByQuery(queryWrapper); + return msgCacheEntity; + } + + + @Override + public synchronized void refreshToken(List msgPushEntities) { + // 如果存在微信公众号推送配置 则尝试初始化token + List msgPushEntityList; + if (CollUtil.isEmpty(msgPushEntities)) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("config_type", "2"); + msgPushEntityList = msgPushMapper.selectListByQuery(queryWrapper); + } else { + msgPushEntityList = msgPushEntities; + } + + if (CollUtil.isEmpty(msgPushEntityList)) { + return; + } + + Map cacheUpdateMap = new HashMap<>(); + + for (MsgPushEntity msgPushEntity : msgPushEntityList) { + WeChatParams weChatParams = null; + try { + weChatParams = JSONObject.parseObject(msgPushEntity.getConfigParams(), WeChatParams.class); + } catch (Exception e) { + log.error("configParams转化失败"); + continue; + } + // 缓存token + String accessToken = ""; + if (!cacheUpdateMap.containsKey(weChatParams.getAppId())) { + accessToken = getAccessToken(weChatParams.getAppId(), weChatParams.getAppSecret()); + if (StringUtils.isNotBlank(accessToken)) { + syncCache(weChatParams.getAppId(), accessToken, "0"); + // 更新了的缓存放入map不再更新 + cacheUpdateMap.put(weChatParams.getAppId(), true); + } + } + + // 缓存微信公众号消息模板(消息模板开放接口调用次数较少 不能频繁去调用 需要缓存起来 定时去更新 也可以手动通过设置消息推送的配置项触发) + List template = getTemplate(accessToken); + if (CollUtil.isEmpty(template)) { + continue; + } + for (Map obj : template) { + String key = String.valueOf(obj.get("template_id")); + if (cacheUpdateMap.containsKey(key)) { + continue; + } + String value = String.valueOf(obj.get("content")); + syncCache(key, value, "1"); + // 更新了的缓存放入map不再更新 + cacheUpdateMap.put(key, true); + } + } + } + + private String getAccessToken(String appId, String appSecret) { + String api = Constant.API_GET_TOKEN + APPID + appId + APPSECRET + appSecret; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity requestEntity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(api, HttpMethod.GET, requestEntity, JSONObject.class); + + String accessToken = ""; + if (!Objects.isNull(response) && response.getStatusCode().value() == 200 && !Objects.isNull(response.getBody()) && !Objects.isNull(response.getBody().get("access_token"))) { + accessToken = String.valueOf(response.getBody().get("access_token")); + } + + return accessToken; + } + + + private List getTemplate(String accessToken) { + String api = Constant.API_GET_TEMPLATELIST + ACCESS_TOKEN + accessToken; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity requestEntity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(api, HttpMethod.GET, requestEntity, JSONObject.class); + + List templateList = null; + if (!Objects.isNull(response) && response.getStatusCode().value() == 200 && !Objects.isNull(response.getBody()) && !Objects.isNull(response.getBody().get("template_list"))) { + templateList = (List) response.getBody().get("template_list"); + } + + return templateList; + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/controller/MsgHisController.java b/modules/msg/src/main/java/com/thing/msg/history/controller/MsgHisController.java new file mode 100644 index 0000000..b8b2f63 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/controller/MsgHisController.java @@ -0,0 +1,92 @@ +package com.thing.msg.history.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.service.MsgHisService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Map; + + +/** +* 推送消息记录 +* +* @author zy zy@xxx.com +* @since 3.0 2021-12-28 +*/ +@RestController +@RequestMapping("msghis/msghis") +@Tag(name="推送消息记录") +public class MsgHisController { + @Autowired + private MsgHisService msgHisService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "beginTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public Result> page( @RequestParam Map params){ + PageData page = msgHisService.getPageData(params,MsgHisDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + MsgHisDTO data = msgHisService.getByIdAs(id,MsgHisDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody MsgHisDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + msgHisService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody MsgHisDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgHisService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + msgHisService.removeByIds(Arrays.asList(ids)); + return new Result(); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/dto/MsgHisDTO.java b/modules/msg/src/main/java/com/thing/msg/history/dto/MsgHisDTO.java new file mode 100644 index 0000000..5c67e57 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/dto/MsgHisDTO.java @@ -0,0 +1,62 @@ +package com.thing.msg.history.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 推送消息记录 +* +* @author zy zy@xxx.com +* @since 3.0 2021-12-28 +*/ +@Data +@Schema( name= "推送消息记录") +public class MsgHisDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "推送方编码") + private String publisherCode; + @Schema(description = "接收方编码") + private String receiverCode; + @Schema(description = "推送方") + private String publisher; + @Schema(description = "接收方") + private String receiver; + @Schema(description = "推送历史记录内容") + private String hisMsg; + @Schema(description = "推送配置项id") + private Long pushConfigId; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "企业ID") + private Long companyId; + @Schema(description = "推送状态") + private String status; + @Schema(description = "状态信息") + private String statusMsg; + @Schema(description = "推送设置id") + private Long pushSettingId; + @Schema(description = "推送接收状态") + private String pushReceiveStatus; + @Schema(description = "推送次数") + private String pushNum; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/entity/MsgHisEntity.java b/modules/msg/src/main/java/com/thing/msg/history/entity/MsgHisEntity.java new file mode 100644 index 0000000..20af70c --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/entity/MsgHisEntity.java @@ -0,0 +1,77 @@ +package com.thing.msg.history.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 推送消息记录 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-28 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_his") +public class MsgHisEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 推送方编码 + */ + private String publisherCode; + /** + * 接收方编码 + */ + private String receiverCode; + + /** + * 推送方 + */ + private String publisher; + /** + * 接收方 + */ + private String receiver; + /** + * 推送历史记录内容 + */ + private String hisMsg; + /** + * 推送配置项id + */ + private String pushConfigId; + + + /** + * 推送状态 + */ + private String status; + + /** + * 状态信息 + */ + private String statusMsg; + + /** + * 推送设置id + */ + private Long pushSettingId; + + /** + * 推送接收状态 + */ + private String pushReceiveStatus; + + /** + * 推送次数 + */ + private String pushNum; +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/mapper/MsgHisMapper.java b/modules/msg/src/main/java/com/thing/msg/history/mapper/MsgHisMapper.java new file mode 100644 index 0000000..9f4cfcf --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/mapper/MsgHisMapper.java @@ -0,0 +1,19 @@ +package com.thing.msg.history.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.entity.MsgHisEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 推送消息记录 +* +* @author zy zy@xxx.com +* @since 3.0 2021-12-28 +*/ +@Mapper +public interface MsgHisMapper extends PowerBaseMapper { + List getRepeatPushMsgList(); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/service/MsgHisService.java b/modules/msg/src/main/java/com/thing/msg/history/service/MsgHisService.java new file mode 100644 index 0000000..49e9c8b --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/service/MsgHisService.java @@ -0,0 +1,27 @@ +package com.thing.msg.history.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.push.dto.DingTalkParams; +import com.thing.msg.push.dto.WeChatComParams; +import org.springframework.http.HttpHeaders; + +import java.util.List; + +/** + * 推送消息记录 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-28 + */ +public interface MsgHisService extends IBaseService { + + void sendWxMsg(MsgHisEntity msgHisEntity, String api, String paramJson, HttpHeaders headers); + + void sendDingTalkMsg(MsgHisEntity msgHisEntity, DingTalkParams dingTalkParams); + + void sendWeChatComMsg(MsgHisEntity msgHisEntity, WeChatComParams weChatComParams); + + List getRepeatPushMsgList(); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/history/service/impl/MsgHisServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/history/service/impl/MsgHisServiceImpl.java new file mode 100644 index 0000000..d39b326 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/history/service/impl/MsgHisServiceImpl.java @@ -0,0 +1,191 @@ +package com.thing.msg.history.service.impl; + +import cn.hutool.http.HttpUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.clickhouse.client.internal.apache.hc.client5.http.utils.Base64; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.mapper.MsgHisMapper; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.DingTalkParams; +import com.thing.msg.push.dto.WeChatComParams; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 推送消息记录 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-28 + */ +@Slf4j +@Service +public class MsgHisServiceImpl extends BaseServiceImpl implements MsgHisService { + + @Autowired + private RestTemplate restTemplate; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + if (StringUtils.isNotBlank((String) params.get("beginTime")) && StringUtils.isNotBlank((String) params.get("endTime"))) { + Date beginTime = DateTimeUtils.parse((String) params.get("beginTime"), DateTimeUtils.DATE_TIME_PATTERN_STR); + Date endTime = DateTimeUtils.parse((String) params.get("endTime"), DateTimeUtils.DATE_TIME_PATTERN_STR); + wrapper.between("create_date", beginTime, endTime); + } + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendWxMsg(MsgHisEntity msgHisEntity, String api, String paramJson, HttpHeaders headers){ + HttpEntity requestEntity = new HttpEntity<>(paramJson, headers); + ResponseEntity response = restTemplate.postForEntity(api, requestEntity, JSONObject.class); + if (Objects.isNull(response) + || response.getStatusCode().value() != 200 + || Objects.isNull(response.getBody()) + || Objects.isNull(response.getBody().get("errcode")) + || Integer.parseInt(String.valueOf(response.getBody().get("errcode"))) != 0) { + msgHisEntity.setStatus(PushStatus.FAIL.getStatus()); + msgHisEntity.setStatusMsg("微信公众号端返回错误,请检查相关配置"); + } + this.save(msgHisEntity); + } + @Override + @Transactional(rollbackFor = Exception.class) + public void sendDingTalkMsg(MsgHisEntity msgHisEntity, DingTalkParams dingTalkParams){ + //通知具体人的手机号码列表 + List mobileList = Arrays.asList(dingTalkParams.getAtMobilesStr().split(",")); + //组装请求内容 + String reqStr = buildReqStr(dingTalkParams, mobileList); + + String sign = null; + Long timestamp = System.currentTimeMillis(); + if (StringUtils.isNotBlank(dingTalkParams.getSign())) { + try { + String secret = dingTalkParams.getSign(); + String stringToSign = timestamp + "\n" + secret; + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256")); + byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8)); + sign = URLEncoder.encode(new String(Base64.encodeBase64(signData)), StandardCharsets.UTF_8); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + //推送消息(http请求) + String url = StringUtils.isNotBlank(sign) ? dingTalkParams.getWebhookUrl().concat("×tamp=").concat(String.valueOf(timestamp)).concat("&sign=").concat(sign) : dingTalkParams.getWebhookUrl(); + String result = HttpUtil.post(url, reqStr); + Map resultMap; + try{ + resultMap = JSONObject.parseObject(result, HashMap.class); + }catch (Exception e){ + throw new SysException("钉钉消息推送结果数据解析失败"); + } + if(Objects.isNull(resultMap) || Objects.isNull(resultMap.get("errcode")) || !"0".equals(String.valueOf(resultMap.get("errcode")))){ + throw new SysException("钉钉消息推送失败"); + } + this.save(msgHisEntity); + } + + @Override + public void sendWeChatComMsg(MsgHisEntity msgHisEntity, WeChatComParams weChatComParams) { + //通知具体人的手机号码列表 + List mobileList = Lists.newArrayList(weChatComParams.getAtMobilesStr().split(",")); + //组装请求内容 + String reqStr = buildWeChatComReqStr(weChatComParams, mobileList); + + //推送消息(http请求) + String url = weChatComParams.getWebhookUrl(); + String result = HttpUtil.post(url, reqStr); + Map resultMap; + try{ + resultMap = JSONObject.parseObject(result, HashMap.class); + }catch (Exception e){ + throw new SysException("企业微信推送结果数据解析失败"); + } + if(Objects.isNull(resultMap) || Objects.isNull(resultMap.get("errcode")) || !"0".equals(String.valueOf(resultMap.get("errcode")))){ + throw new SysException("企业微信推送失败"); + } + this.save(msgHisEntity); + } + + @Override + public List getRepeatPushMsgList() { + return mapper.getRepeatPushMsgList(); + } + + /** + * 组装请求报文 + * @return + */ + private static String buildWeChatComReqStr(WeChatComParams weChatComParams, List mobileList) { + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(weChatComParams.getContent()); + //消息内容 + Map contentMap = Maps.newHashMap(); + contentMap.put("content", stringBuilder.toString()); + + if (Boolean.parseBoolean(weChatComParams.getIsAtAll())) { + mobileList.add("@all"); + } + contentMap.put("mentioned_mobile_list", mobileList); + + Map reqMap = Maps.newHashMap(); + reqMap.put("msgtype", weChatComParams.getMsgType()); + reqMap.put("text", contentMap); + + return JSON.toJSONString(reqMap); + } + + /** + * 组装请求报文 + * @return + */ + private static String buildReqStr(DingTalkParams dingTalkParams, List mobileList) { + + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(dingTalkParams.getKeyWord() + ":"); + stringBuilder.append("\n"); + stringBuilder.append(dingTalkParams.getContent()); + //消息内容 + Map contentMap = Maps.newHashMap(); + contentMap.put("content", stringBuilder.toString()); + + //通知人 + Map atMap = Maps.newHashMap(); + //1.是否通知所有人 + atMap.put("isAtAll", Boolean.parseBoolean(dingTalkParams.getIsAtAll())); + //2.通知具体人的手机号码列表 + atMap.put("atMobiles", mobileList); + + Map reqMap = Maps.newHashMap(); + reqMap.put("msgtype", dingTalkParams.getMsgType()); + reqMap.put("text", contentMap); + reqMap.put("at", atMap); + + return JSON.toJSONString(reqMap); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/param/controller/MsgSysParamsController.java b/modules/msg/src/main/java/com/thing/msg/param/controller/MsgSysParamsController.java new file mode 100644 index 0000000..2eb8cb5 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/controller/MsgSysParamsController.java @@ -0,0 +1,110 @@ + + +package com.thing.msg.param.controller; + + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.param.dto.MsgSysParamsDTO; +import com.thing.msg.param.service.MsgSysParamsService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.apache.commons.lang3.StringUtils; +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 Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@RestController +@RequestMapping("msgsys/params") +@Tag(name = "参数管理") +public class MsgSysParamsController { + @Autowired + private MsgSysParamsService msgSysParamsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "paramCode",description ="参数编码") + }) + public Result> page( @RequestParam Map params) { + PageData page = msgSysParamsService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + MsgSysParamsDTO data = msgSysParamsService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody MsgSysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + msgSysParamsService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody MsgSysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgSysParamsService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + msgSysParamsService.delete(ids); + return new Result(); + } + + @GetMapping("getSysParams") + @Operation(summary="查询字典") + public Result> getSysParams(@RequestParam(required = false) String paramCode) { + + Map params = new HashMap<>(); + if (StringUtils.isNotBlank(paramCode)) { + params.put("paramCode", paramCode); + } + + List list = msgSysParamsService.list(params); + + return new Result>().ok(list); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/param/dto/MsgSysParamsDTO.java b/modules/msg/src/main/java/com/thing/msg/param/dto/MsgSysParamsDTO.java new file mode 100644 index 0000000..ba56bcc --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/dto/MsgSysParamsDTO.java @@ -0,0 +1,61 @@ + + +package com.thing.msg.param.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "参数管理") +public class MsgSysParamsDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "参数编码") + @NotBlank(message="{sysparams.paramcode.require}", groups = DefaultGroup.class) + private String paramCode; + + @Schema(description = "参数值") + @NotBlank(message="{sysparams.paramvalue.require}", groups = DefaultGroup.class) + private String paramValue; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} diff --git a/modules/msg/src/main/java/com/thing/msg/param/entity/MsgParamsEntity.java b/modules/msg/src/main/java/com/thing/msg/param/entity/MsgParamsEntity.java new file mode 100644 index 0000000..63b4eb5 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/entity/MsgParamsEntity.java @@ -0,0 +1,45 @@ + + +package com.thing.msg.param.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_params") +public class MsgParamsEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 参数编码 + */ + private String paramCode; + /** + * 参数值 + */ + private String paramValue; + /** + * 类型 0:系统参数 1:非系统参数 + */ + private Integer paramType; + /** + * 备注 + */ + private String remark; + + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/param/mapper/MsgSysParamsMapper.java b/modules/msg/src/main/java/com/thing/msg/param/mapper/MsgSysParamsMapper.java new file mode 100644 index 0000000..5f270ed --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/mapper/MsgSysParamsMapper.java @@ -0,0 +1,39 @@ + +package com.thing.msg.param.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.msg.param.entity.MsgParamsEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface MsgSysParamsMapper extends PowerBaseMapper { + /** + * 根据参数编码,查询value + * @param paramCode 参数编码 + * @return 参数值 + */ + String getValueByCode(String paramCode); + + /** + * 获取参数编码列表 + * @param ids ids + * @return 返回参数编码列表 + */ + List getParamCodeList(Long[] ids); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue); +} diff --git a/modules/msg/src/main/java/com/thing/msg/param/redis/MsgSysParamsRedis.java b/modules/msg/src/main/java/com/thing/msg/param/redis/MsgSysParamsRedis.java new file mode 100644 index 0000000..286dd01 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/redis/MsgSysParamsRedis.java @@ -0,0 +1,54 @@ +package com.thing.msg.param.redis; + +import com.thing.common.cache.config.RedisConfig; +import com.thing.common.cache.redis.RedisKeys; +import com.thing.common.cache.redis.RedisUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.stereotype.Component; + +import java.util.Objects; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Component +@ConditionalOnBean(RedisConfig.class) +public class MsgSysParamsRedis { + @Autowired + private RedisUtils msgRedisUtils; + + public void delete(Object[] paramCodes) { + String key = RedisKeys.getSysParamsKey(); + msgRedisUtils.hDel(key, paramCodes); + } + + public void set(String paramCode, String paramValue) { + if (paramValue == null) { + return; + } + String key = RedisKeys.getSysParamsKey(); + msgRedisUtils.hSet(key, paramCode, paramValue); + } + + public String get(String paramCode) { + String key = RedisKeys.getSysParamsKey(); + return (String) msgRedisUtils.hGet(key, paramCode); + } + + public String getByKey(String paramCode) { + String key = RedisKeys.getSysParamsKey(paramCode); + return (String) msgRedisUtils.get(key); + } + + public void setByKey(String paramCode, String paramValue, Long expire) { + if (paramValue == null) { + return; + } + String key = RedisKeys.getSysParamsKey(paramCode); + msgRedisUtils.set(key, paramValue, Objects.isNull(expire) ? 5400 : expire); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/param/service/MsgSysParamsService.java b/modules/msg/src/main/java/com/thing/msg/param/service/MsgSysParamsService.java new file mode 100644 index 0000000..2472b71 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/service/MsgSysParamsService.java @@ -0,0 +1,61 @@ + + +package com.thing.msg.param.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.param.dto.MsgSysParamsDTO; +import com.thing.msg.param.entity.MsgParamsEntity; + +import java.util.List; +import java.util.Map; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface MsgSysParamsService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + MsgSysParamsDTO get(Long id); + + void save(MsgSysParamsDTO dto); + + void update(MsgSysParamsDTO dto); + + void delete(Long[] ids); + + /** + * 根据参数编码,获取参数的value值 + * + * @param paramCode 参数编码 + */ + String getValue(String paramCode); + + /** + * 根据参数编码,获取value的Object对象 + * @param paramCode 参数编码 + * @param clazz Object对象 + */ + T getValueObject(String paramCode, Class clazz); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(String paramCode, String paramValue); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + void updateByCode(String paramCode, String paramValue); +} diff --git a/modules/msg/src/main/java/com/thing/msg/param/service/impl/MsgSysParamsServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/param/service/impl/MsgSysParamsServiceImpl.java new file mode 100644 index 0000000..42b33d1 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/param/service/impl/MsgSysParamsServiceImpl.java @@ -0,0 +1,162 @@ + + +package com.thing.msg.param.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.param.dto.MsgSysParamsDTO; +import com.thing.msg.param.entity.MsgParamsEntity; +import com.thing.msg.param.mapper.MsgSysParamsMapper; +import com.thing.msg.param.redis.MsgSysParamsRedis; +import com.thing.msg.param.service.MsgSysParamsService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +public class MsgSysParamsServiceImpl extends BaseServiceImpl implements MsgSysParamsService { + @Value("${spring.cache.type}") + private String cacheType; + + private MsgSysParamsRedis getMsgSysParamsRedis(){ + return SpringContextUtils.getBean(MsgSysParamsRedis.class); + } + + private boolean inUsingRedis(){ + return "redis".equals(cacheType); + } + + @Override + public PageData page(Map params) { + PageData pageData = getPageData(params,MsgSysParamsDTO.class); + return pageData; + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + return ConvertUtils.sourceToTarget(entityList, MsgSysParamsDTO.class); + } + + public QueryWrapper getWrapper(Map params) { + String paramCode = (String) params.get("paramCode"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("param_type", 1); + wrapper.like("param_code", paramCode,StringUtils.isNotBlank(paramCode)); + + return wrapper; + } + + @Override + public MsgSysParamsDTO get(Long id) { + MsgParamsEntity entity = mapper.selectOneById(id); + return ConvertUtils.sourceToTarget(entity, MsgSysParamsDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(MsgSysParamsDTO dto) { + MsgParamsEntity entity = ConvertUtils.sourceToTarget(dto, MsgParamsEntity.class); + mapper.insert(entity); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(entity.getParamCode(), entity.getParamValue()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(MsgSysParamsDTO dto) { + MsgParamsEntity entity = ConvertUtils.sourceToTarget(dto, MsgParamsEntity.class); + updateById(entity); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(entity.getParamCode(), entity.getParamValue()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除Redis数据 + List paramCodeList = mapper.getParamCodeList(ids); + String[] paramCodes = paramCodeList.toArray(new String[paramCodeList.size()]); + if (inUsingRedis()) { + getMsgSysParamsRedis().delete(paramCodes); + } + //删除 + mapper.deleteBatchByIds(Arrays.asList(ids)); + } + + @Override + public String getValue(String paramCode) { + String paramValue = getMsgSysParamsRedis().get(paramCode); + if (paramValue == null) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("param_code",paramCode); + queryWrapper.where("limit 1"); + MsgParamsEntity msgParamsEntity = mapper.selectOneById(queryWrapper); + if(!Objects.isNull(msgParamsEntity) && StringUtils.isNotBlank(msgParamsEntity.getParamValue())){ + if (inUsingRedis()) { + getMsgSysParamsRedis().set(paramCode, msgParamsEntity.getParamValue()); + } + paramValue = msgParamsEntity.getParamValue(); + } + } + return paramValue; + } + + @Override + public T getValueObject(String paramCode, Class clazz) { + String paramValue = getValue(paramCode); + if (StringUtils.isNotBlank(paramValue)) { + return JSON.parseObject(paramValue, clazz); + } + + try { + return clazz.newInstance(); + } catch (Exception e) { + throw new SysException(ErrorCode.PARAMS_GET_ERROR); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int updateValueByCode(String paramCode, String paramValue) { + int count = mapper.updateValueByCode(paramCode, paramValue); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(paramCode, paramValue); + } + return count; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateByCode(String paramCode, String paramValue) { + QueryWrapper update = new QueryWrapper(); + update.eq("param_code",paramCode); + MsgParamsEntity sysParamsEntity = mapper.selectOneByQuery(update); + sysParamsEntity.setParamValue(paramValue); + mapper.update(sysParamsEntity); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(paramCode, paramValue); + } + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/controller/MsgPushController.java b/modules/msg/src/main/java/com/thing/msg/push/controller/MsgPushController.java new file mode 100644 index 0000000..1e27f32 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/controller/MsgPushController.java @@ -0,0 +1,180 @@ +package com.thing.msg.push.controller; + +import cn.hutool.core.collection.CollUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Maps; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.param.service.MsgSysParamsService; +import com.thing.msg.push.dto.CustomPushDTO; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.service.MsgTemplateService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + + +/** + * 消息推送 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-23 + */ +@RestController +@RequestMapping("msgpush/msgpush") +@Tag(name = "消息推送") +public class MsgPushController { + @Autowired + private MsgPushService msgPushService; + + @Autowired + private MsgSysParamsService msgSysParamsService; + + @Autowired + private MsgTemplateService msgTemplateService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "configName",description ="配置名称"), + @Parameter(name = "configType",description ="配置类型"), + @Parameter(name = "executeByJob",description ="是否由定时任务执行 0:否 1:是") + }) +// @RequiresPermissions("msgpush:msgpush:page") + public Result> page( @RequestParam Map params) { + PageData page = msgPushService.getPageData(params,MsgPushDTO.class); + if (CollUtil.isNotEmpty(page.getList())) { + page.getList().forEach(item -> { + MsgTemplateDTO msgTemplateDTO = msgTemplateService.getByIdAs(item.getTemplateId(),MsgTemplateDTO.class); + item.setTemplateName(Objects.isNull(msgTemplateDTO) ? "" : msgTemplateDTO.getName()); + JSONObject jsonObject = JSONObject.parseObject(item.getConfigParams()); + item.setContent(String.valueOf(jsonObject.get("content"))); + }); + } + return new Result>().ok(page); + } + + @Operation(summary="所有推送列表") + @GetMapping("list") + public Result> list(@RequestParam(required = false) String configType, @RequestParam(required = false) String configName){ + Map map = Maps.newHashMap(); + if (StringUtils.isNotBlank(configType)) map.put("configType", configType); + if (StringUtils.isNotBlank(configName)) map.put("configName", configName); + List list = msgPushService.listAs(map,MsgPushDTO.class); + list.forEach(item -> { + MsgTemplateDTO msgTemplateDTO = msgTemplateService.getByIdAs(item.getTemplateId(),MsgTemplateDTO.class); + item.setTemplateName(Objects.isNull(msgTemplateDTO) ? "" : msgTemplateDTO.getName()); + }); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("msgpush:msgpush:info") + public Result get(@PathVariable("id") Long id) { + MsgPushDTO data = msgPushService.getByIdAs(id,MsgPushDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") +// @RequiresPermissions("msgpush:msgpush:save") + public Result save(@RequestBody MsgPushDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + msgPushService.saveInfo(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") +// @RequiresPermissions("msgpush:msgpush:update") + public Result update(@RequestBody MsgPushDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgPushService.updateInfo(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") +// @RequiresPermissions("msgpush:msgpush:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + msgPushService.removeByIds(Arrays.asList(ids)); + return new Result(); + } + + @PostMapping("sendMsg") + @Operation(summary="根据配置项id推送") +// @RequiresPermissions("msgpush:msgpush:export") + public Result sendMsg(@RequestBody Long[] ids) { + msgPushService.sendMsg(ids); + return new Result(); + } + + @PostMapping("sendCustomContentMsg") + @Operation(summary="自定义消息内容推送") +// @RequiresPermissions("msgpush:msgpush:export") + public Result sendCustomContentMsg(@RequestBody List dtoList) { + msgPushService.sendCustomContentMsg(dtoList); + return new Result(); + } + + @PostMapping("sendCustomContentMsgTest") + @Operation(summary="推送测试") +// @RequiresPermissions("msgpush:msgpush:export") + public Result sendCustomContentMsgTest(@RequestBody List msgPushDTOList) { + msgPushService.sendCustomContentMsg(msgPushDTOList, null, null, null, false); + return new Result(); + } + + @PostMapping("sendMsgByUserId") + @Operation(summary="根据配置项id及用户id来推送") + public Result sendMsgByUserId(@RequestBody Long[] ids, @RequestParam List userId) { + msgPushService.sendMsgByUserId(ids, userId); + return new Result(); + } + + @PostMapping("sendCustomContentMsgByUserId") + @Operation(summary="根据用户id自定义消息内容推送") + public Result sendCustomContentMsgByUserId(@RequestBody List msgPushDTOList, @RequestParam List userId) { + msgPushService.sendCustomContentMsgByUserId(msgPushDTOList, userId); + return new Result(); + } + + @GetMapping("/config") + @Operation(summary="获取配置信息") +// @RequiresPermissions("sys:mail:all") + public Result>> config(@RequestParam String configKey) { + List> config = msgSysParamsService.getValueObject(configKey, List.class); + return new Result>>().ok(config); + } + + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/CustomPushDTO.java b/modules/msg/src/main/java/com/thing/msg/push/dto/CustomPushDTO.java new file mode 100644 index 0000000..139e370 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/CustomPushDTO.java @@ -0,0 +1,18 @@ +package com.thing.msg.push.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +@Data +@Schema( name= "自定义推送参数") +public class CustomPushDTO { + @Schema(description = "id") + @NotBlank(message = "id不能为空") + private String id; + @Schema(description = "msg参数") + @NotBlank(message = "msg参数不能为空") + private String msg; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/DingTalkParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/DingTalkParams.java new file mode 100644 index 0000000..8cdb06f --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/DingTalkParams.java @@ -0,0 +1,42 @@ +package com.thing.msg.push.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/29 0029 15:12:06 + */ +@Data +@Valid +public class DingTalkParams { + private Long configId; + @Schema(description = "机器人tokenUrl") + @NotBlank(message = "configParams中缺少必填项webhookUrl(机器人tokenUrl),多个英文分号隔开") + private String webhookUrl; + @Schema(description = "消息关键字") + //@NotBlank(message = "configParams中缺少必填项keyWord(消息推送关键字,包含关键字的才能发,与群机器人创建时的关键字一致)") + private String keyWord; + @Schema(description = "接收人手机号") + //@NotBlank(message = "configParams中缺少必填项atMobilesStr(接收人手机号,多个英文分号隔开)") + private String atMobilesStr; + @Schema(description = "消息内容") + @NotBlank(message = "configParams中缺少必填项text(消息内容)") + private String content; + @Schema(description = "消息类型") + @NotBlank(message = "configParams中缺少必填项msgType(消息类型 text:文本(默认仅支持text))") + private String msgType; + @Schema(description = "是否通知全部") + @NotNull(message = "configParams中缺少必填项isAtAll(是否通知全部 false:否 true:是)") + private String isAtAll; + @Schema(description = "签名") + private String sign; + private List atMobiles; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/MailAllParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/MailAllParams.java new file mode 100644 index 0000000..81d3b03 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/MailAllParams.java @@ -0,0 +1,17 @@ +package com.thing.msg.push.dto; + +import lombok.Data; + +/** + * @author zy + * @version 1.0 + * @date 2022/1/10 0010 16:57:16 + */ +@Data +public class MailAllParams extends MailParams { + private Long configId; + private String[] toArr; + private String[] ccArr; + private String subject; + private String content; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/MailParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/MailParams.java new file mode 100644 index 0000000..12fce70 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/MailParams.java @@ -0,0 +1,24 @@ +package com.thing.msg.push.dto; + +import com.thing.msg.push.email.EmailConfig; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +/** + * @author zy + * @version 1.0 + * @date 2021/12/27 0027 16:24:54 + */ +@Data +@Valid +public class MailParams extends EmailConfig { + @Schema(description = "接收人邮箱") + @NotBlank(message = "configParams中缺少必填项to(接收人邮箱),多个英文分号隔开") + private String to; + @Schema(description = "抄送人邮箱") + private String cc; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/MsgPushDTO.java b/modules/msg/src/main/java/com/thing/msg/push/dto/MsgPushDTO.java new file mode 100644 index 0000000..929664e --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/MsgPushDTO.java @@ -0,0 +1,68 @@ +package com.thing.msg.push.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 消息推送 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-23 + */ +@Data +@Schema( name= "消息推送") +public class MsgPushDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + @Schema(description = "id(修改时不能为空)") + @NotNull(message = "id不能为空", groups = UpdateGroup.class) + private Long id; + @Schema(description = "配置项名称") + @NotBlank(message = "配置项名称不能为空", groups = DefaultGroup.class) + private String configName; + @Schema(description = "配置项类型 0:短信 1:邮件 2:微信公众号 3:钉钉 4:企业微信") + @NotBlank(message = "配置项类型不能为空", groups = DefaultGroup.class) + private String configType; + @Schema(description = "测试消息内容") + private String content; + @Schema(description = "模板内容(消息内容如果以json格式则自动匹配模板中的参数进行替换,短信和微信公众号必须使用申请的模板,邮箱、钉钉可以自定义模板)") + private String templateMsg; + @Schema(description = "模板id(仅针对邮箱、钉钉的模板,短信和微信公众号无效)") + private Long templateId; + @Schema(description = "模板名称(用于列表显示)") + private String templateName; + @Schema(description = "配置项参数") + @NotBlank(message = "配置项类型不能为空", groups = DefaultGroup.class) + private String configParams; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "是否由定时任务执行 0:否 1:是") + @NotBlank(message = "", groups = DefaultGroup.class) + @Range(min=0, max=1, message = "是否定时执行请选择是或否", groups = DefaultGroup.class) + private String executeByJob; + @Schema(description = "企业ID") + private Long companyId; + @Schema(description = "是否手动推送") + private Boolean manual; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/SmsParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/SmsParams.java new file mode 100644 index 0000000..6da8b3e --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/SmsParams.java @@ -0,0 +1,31 @@ +package com.thing.msg.push.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.msg.push.sms.SmsConfig; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + +/** + * @author zy + * @version 1.0 + * @date 2021/12/27 0027 17:06:51 + */ +@Data +@Valid +public class SmsParams extends SmsConfig { + + private Long configId; + + @Schema(description = "手机号") + @NotBlank(message = "configParams中缺少必填项mobile", groups = DefaultGroup.class) + private String mobile; + + @Schema(description = "平台类型 1:阿里云 2:腾讯云 3:七牛") + @NotNull(message = "configParams中缺少必填项platform:1:阿里云 2:腾讯云 3:七牛", groups = DefaultGroup.class) + private Integer platform; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatComParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatComParams.java new file mode 100644 index 0000000..c3e6097 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatComParams.java @@ -0,0 +1,34 @@ +package com.thing.msg.push.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +/** + * @author zzx + * @date 2022/6/20 + */ +@Data +@Valid +public class WeChatComParams { + + private Long configId; + + @Schema(description = "机器人tokenUrl") + @NotBlank(message = "configParams中缺少必填项webhookUrl(机器人tokenUrl),多个英文分号隔开") + private String webhookUrl; + @Schema(description = "接收人手机号") + //@NotBlank(message = "configParams中缺少必填项atMobilesStr(接收人手机号,多个英文分号隔开)") + private String atMobilesStr; + @Schema(description = "消息类型") + @NotBlank(message = "configParams中缺少必填项msgType(消息类型 text:文本(默认仅支持text))") + private String msgType; + @Schema(description = "消息内容") + @NotBlank(message = "configParams中缺少必填项text(消息内容)") + private String content; + @Schema(description = "是否通知全部") + //@NotNull(message = "configParams中缺少必填项isAtAll(是否通知全部 false:否 true:是)") + private String isAtAll; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatParams.java b/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatParams.java new file mode 100644 index 0000000..cf8b9b8 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/WeChatParams.java @@ -0,0 +1,37 @@ +package com.thing.msg.push.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +/** + * @author zy + * @version 1.0 + * @date 2021/12/27 0027 16:21:09 + */ +@Data +@Valid +public class WeChatParams { + private Long configId; + @Schema(description = "公众号appId") + @NotBlank(message = "configParams中缺少必填项appId", groups = DefaultGroup.class) + private String appId; + @Schema(description = "公众号appSecret") + @NotBlank(message = "configParams中缺少必填项appSecret", groups = DefaultGroup.class) + private String appSecret; + @Schema(description = "公众号推送类型 0:分组推送 1:指定人推送") + @NotBlank(message = "configParams中缺少必填项type,0:分组推送 1:指定人推送", groups = DefaultGroup.class) + private String type; + @Schema(description = "公众号消息模板id") + @NotBlank(message = "configParams中缺少必填项templateId(微信公众号消息模板id)", groups = DefaultGroup.class) + private String templateId; + @Schema(description = "公众号关注人分组标签") + //@NotBlank(message = "configParams中缺少必填项groupName,type:0为微信分组推送方式时,参数groupName不能为空(且需要与微信公众号的分组标签名称一致)", groups = GroupNameGroup.class) + private String groupName; + @Schema(description = "微信唯一标识") + //@NotBlank(message = "configParams中缺少必填项wxOpenId,type:1为微信号推送方式时,参数wxOpenId不能为空(配置项中wxOpenId填存在微信号的唯一id)", groups = WxOpenIdGroup.class) + private String wxOpenId; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/dto/WxParamObj.java b/modules/msg/src/main/java/com/thing/msg/push/dto/WxParamObj.java new file mode 100644 index 0000000..8291a21 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/dto/WxParamObj.java @@ -0,0 +1,20 @@ +package com.thing.msg.push.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/29 0029 9:18:11 + */ +@Data +public class WxParamObj implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + private String value; + private String color; +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/email/EmailConfig.java b/modules/msg/src/main/java/com/thing/msg/push/email/EmailConfig.java new file mode 100644 index 0000000..386ad7e --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/email/EmailConfig.java @@ -0,0 +1,45 @@ + + +package com.thing.msg.push.email; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.Getter; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 邮件配置信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "邮件配置信息") +@Valid +public class EmailConfig implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "SMTP") + @NotBlank(message="configParams中缺少必填项smtp(邮箱服务地址)") + private String smtp; + + @Schema(description = "端口号") + @NotNull(message="configParams中缺少必填项port(邮箱服务端口)") + private Integer port; + + @Schema(description = "邮箱账号") + @NotBlank(message="configParams中缺少必填项username(邮箱账号)") + private String username; + + @Schema(description = "邮箱密码") + @NotBlank(message="configParams中缺少必填项password(邮箱授权码)") + private String password; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/email/MsgEmailUtils.java b/modules/msg/src/main/java/com/thing/msg/push/email/MsgEmailUtils.java new file mode 100644 index 0000000..72fd832 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/email/MsgEmailUtils.java @@ -0,0 +1,115 @@ + + +package com.thing.msg.push.email; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.enumeration.PushReceiveStatus; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.exception.SysException; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.MailAllParams; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import jakarta.mail.internet.MimeMessage; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.stream.Collectors; + +/** + * 邮件工具类 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +public class MsgEmailUtils { + @Autowired + private MsgHisService msgHisService; + + private JavaMailSenderImpl createMailSender(EmailConfig config) { + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + sender.setHost(config.getSmtp()); + sender.setPort(config.getPort()); + sender.setUsername(config.getUsername()); + sender.setPassword(config.getPassword()); + sender.setDefaultEncoding("Utf-8"); + Properties p = new Properties(); + p.setProperty("mail.smtp.timeout", "10000"); + p.setProperty("mail.smtp.auth", "false"); + p.setProperty("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); + sender.setJavaMailProperties(p); + return sender; + } + /** + * 获取Freemarker渲染后的内容 + * + * @param content 模板内容 + * @param params 参数 + */ + private String getFreemarkerContent(String content, Map params) { + if (MapUtil.isEmpty(params)) { + return content; + } + //渲染模板 + for(Map.Entry entry : params.entrySet()){ + content = content.replace("${"+entry.getKey()+"}", (CharSequence) entry.getValue()); + } + return content; + } + /** + * 发送邮件 + * + * @param mailAllParams 邮箱参数 + * @return true:成功 false:失败 + */ + @Transactional(rollbackFor = Exception.class) + public boolean sendMail(MailAllParams mailAllParams, Map params, String publisher, List receiverList, Long pushSettingId, Boolean manual) throws Exception { + + JavaMailSenderImpl mailSender = createMailSender(mailAllParams); + MimeMessage mimeMessage = mailSender.createMimeMessage(); + //设置utf-8编码 + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); + messageHelper.setFrom(mailAllParams.getUsername()); + + //收件人 + messageHelper.setTo(mailAllParams.getToArr()); + //抄送 + if (mailAllParams.getCcArr() != null && mailAllParams.getCcArr().length > 0) { + messageHelper.setCc(mailAllParams.getCcArr()); + } + //主题 + messageHelper.setSubject(mailAllParams.getSubject()); + //邮件正文 + String content = getFreemarkerContent(mailAllParams.getContent(), params); + messageHelper.setText(content, true); + + //发送邮件 + try { + mailSender.send(mimeMessage); + } catch (Exception e) { + throw new SysException("发送失败:" + e.getMessage()); + } + + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : mailAllParams.getUsername()); + Map emailMap = CollectionUtil.isNotEmpty(receiverList) ? receiverList.stream().collect(Collectors.toMap(SysUserInfoDTO::getEmailUsername, SysUserInfoDTO::getRealName)) : null; + msgHisEntity.setReceiver(CollectionUtil.isNotEmpty(emailMap) ? emailMap.get(mailAllParams.getTo()) : mailAllParams.getTo()); + msgHisEntity.setPushConfigId(String.valueOf(mailAllParams.getConfigId())); + msgHisEntity.setHisMsg(content); + msgHisEntity.setStatus(PushStatus.SUCCESS.getStatus()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisEntity.setPushReceiveStatus(ObjectUtil.isNotNull(manual) && manual ? PushReceiveStatus.RECEIVE.getStatus() : PushReceiveStatus.NO_RECEIVE.getStatus()); + msgHisService.save(msgHisEntity); + return true; + } + +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/entity/MsgPushEntity.java b/modules/msg/src/main/java/com/thing/msg/push/entity/MsgPushEntity.java new file mode 100644 index 0000000..76b3ad7 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/entity/MsgPushEntity.java @@ -0,0 +1,33 @@ +package com.thing.msg.push.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 消息推送 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-23 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_push") +public class MsgPushEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + private String configName; + private String configType; + private String templateMsg; + private Long templateId; + private String configParams; + private String executeByJob; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/MsgPush.java b/modules/msg/src/main/java/com/thing/msg/push/factory/MsgPush.java new file mode 100644 index 0000000..0f58e69 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/MsgPush.java @@ -0,0 +1,15 @@ +package com.thing.msg.push.factory; + +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/23 0023 17:39:51 + */ +public interface MsgPush { + void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat); +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/PushMsgFactory.java b/modules/msg/src/main/java/com/thing/msg/push/factory/PushMsgFactory.java new file mode 100644 index 0000000..792998b --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/PushMsgFactory.java @@ -0,0 +1,38 @@ +package com.thing.msg.push.factory; + + +import com.thing.common.core.enumeration.MsgType; +import com.thing.common.core.exception.SysException; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.factory.impl.*; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; + +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/23 0023 17:38:26 + */ +public class PushMsgFactory { + + private static MsgPush msgPush; + + public static void buildSend(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + if (MsgType.Sms.getValue().equals(dto.getConfigType())) { + msgPush = new SmsInfoService(); + } else if (MsgType.Mail.getValue().equals(dto.getConfigType())) { + msgPush = new MailInfoService(); + } else if (MsgType.WeChat.getValue().equals(dto.getConfigType())) { + msgPush = new WeChatInfoService(); + } else if (MsgType.DingTalk.getValue().equals(dto.getConfigType())) { + msgPush = new DingTalkInfoService(); + } else if (MsgType.WeChatCom.getValue().equals(dto.getConfigType())) { + msgPush = new WeChatComInfoService(); + } else { + throw new SysException("暂不支持其它类型的消息推送"); + } + + msgPush.send(dto, publisher, receiverList, pushSettingId, repeat); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/impl/DingTalkInfoService.java b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/DingTalkInfoService.java new file mode 100644 index 0000000..86a190a --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/DingTalkInfoService.java @@ -0,0 +1,125 @@ +package com.thing.msg.push.factory.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.PushReceiveStatus; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.DingTalkParams; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.factory.MsgPush; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.service.MsgTemplateService; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/24 0024 9:40:24 + */ +@Slf4j +public class DingTalkInfoService implements MsgPush { + + + private static MsgPushService msgPushService; + + private static MsgHisService msgHisService; + + private static MsgTemplateService msgTemplateService; + + public static Pattern patternDingTalk = Pattern.compile("\\$\\{([a-z]+)\\}"); + + static { + msgPushService = SpringContextUtils.getBean(MsgPushService.class); + msgHisService = SpringContextUtils.getBean(MsgHisService.class); + msgTemplateService = SpringContextUtils.getBean(MsgTemplateService.class); + } + + @Override + public void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + + DingTalkParams dingTalkParams = msgPushService.checkDingTalkParams(dto.getConfigParams(),false); + dingTalkParams.setConfigId(dto.getId()); + String completeContent; + if (repeat) { + completeContent = dto.getContent(); + } else { + completeContent = getCompleteContent(getTemplateContent(dingTalkParams.getContent(), dto.getTemplateId()), dto.getTemplateMsg()); + } + dingTalkParams.setContent(completeContent); + + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : Constant.PUBLISHER_TYPE_DINGTALK); + Map dingdingPhoneMap = CollectionUtil.isNotEmpty(receiverList) ? receiverList.stream().collect(Collectors.toMap(SysUserInfoDTO::getDingdingPhone, SysUserInfoDTO::getRealName)) : null; + msgHisEntity.setReceiver(CollectionUtil.isNotEmpty(dingdingPhoneMap) ? dingdingPhoneMap.get(dingTalkParams.getAtMobilesStr()) : dingTalkParams.getAtMobilesStr()); + msgHisEntity.setPushConfigId(String.valueOf(dto.getId())); + msgHisEntity.setHisMsg(completeContent); + msgHisEntity.setStatus(PushStatus.SUCCESS.getStatus()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisEntity.setPushReceiveStatus(ObjectUtil.isNotNull(dto.getManual()) && dto.getManual() ? PushReceiveStatus.RECEIVE.getStatus() : PushReceiveStatus.NO_RECEIVE.getStatus()); + + // 保证事务生效 将消息记录和发送操作放在msgHisService 如果放在当前类中会导致事务失效 + msgHisService.sendDingTalkMsg(msgHisEntity, dingTalkParams); + } + + public List paramGroups(String content) { + Matcher matcher = patternDingTalk.matcher(content); + List paramList = new ArrayList<>(); + while (matcher.find()) { + paramList.add(matcher.group(0)); + } + + return paramList; + } + + // 有模板优先使用模板 + public String getTemplateContent(String text, Long templateId) { + if (!Objects.isNull(templateId)) { + MsgTemplateDTO msgTemplateDTO = msgTemplateService.getByIdAs(templateId,MsgTemplateDTO.class); + if (!Objects.isNull(msgTemplateDTO)) { + text = StringUtils.isNotBlank(msgTemplateDTO.getContent()) ? msgTemplateDTO.getContent() : text; + } + } + return text; + } + + // 匹配参数并替换 + public String getCompleteContent(String text, String msg) { + // 待替换的参数 + Map params = new HashMap<>(); + try { + params = JSONObject.parseObject(msg, HashMap.class); + } catch (Exception e) { + log.error(e.getMessage()); + } + String completeContent = text; + List paramList = paramGroups(completeContent); + if (Objects.isNull(params) || CollUtil.isEmpty(paramList)) { + return text; + } + + for (String key : paramList) { + String paramKey = key.substring(key.indexOf("${") + 2, key.indexOf("}")); + if (!params.containsKey(paramKey)) { + continue; + } + completeContent = completeContent.replace(key, Objects.isNull(params.get(paramKey)) ? "" : String.valueOf(params.get(paramKey))); + break; + } + + return completeContent; + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/impl/MailInfoService.java b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/MailInfoService.java new file mode 100644 index 0000000..d06dd80 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/MailInfoService.java @@ -0,0 +1,73 @@ +package com.thing.msg.push.factory.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.msg.push.dto.MailAllParams; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.email.MsgEmailUtils; +import com.thing.msg.push.factory.MsgPush; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.service.MsgTemplateService; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/23 0023 17:43:53 + */ +@Slf4j +public class MailInfoService implements MsgPush { + + private static MsgEmailUtils msgEmailUtils; + + private static MsgPushService msgPushService; + private static MsgTemplateService msgTemplateService; + + static { + msgEmailUtils = SpringContextUtils.getBean(MsgEmailUtils.class); + msgPushService = SpringContextUtils.getBean(MsgPushService.class); + msgTemplateService = SpringContextUtils.getBean(MsgTemplateService.class); + } + + @Override + public void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + MailAllParams mailAllParams = msgPushService.checkMailParams(dto.getConfigParams(),false); + mailAllParams.setConfigId(dto.getId()); + + // 待替换的参数 + Map params = new HashMap<>(); + try { + params = JSONObject.parseObject(dto.getTemplateMsg(), HashMap.class); + } catch (Exception e) { + log.error(e.getMessage()); + } + + if (repeat) { + mailAllParams.setContent(dto.getContent()); + } else { + if (ObjectUtil.isNotNull(dto.getTemplateId())) { + MsgTemplateDTO msgTemplateDTO = msgTemplateService.getByIdAs(dto.getTemplateId(),MsgTemplateDTO.class); + if (ObjectUtil.isNotNull(msgTemplateDTO)) { + mailAllParams.setSubject(StringUtils.isNotBlank(msgTemplateDTO.getSubject()) ? msgTemplateDTO.getSubject() : mailAllParams.getSubject()); + mailAllParams.setContent(StringUtils.isNotBlank(msgTemplateDTO.getContent()) ? msgTemplateDTO.getContent() : mailAllParams.getContent()); + } + } + } + + try { + // 发送 + msgEmailUtils.sendMail(mailAllParams, params, publisher, receiverList, pushSettingId, dto.getManual()); + } catch (Exception e) { + throw new SysException("邮件消息发送异常:" + e.getMessage()); + } + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/impl/SmsInfoService.java b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/SmsInfoService.java new file mode 100644 index 0000000..79866b1 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/SmsInfoService.java @@ -0,0 +1,52 @@ +package com.thing.msg.push.factory.impl; + +import com.alibaba.fastjson.JSON; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.dto.SmsParams; +import com.thing.msg.push.factory.MsgPush; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.push.sms.AbstractSmsService; +import com.thing.msg.push.sms.SmsFactory; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import lombok.extern.slf4j.Slf4j; + +import java.util.LinkedHashMap; +import java.util.List; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/23 0023 17:41:55 + */ +@Slf4j +public class SmsInfoService implements MsgPush { + + private static MsgPushService msgPushService; + + static { + + msgPushService = SpringContextUtils.getBean(MsgPushService.class); + } + + @Override + public void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + SmsParams smsParams = msgPushService.checkSmsParams(dto.getConfigParams(),false); + smsParams.setConfigId(dto.getId()); + //短信服务 + AbstractSmsService service = SmsFactory.build(smsParams); + if (service == null) { + throw new SysException("短信服务初始化失败"); + } + // 模板参数(替换短信模板中的变量) + LinkedHashMap templateParamMap = new LinkedHashMap<>(); + try { + templateParamMap = JSON.parseObject(dto.getTemplateMsg(), LinkedHashMap.class); + } catch (Exception e) { + log.error(e.getMessage()); + } + + service.sendSmsNew(smsParams, templateParamMap); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatComInfoService.java b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatComInfoService.java new file mode 100644 index 0000000..52db78a --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatComInfoService.java @@ -0,0 +1,125 @@ +package com.thing.msg.push.factory.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.PushReceiveStatus; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.dto.WeChatComParams; +import com.thing.msg.push.factory.MsgPush; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.service.MsgTemplateService; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/24 0024 9:40:24 + */ +@Slf4j +public class WeChatComInfoService implements MsgPush { + + + private static MsgPushService msgPushService; + + private static MsgHisService msgHisService; + + private static MsgTemplateService msgTemplateService; + + public static Pattern patternWeChatCom = Pattern.compile("\\$\\{([a-z]+)\\}"); + + static { + msgPushService = SpringContextUtils.getBean(MsgPushService.class); + msgHisService = SpringContextUtils.getBean(MsgHisService.class); + msgTemplateService = SpringContextUtils.getBean(MsgTemplateService.class); + } + + @Override + public void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + + WeChatComParams weChatComParams = msgPushService.checkWeChatComParams(dto.getConfigParams(),false); + weChatComParams.setConfigId(dto.getId()); + String completeContent; + if (repeat) { + completeContent = dto.getContent(); + } else { + completeContent = ObjectUtil.isNull(dto.getTemplateId()) ? weChatComParams.getContent() : getCompleteContent(getTemplateContent(weChatComParams.getContent(), dto.getTemplateId()), dto.getTemplateMsg()); + } + weChatComParams.setContent(completeContent); + + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : Constant.PUBLISHER_TYPE_WECHATCOM); + Map wxPhoneMap = CollectionUtil.isNotEmpty(receiverList) ? receiverList.stream().collect(Collectors.toMap(SysUserInfoDTO::getWxPhone, SysUserInfoDTO::getRealName)) : null; + msgHisEntity.setReceiver(CollectionUtil.isNotEmpty(wxPhoneMap) ? wxPhoneMap.get(weChatComParams.getAtMobilesStr()) : weChatComParams.getAtMobilesStr()); + msgHisEntity.setPushConfigId(String.valueOf(dto.getId())); + msgHisEntity.setHisMsg(completeContent); + msgHisEntity.setStatus(PushStatus.SUCCESS.getStatus()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisEntity.setPushReceiveStatus(ObjectUtil.isNotNull(dto.getManual()) && dto.getManual() ? PushReceiveStatus.RECEIVE.getStatus() : PushReceiveStatus.NO_RECEIVE.getStatus()); + + // 保证事务生效 将消息记录和发送操作放在msgHisService 如果放在当前类中会导致事务失效 + msgHisService.sendWeChatComMsg(msgHisEntity, weChatComParams); + } + + public List paramGroups(String content) { + Matcher matcher = patternWeChatCom.matcher(content); + List paramList = new ArrayList<>(); + while (matcher.find()) { + paramList.add(matcher.group(0)); + } + + return paramList; + } + + // 有模板优先使用模板 + private String getTemplateContent(String text, Long templateId) { + if (!Objects.isNull(templateId)) { + MsgTemplateDTO msgTemplateDTO = msgTemplateService.getByIdAs(templateId,MsgTemplateDTO.class); + if (!Objects.isNull(msgTemplateDTO)) { + text = StringUtils.isNotBlank(msgTemplateDTO.getContent()) ? msgTemplateDTO.getContent() : text; + } + } + return text; + } + + // 匹配参数并替换 + private String getCompleteContent(String text, String msg) { + // 待替换的参数 + Map params = new HashMap<>(); + try { + params = JSONObject.parseObject(msg, HashMap.class); + } catch (Exception e) { + log.error(e.getMessage()); + } + String completeContent = text; + List paramList = paramGroups(completeContent); + if (Objects.isNull(params) || CollUtil.isEmpty(paramList)) { + return text; + } + + for (String key : paramList) { + String paramKey = key.substring(key.indexOf("${") + 2, key.indexOf("}")); + if (!params.containsKey(paramKey)) { + continue; + } + completeContent = completeContent.replace(key, Objects.isNull(params.get(paramKey)) ? "" : String.valueOf(params.get(paramKey))); + break; + } + + return completeContent; + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatInfoService.java b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatInfoService.java new file mode 100644 index 0000000..d919604 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/factory/impl/WeChatInfoService.java @@ -0,0 +1,303 @@ +package com.thing.msg.push.factory.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Maps; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.PushReceiveStatus; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.msg.cache.service.MsgCacheService; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.MsgPushDTO; +import com.thing.msg.push.dto.WeChatParams; +import com.thing.msg.push.dto.WxParamObj; +import com.thing.msg.push.factory.MsgPush; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; + +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author zy + * @version 1.0 + * @date 2021/12/24 0024 9:39:31 + */ +@Slf4j +public class WeChatInfoService implements MsgPush { + + @Qualifier(value = "restTemplate") + private static RestTemplate restTemplate; + + private static MsgPushService msgPushService; + + private static MsgHisService msgHisService; + + private static MsgCacheService msgCacheService; + + public static Pattern patternWeChat = Pattern.compile("(\\{\\{\\w+\\.\\w+}})+([^\\{]*)+"); + + static { + msgPushService = SpringContextUtils.getBean(MsgPushService.class); + msgHisService = SpringContextUtils.getBean(MsgHisService.class); + msgCacheService = SpringContextUtils.getBean(MsgCacheService.class); + } + + private static String ACCESS_TOKEN = "?access_token="; + + @Override + public void send(MsgPushDTO dto, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + WeChatParams weChatParams = msgPushService.checkWeChatParams(dto.getConfigParams(), false); + weChatParams.setConfigId(dto.getId()); + + // 待替换的参数 + Map params = new HashMap<>(); + try { + params = JSONObject.parseObject(dto.getTemplateMsg(), HashMap.class); + if (repeat) { + WxParamObj wxParamObj = new WxParamObj(); + wxParamObj.setValue(dto.getContent()); + params = Maps.newLinkedHashMapWithExpectedSize(1); + params.put("keyword4", wxParamObj); + } + } catch (Exception e) { + log.error("微信公众号模板参数json转化失败"); + } + //如果不符合格式则转换成微信公众号能接收到的格式 + //todo 公众号暂时没有模板,等申请的模板下来后修改回去 + if (ObjectUtil.isNotNull(params) && !(params.get("keyword4") instanceof Map) && !repeat) { + WxParamObj wxParamObj = new WxParamObj(); + wxParamObj.setValue(String.valueOf(params.get("keyword4"))); + wxParamObj.setColor("red"); + params.put("keyword4", wxParamObj); + } else if (ObjectUtil.isNotNull(params) && !repeat){ + params.put("keyword4", BeanUtil.mapToBean((Map) params.get("keyword4"), WxParamObj.class, false, new CopyOptions())); + } + + String weChatToken = getWeChatToken(weChatParams.getAppId()); + List userList = null; + if ("0".equals(weChatParams.getType())) { + userList = getGroupList(weChatToken, weChatParams.getGroupName()); + } else if ("1".equals(weChatParams.getType())) { + userList = Arrays.asList(weChatParams.getWxOpenId().split(",")); + } + + if (CollUtil.isEmpty(userList)) { + return; + } + + if (CollectionUtil.isNotEmpty(receiverList)) { + for (SysUserInfoDTO sysUserInfoDTO : receiverList) { + sendMsg(weChatToken, sysUserInfoDTO, weChatParams.getTemplateId(), weChatParams.getConfigId(), params, publisher, pushSettingId, dto.getManual()); + } + } else { + for (String userId : userList) { + sendMsg(weChatToken, userId, weChatParams.getTemplateId(), weChatParams.getConfigId(), params, publisher, pushSettingId, dto.getManual()); + } + } + } + + public void sendMsg(String weChatToken, SysUserInfoDTO sysUserInfoDTO, String templateId, Long configId, Map params, String publisher, Long pushSettingId, Boolean manual) { + String api = Constant.API_SEND_MSG + ACCESS_TOKEN + weChatToken; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map postParams = new HashMap<>(); + postParams.put("touser", sysUserInfoDTO.getWxOpenId()); + postParams.put("template_id", templateId); + postParams.put("data", params); + + /*String templateContent = getTemplateContent(templateId); + + List paramList = paramGroups(templateContent); + if (!Objects.isNull(params) && CollUtil.isNotEmpty(paramList)) { + for (String key : paramList) { + String paramKey = key.substring(key.indexOf("{{") + 2, key.indexOf(".DATA")); + if (params.containsKey(paramKey)) { + templateContent = templateContent.replace(key, Objects.isNull(params.get(paramKey)) ? "" : params.get(paramKey).getValue()); + } + } + }*/ + + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : Constant.PUBLISHER_TYPE_WECHAT); + msgHisEntity.setReceiver(sysUserInfoDTO.getRealName()); + msgHisEntity.setPushConfigId(String.valueOf(configId)); + msgHisEntity.setHisMsg(ObjectUtil.isNotNull(params) ? params.get("keyword4").getValue() : "测试发送"); + msgHisEntity.setStatus(PushStatus.SUCCESS.getStatus()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisEntity.setPushReceiveStatus(ObjectUtil.isNotNull(manual) && manual ? PushReceiveStatus.RECEIVE.getStatus() : PushReceiveStatus.NO_RECEIVE.getStatus()); + + String paramJson = JSON.toJSONString(postParams); + // 保证事务生效 将消息记录和发送操作放在msgHisService 如果放在当前类中会导致事务失效 + msgHisService.sendWxMsg(msgHisEntity, api, paramJson, headers); + } + + public void sendMsg(String weChatToken, String userId, String templateId, Long configId, Map params, String publisher, Long pushSettingId, Boolean manual) { + String api = Constant.API_SEND_MSG + ACCESS_TOKEN + weChatToken; + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map postParams = new HashMap<>(); + postParams.put("touser", userId); + postParams.put("template_id", templateId); + postParams.put("data", params); + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : Constant.PUBLISHER_TYPE_WECHAT); + msgHisEntity.setReceiver(userId); + msgHisEntity.setPushConfigId(String.valueOf(configId)); + msgHisEntity.setHisMsg(ObjectUtil.isNotNull(params) ? params.get("keyword4").getValue() : "测试发送"); + msgHisEntity.setStatus(PushStatus.SUCCESS.getStatus()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisEntity.setPushReceiveStatus(ObjectUtil.isNotNull(manual) && manual ? PushReceiveStatus.RECEIVE.getStatus() : PushReceiveStatus.NO_RECEIVE.getStatus()); + + String paramJson = JSON.toJSONString(postParams); + // 保证事务生效 将消息记录和发送操作放在msgHisService 如果放在当前类中会导致事务失效 + msgHisService.sendWxMsg(msgHisEntity, api, paramJson, headers); + } + + public List getGroupList(String weChatToken, String groupName) { + String api = Constant.API_GET_GROUPLIST + ACCESS_TOKEN + weChatToken; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity requestEntity = new HttpEntity<>(headers); + ResponseEntity response = restTemplate.exchange(api, HttpMethod.GET, requestEntity, JSONObject.class); + List userList = new ArrayList<>(); + if (response.getStatusCode().value() == 200 && !Objects.isNull(response.getBody()) && !Objects.isNull(response.getBody().get("tags"))) { + List groupList = (List) response.getBody().get("tags"); + + if (CollUtil.isEmpty(groupList)) { + return userList; + } + for (Map group : groupList) { + String wxGroupName = (String) group.get("name"); + if (wxGroupName.equals(groupName)) { + userList = getGroupUserList(weChatToken, (Integer) group.get("id")); + } + } + } else { + throw new SysException("获取微信公众号分组标签失败:" + getBodyMsg(response)); + } + return userList; + } + + public List getGroupUserList(String weChatToken, Integer tagId) { + String api = Constant.API_GET_GROUPLIST + ACCESS_TOKEN + weChatToken; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map postParams = new HashMap<>(); + postParams.put("tagid", tagId); + postParams.put("next_openid", ""); + String paramJson = JSON.toJSONString(postParams); + HttpEntity requestEntity = new HttpEntity<>(paramJson, headers); + ResponseEntity response = restTemplate.postForEntity(api, requestEntity, JSONObject.class); + if (!Objects.isNull(response) + && response.getStatusCode().value() == 200 + && !Objects.isNull(response.getBody()) + && !Objects.isNull(response.getBody().get("data"))) { + Map data = (Map) response.getBody().get("data"); + List userList = Objects.isNull(data.get("openid")) ? new ArrayList<>() : (List) data.get("openid"); + return userList; + } else { + throw new SysException("获取微信公众号分组关注用户失败:" + getBodyMsg(response)); + } + } + + // 获取模板内容 + public String getTemplateContent(String templateId) { + + String content = msgCacheService.getCache(templateId, "1"); + + if (StringUtils.isNotBlank(content)) { + return content; + } else { + throw new SysException("获取微信公众号模板列表失败"); + } + } + + + public List paramGroups(String content) { + Matcher matcher = patternWeChat.matcher(content); + List paramList = new ArrayList<>(); + while (matcher.find()) { + paramList.add(matcher.group(1)); + } + + return paramList; + } + + public String getUserInfo(String weChatToken, String openId) { + String api = Constant.API_GET_USERINFO + ACCESS_TOKEN + weChatToken + "&openid=" + openId + "&lang=zh_CN"; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map postParams = new HashMap<>(); + String paramJson = JSON.toJSONString(postParams); + HttpEntity requestEntity = new HttpEntity<>(paramJson, headers); + ResponseEntity response = restTemplate.postForEntity(api, requestEntity, JSONObject.class); + String nickname = ""; + if (!Objects.isNull(response) && response.getStatusCode().value() == 200 && !Objects.isNull(response.getBody())) { + Map data = response.getBody(); + if (!Objects.isNull(data) && !Objects.isNull(data.get("nickname"))) { + nickname = String.valueOf(data.get("nickname")); + return nickname; + } + } else { + throw new SysException("获取微信关注用户信息失败:" + getBodyMsg(response)); + } + + return nickname; + } + + public List getUserList(String weChatToken) { + String api = Constant.API_GET_USERLIST + ACCESS_TOKEN + weChatToken + "&next_openid="; + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + Map postParams = new HashMap<>(); + String paramJson = JSON.toJSONString(postParams); + HttpEntity requestEntity = new HttpEntity<>(paramJson, headers); + ResponseEntity response = restTemplate.postForEntity(api, requestEntity, JSONObject.class); + List userList = new ArrayList<>(); + if (!Objects.isNull(response) && response.getStatusCode().value() == 200 && !Objects.isNull(response.getBody()) && !Objects.isNull(response.getBody().get("data"))) { + Map data = (Map) response.getBody().get("data"); + if (!Objects.isNull(data) && !Objects.isNull(data.get("openid"))) { + userList = (List) data.get("openid"); + return userList; + } + } else { + throw new SysException("获取微信公众号关注用户失败:" + getBodyMsg(response)); + } + + return userList; + } + + // 获取微信公众号token + public String getWeChatToken(String appId) { + //根据官方解释,多次生成access_token并且都未过期情况下,老的token不会立即失效,会在5分钟内逐渐失效 + //所以新的token生成并更新至redis的时候即便存在一些线程拿着老token一样可以正常进行接口调用,当redis更新完成后 所有的线程都会使用新的token + //官方链接->https://developers.weixin.qq.com/community/develop/article/doc/000e0e0d52c4d88a0b2c583895b813 + String token = msgCacheService.getCache(appId, "0"); + if (StringUtils.isNotBlank(token)) { + return token; + } else { + throw new SysException("获取微信公众号token失败"); + } + } + + public String getBodyMsg(ResponseEntity response) { + return (!Objects.isNull(response) && !Objects.isNull(response.getBody()) ? response.getBody() + "" : ""); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/init/WeChatTokenRunner.java b/modules/msg/src/main/java/com/thing/msg/push/init/WeChatTokenRunner.java new file mode 100644 index 0000000..4653747 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/init/WeChatTokenRunner.java @@ -0,0 +1,25 @@ + + +package com.thing.msg.push.init; + +import com.thing.msg.cache.service.MsgCacheService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +/** + * 初始化定时任务数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +public class WeChatTokenRunner implements CommandLineRunner { + @Autowired + private MsgCacheService msgCacheService; + + @Override + public void run(String... args) { + // 如果存在微信公众号推送配置 则尝试初始化token + msgCacheService.refreshToken(null); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/mapper/MsgPushMapper.java b/modules/msg/src/main/java/com/thing/msg/push/mapper/MsgPushMapper.java new file mode 100644 index 0000000..bf38b6f --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/mapper/MsgPushMapper.java @@ -0,0 +1,16 @@ +package com.thing.msg.push.mapper; + +import com.mybatisflex.core.BaseMapper; +import com.thing.msg.push.entity.MsgPushEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 消息推送 +* +* @author zy zy@gmail.com +* @since 3.0 2021-12-23 +*/ +@Mapper +public interface MsgPushMapper extends BaseMapper { + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/service/MsgPushService.java b/modules/msg/src/main/java/com/thing/msg/push/service/MsgPushService.java new file mode 100644 index 0000000..7255601 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/service/MsgPushService.java @@ -0,0 +1,48 @@ +package com.thing.msg.push.service; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.push.dto.*; +import com.thing.msg.push.entity.MsgPushEntity; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; + + +import java.util.List; + +/** + * 消息推送 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-23 + */ +public interface MsgPushService extends IBaseService { + void sendMsg(Long[] ids); + + void sendCustomContentMsg(List dtoList); + + void saveInfo(MsgPushDTO dto); + + void updateInfo(MsgPushDTO dto); + + void sendCustomContentMsg(List msgPushDTOList, String publisher, List sysUserInfoDTOList, Long pushSettingId, Boolean repeat); + + MailAllParams checkMailParams(String params, boolean updateDefaultConfig); + + WeChatParams checkWeChatParams(String params, boolean updateDefaultConfig); + + WeChatComParams checkWeChatComParams(String params, boolean updateDefaultConfig); + + SmsParams checkSmsParams(String params, boolean updateDefaultConfig); + + DingTalkParams checkDingTalkParams(String params, boolean updateDefaultConfig); + + void sendMsgByUserId(Long[] ids, List userId); + + void sendCustomContentMsgByUserId(List msgPushDTOList, List userId); + + void sendCustomContentMsgByUserId(List msgPushDTOList, List sysUserInfoDTOList, String publisher, Long pushSettingId, Boolean repeat); + + void sendMsgByTimeTask(); + + List selectList(QueryWrapper queryWrapper); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/service/impl/MsgPushServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/push/service/impl/MsgPushServiceImpl.java new file mode 100644 index 0000000..cd2c037 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/service/impl/MsgPushServiceImpl.java @@ -0,0 +1,499 @@ +package com.thing.msg.push.service.impl; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.google.gson.Gson; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.MsgType; +import com.thing.common.core.enumeration.PushStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.*; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.cache.service.MsgCacheService; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.*; +import com.thing.msg.push.entity.MsgPushEntity; +import com.thing.msg.push.factory.PushMsgFactory; +import com.thing.msg.push.mapper.MsgPushMapper; +import com.thing.msg.param.service.MsgSysParamsService; +import com.thing.msg.push.service.MsgPushService; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.dto.SysUserInfoDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 消息推送 + * + * @author zy zy@gmail.com + * @since 3.0 2021-12-23 + */ +@Service +public class MsgPushServiceImpl extends BaseServiceImpl implements MsgPushService { + + @Resource + private MsgSysParamsService msgSysParamsService; + + @Resource + private MsgCacheService msgCacheService; + + @Resource + private MsgUserService msgUserService; + + @Resource + private MsgHisService msgHisService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + if (!Objects.isNull(params.get("configName")) && StringUtils.isNotBlank(String.valueOf(params.get("configName")))) { + wrapper.like("config_name", String.valueOf(params.get("configName"))); + } + + if (!Objects.isNull(params.get("configType")) && StringUtils.isNotBlank(String.valueOf(params.get("configType")))) { + wrapper.eq("config_type", String.valueOf(params.get("configType"))); + } + + if (!Objects.isNull(params.get("executeByJob")) && StringUtils.isNotBlank(String.valueOf(params.get("executeByJob")))) { + wrapper.eq("execute_by_job", String.valueOf(params.get("executeByJob"))); + } + return wrapper; + } + + + @Override + public void sendMsg(Long[] ids) { + List dtoList = checkParamsAndReturnList(ids); + for (MsgPushDTO msgPushDTO : dtoList) { + //程序内部调用(页面新增、修改配置时updateDefaultConfig传true,发送消息时传false,updateDefaultConfig影响页面的默认配置项,页面操作有利于下次操作直接拿默认配置不需要重新填值) + PushMsgFactory.buildSend(msgPushDTO, null, null, null, false); + } + } + + @Override + public void sendCustomContentMsg(List customPushDTOList) { + List list = customPushDTOList.stream().map(CustomPushDTO::getId).collect(Collectors.toList()); + Map map = customPushDTOList.stream().collect(Collectors.toMap(CustomPushDTO::getId, CustomPushDTO::getMsg)); + List dtoList = checkParamsAndReturnList(stringToLong(list)); + for (MsgPushDTO msgPushDTO : dtoList) { + msgPushDTO.setTemplateMsg(map.get(String.valueOf(msgPushDTO.getId()))); + //程序内部调用(页面新增、修改配置时updateDefaultConfig传true,发送消息时传false,updateDefaultConfig影响页面的默认配置项,页面操作有利于下次操作直接拿默认配置不需要重新填值) + PushMsgFactory.buildSend(msgPushDTO, null, null, null, false); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveInfo(MsgPushDTO dto) { + this.saveDto(dto); + // 验证各个推送类型是否参数正确 + checkParams(dto, false); + + if ("2".equals(dto.getConfigType())) { + MsgPushEntity msgPushEntity = new MsgPushEntity(); + BeanUtils.copyProperties(dto, msgPushEntity); + msgCacheService.refreshToken(Arrays.asList(msgPushEntity)); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateInfo(MsgPushDTO dto) { + + MsgPushDTO msgPushDTO = this.getByIdAs(dto.getId(),MsgPushDTO.class); + Map oldParam = JSONObject.parseObject(msgPushDTO.getConfigParams(), HashMap.class); + Map newParam = JSONObject.parseObject(dto.getConfigParams(), HashMap.class); + // 如果数据库appId和appSecret与提交的不一致则更新token(更新token的操作不建议太频繁) + boolean updateToken = true; + if (!Objects.isNull(oldParam.get("appId")) && !oldParam.get("appId").equals(newParam.get("appId")) + || !Objects.isNull(oldParam.get("appSecret")) && oldParam.get("appSecret").equals(newParam.get("appSecret"))) { + updateToken = false; + } + + this.updateDto(dto); + // 验证各个推送类型是否参数正确 + checkParams(dto, false); + + if ("2".equals(dto.getConfigType()) && updateToken) { + MsgPushEntity msgPushEntity = new MsgPushEntity(); + BeanUtils.copyProperties(dto, msgPushEntity); + msgCacheService.refreshToken(Arrays.asList(msgPushEntity)); + } + } + + + @Override + public void sendCustomContentMsg(List msgPushDTOList, String publisher, List receiverList, Long pushSettingId, Boolean repeat) { + if (CollectionUtil.isEmpty(msgPushDTOList)) { + throw new SysException("配置项不可用"); + } + List list = new ArrayList<>(); + for (MsgPushDTO dto : msgPushDTOList) { + if (repeat) { + list.add(dto); + continue; + } + MsgPushDTO msgPushDTO = this.getByIdAs(dto.getId(),MsgPushDTO.class); + if (ObjectUtil.isNotNull(msgPushDTO)) { + if (StringUtils.isNotBlank(dto.getConfigParams())) msgPushDTO.setConfigParams(dto.getConfigParams()); + msgPushDTO.setTemplateId(ObjectUtil.isNotNull(dto.getTemplateId()) ? dto.getTemplateId() : null); + String templateMsg = dto.getTemplateMsg(); + if (ObjectUtil.isNotNull(dto.getTemplateId())) { + JSONObject jsonObject = new JSONObject(); + //todo 公众号暂时没有模板,等申请的模板下来后修改回去 + if (dto.getConfigType().equals(MsgType.WeChat.getValue())) { + jsonObject.put("keyword4", dto.getTemplateMsg()); + } else { + jsonObject.put("msg", dto.getTemplateMsg()); + } + templateMsg = jsonObject.toJSONString(); + } + msgPushDTO.setTemplateMsg(templateMsg); + if (dto.getManual()) msgPushDTO.setManual(dto.getManual()); + list.add(msgPushDTO); + } else { + list.add(dto); + } + } + list.forEach(dto -> checkParams(dto, false)); + list.forEach(dto -> PushMsgFactory.buildSend(dto, publisher, receiverList, pushSettingId, repeat)); + } + + public void checkParams(MsgPushDTO dto, boolean updateDefaultConfig) { + if(dto == null || StringUtils.isBlank(dto.getConfigType())) { + throw new SysException("未找到类型configType"); + } + switch (dto.getConfigType()) { + case "0": + checkSmsParams(dto.getConfigParams(), updateDefaultConfig); + break; + case "1": + checkMailParams(dto.getConfigParams(), updateDefaultConfig); + break; + case "2": + checkWeChatParams(dto.getConfigParams(), updateDefaultConfig); + break; + case "3": + checkDingTalkParams(dto.getConfigParams(), updateDefaultConfig); + break; + case "4": + checkWeChatComParams(dto.getConfigParams(), updateDefaultConfig); + break; + default: + throw new SysException("当前不支持类型configType:" + dto.getConfigType()); + } + } + + + public T getParamObj(String params, Class clazz) { + T customObject; + try { + customObject = JSONObject.parseObject(params, clazz); + } catch (Exception e) { + throw new SysException("配置项:" + params + "无法解析,请确认"); + } + + if (Objects.isNull(customObject)) { + throw new SysException("参数configParams无效"); + } + return customObject; + } + + + @Override + public MailAllParams checkMailParams(String params, boolean updateDefaultConfig) { + MailAllParams mailAllParams = getParamObj(params, MailAllParams.class); + //效验数据 + ValidatorUtils.validateEntity(mailAllParams); + // 收件人 + String[] to = String.valueOf(mailAllParams.getTo()).split(","); + if (to.length == 0) { + throw new SysException("收件人不能为空"); + } + + // 收件人 + String[] cc = !Objects.isNull(mailAllParams.getCc()) && StringUtils.isNotBlank(mailAllParams.getCc()) ? mailAllParams.getCc().split(",") : null; + mailAllParams.setToArr(to); + mailAllParams.setCcArr(cc); + + if (updateDefaultConfig) { + // 验证通过放入redis + MailParams mailParams = new MailParams(); + BeanUtils.copyProperties(mailAllParams, mailParams); + Map param = JSONObject.parseObject(JSON.toJSONString(mailParams, SerializerFeature.WriteNullStringAsEmpty), HashMap.class); + + updateValueByCode(Constant.MAIL_CONFIG_KEY, new Gson().toJson(param)); + } + + return mailAllParams; + } + + @Override + public WeChatParams checkWeChatParams(String params, boolean updateDefaultConfig) { + WeChatParams weChatParams = getParamObj(params, WeChatParams.class); + if (Objects.isNull(weChatParams.getType())) { + throw new SysException("configParams中缺少必填项type,0:分组推送 1:指定人推送"); + } + //效验数据 + if ("0".equals(weChatParams.getType())) { + ValidatorUtils.validateEntity(weChatParams, GroupNameGroup.class, DefaultGroup.class); + } else if ("1".equals(weChatParams.getType())) { + ValidatorUtils.validateEntity(weChatParams, WxOpenIdGroup.class, DefaultGroup.class); + } else { + throw new SysException("微信推送方式type暂不支持其它类型"); + } + + if (updateDefaultConfig) { + Map param = JSONObject.parseObject(JSON.toJSONString(weChatParams, SerializerFeature.WriteNullStringAsEmpty), HashMap.class); + param.remove("configId"); + updateValueByCode(Constant.WECHAT_CONFIG_KEY, new Gson().toJson(param)); + } + return weChatParams; + } + + @Override + public WeChatComParams checkWeChatComParams(String params, boolean updateDefaultConfig) { + WeChatComParams weChatComParams = getParamObj(params, WeChatComParams.class); + if (!"text".equals(weChatComParams.getMsgType())) { + throw new SysException("当前仅支持text类型的消息推送"); + } + ValidatorUtils.validateEntity(weChatComParams); + + if (updateDefaultConfig) { + Map webhookParam = JSONObject.parseObject(JSON.toJSONString(weChatComParams, SerializerFeature.WriteNullStringAsEmpty), HashMap.class); + webhookParam.remove("configId"); + updateValueByCode(Constant.WECHAT_COM_CONFIG_KEY, new Gson().toJson(webhookParam)); + } + + return weChatComParams; + } + + @Override + public SmsParams checkSmsParams(String params, boolean updateDefaultConfig) { + SmsParams smsParams = getParamObj(params, SmsParams.class); + if (Objects.isNull(smsParams.getPlatform())) { + throw new SysException("configParams中缺少必填项platform:1:阿里云 2:腾讯云 3:七牛"); + } + if (smsParams.getPlatform() == 1) { + ValidatorUtils.validateEntity(smsParams, AliyunGroup.class, DefaultGroup.class); + } else if (smsParams.getPlatform() == 2) { + ValidatorUtils.validateEntity(smsParams, QcloudGroup.class, DefaultGroup.class); + } else if (smsParams.getPlatform() == 3) { + ValidatorUtils.validateEntity(smsParams, QiniuGroup.class, DefaultGroup.class); + } else { + throw new SysException("platform暂不支持其它平台类型"); + } + //效验数据 + if (updateDefaultConfig) { + Map param = JSONObject.parseObject(JSON.toJSONString(smsParams, SerializerFeature.WriteNullStringAsEmpty), HashMap.class); + param.remove("configId"); + updateValueByCode(Constant.SMS_CONFIG_KEY, new Gson().toJson(param)); + } + + return smsParams; + } + + @Override + public DingTalkParams checkDingTalkParams(String params, boolean updateDefaultConfig) { + DingTalkParams dingTalkParams = getParamObj(params, DingTalkParams.class); + if (!"text".equals(dingTalkParams.getMsgType())) { + throw new SysException("当前仅支持text类型的消息推送"); + } + ValidatorUtils.validateEntity(dingTalkParams); + + if (updateDefaultConfig) { + Map webhookParam = JSONObject.parseObject(JSON.toJSONString(dingTalkParams, SerializerFeature.WriteNullStringAsEmpty), HashMap.class); + webhookParam.remove("configId"); + webhookParam.remove("atMobiles"); + updateValueByCode(Constant.DINGTALK_CONFIG_KEY, new Gson().toJson(webhookParam)); + } + + return dingTalkParams; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendMsgByUserId(Long[] ids, List userIds) { + List dtoList = checkParamsAndReturnList(ids); + buildMsgDTOList(dtoList, userIds); + for (MsgPushDTO msgPushDTO : dtoList) { + //程序内部调用(页面新增、修改配置时updateDefaultConfig传true,发送消息时传false,updateDefaultConfig影响页面的默认配置项,页面操作有利于下次操作直接拿默认配置不需要重新填值) + PushMsgFactory.buildSend(msgPushDTO, null, null, null, false); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendCustomContentMsgByUserId(List msgPushDTOList, List userIds) { + try { + buildMsgDTOList(msgPushDTOList, userIds); + sendCustomContentMsg(msgPushDTOList, null, null, null, false); + } catch (Exception e) { + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(Constant.PUBLISHER_TYPE_ALARM); + msgHisEntity.setReceiver(userIds.stream().map(String::valueOf).collect(Collectors.joining(","))); + msgHisEntity.setPushConfigId(msgPushDTOList.stream().map(item -> String.valueOf(item.getId())).collect(Collectors.joining(","))); + msgHisEntity.setStatus(PushStatus.FAIL.getStatus()); + msgHisEntity.setStatusMsg(e.getMessage()); + msgHisService.save(msgHisEntity); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendCustomContentMsgByUserId(List msgPushDTOList, List sysUserInfoDTOList, String publisher, Long pushSettingId, Boolean repeat) { + try { + List userIds = sysUserInfoDTOList.stream().map(SysUserInfoDTO::getId).collect(Collectors.toList()); + buildMsgDTOList(msgPushDTOList, userIds); + sendCustomContentMsg(msgPushDTOList, publisher, sysUserInfoDTOList, pushSettingId, repeat); + } catch (Exception e) { + MsgHisEntity msgHisEntity = new MsgHisEntity(); + List realNames = sysUserInfoDTOList.stream().map(SysUserInfoDTO::getRealName).collect(Collectors.toList()); + msgHisEntity.setPublisher(StringUtils.isNotBlank(publisher) ? publisher : Constant.PUBLISHER_TYPE_ALARM); + msgHisEntity.setReceiver(realNames.stream().map(String::valueOf).collect(Collectors.joining(","))); + msgHisEntity.setPushConfigId(msgPushDTOList.stream().map(item -> String.valueOf(item.getId())).collect(Collectors.joining(","))); + msgHisEntity.setStatus(PushStatus.FAIL.getStatus()); + msgHisEntity.setStatusMsg(e.getMessage()); + if(ObjectUtil.isNotNull(pushSettingId)) msgHisEntity.setPushSettingId(pushSettingId); + msgHisService.save(msgHisEntity); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendMsgByTimeTask() { + List list = mapper.selectListByQuery(QueryWrapper.create().eq(MsgPushEntity::getExecuteByJob, "1")); + if (CollectionUtil.isEmpty(list)) { + return; + } + sendMsg(list.parallelStream().map(MsgPushEntity::getId).toArray(Long[]::new)); + } + + @Override + public List selectList(QueryWrapper queryWrapper) { + return null; + } + + private void updateValueByCode(String key, String configParam) { + try { + msgSysParamsService.updateByCode(key, configParam); + } catch (Exception e) { + throw new SysException("保存配置项失败:" + e.getMessage()); + } + } + + private void buildMsgDTOList(List msgPushDTOList, List userIds) { + List msgUserDTO = msgUserService.getInfoByUserIds(userIds); + msgPushDTOList.parallelStream().map(dto -> { + switch (dto.getConfigType()) { + case "0": + //todo 暂未接入 + String smsConfigParams = buildSmsConfigParams(dto.getConfigParams(), null); + dto.setConfigParams(smsConfigParams); + return dto; + case "1": + String toEmailUserNames = msgUserDTO.stream().map(MsgUserDTO::getEmailUsername).filter(StringUtils::isNotBlank).collect(Collectors.joining(",")); + if (StringUtils.isBlank(toEmailUserNames)) throw new SysException("所有的用户均未设置邮箱,无法推送"); + String mailConfigParams = buildMailConfigParams(dto.getConfigParams(), toEmailUserNames); + dto.setConfigParams(mailConfigParams); + return dto; + case "2": + String wxOpenIds = msgUserDTO.stream().map(MsgUserDTO::getWxOpenId).filter(StringUtils::isNotBlank).collect(Collectors.joining(",")); + if (StringUtils.isBlank(wxOpenIds)) throw new SysException("所有的用户均未绑定微信,无法推送"); + String weChatConfigParams = buildWeChatConfigParams(dto.getConfigParams(), wxOpenIds); + dto.setConfigParams(weChatConfigParams); + return dto; + case "3": + String dingPhones = msgUserDTO.stream().map(MsgUserDTO::getDingdingPhone).filter(StringUtils::isNotBlank).collect(Collectors.joining(",")); + if (StringUtils.isBlank(dingPhones)) throw new SysException("所有的用户均未设置钉钉电话,无法推送"); + String dingTalkConfigParams = buildDingTalkConfigParams(dto.getConfigParams(), dingPhones); + dto.setConfigParams(dingTalkConfigParams); + return dto; + case "4": + String wxPhones = msgUserDTO.stream().map(MsgUserDTO::getWxPhone).filter(StringUtils::isNotBlank).collect(Collectors.joining(",")); + if (StringUtils.isBlank(wxPhones)) throw new SysException("所有的用户均未设置企业微信电话,无法推送"); + String weChatComConfigParams = buildWeChatComConfigParams(dto.getConfigParams(), wxPhones); + dto.setConfigParams(weChatComConfigParams); + return dto; + default: + throw new SysException("当前不支持类型configType:" + dto.getConfigType()); + } + }).collect(Collectors.toList()); + } + + private String buildSmsConfigParams(String configParams, String smsPhones) { + //todo 短信替换,暂不接入 + JSONObject strJson = JSONObject.parseObject(configParams); + //将用户绑定信息填入configParams + /*strJson.put("aliyunAccessKeyId", "1"); + strJson.put("aliyunAccessKeyId", "1");*/ + return strJson.toString(); + } + + private String buildMailConfigParams(String configParams, String toEmailUserNames) { + JSONObject strJson = JSONObject.parseObject(configParams); + //将用户绑定信息填入configParams + strJson.put("to", toEmailUserNames); + return strJson.toString(); + } + + private String buildWeChatConfigParams(String configParams, String wxOpenIds) { + JSONObject strJson = JSONObject.parseObject(configParams); + //将用户绑定信息填入configParams + strJson.put("wxOpenId", wxOpenIds); + return strJson.toString(); + } + + private String buildDingTalkConfigParams(String configParams, String dingPhones) { + JSONObject strJson = JSONObject.parseObject(configParams); + //将用户绑定信息填入configParams + strJson.put("atMobilesStr", dingPhones); + return strJson.toString(); + } + + private String buildWeChatComConfigParams(String configParams, String wxPhones) { + JSONObject strJson = JSONObject.parseObject(configParams); + //将用户绑定信息填入configParams + strJson.put("atMobilesStr", wxPhones); + return strJson.toString(); + } + + private List checkParamsAndReturnList(Long[] ids) { + if (Objects.isNull(ids) || ids.length == 0) { + throw new SysException("请选择需发送的配置项"); + } + List idList = Arrays.stream(ids).distinct().collect(Collectors.toList()); + List list = mapper.selectListByQuery(QueryWrapper.create().in(MsgPushEntity::getId, idList)); + if(CollectionUtil.isEmpty(list)) { + throw new SysException("id为" + idList.stream().map(String::valueOf).collect(Collectors.joining(",")) + "的配置项不存在"); + } + if(idList.size() != list.size()) { + String exists = list.parallelStream().map(item -> String.valueOf(item.getId())).collect(Collectors.joining(",")); + throw new SysException("id为" + idList.parallelStream().filter(id -> !exists.contains(String.valueOf(id))).map(String::valueOf).collect(Collectors.joining(",")) + "的配置项不存在"); + } + return ConvertUtils.sourceToTarget(list, MsgPushDTO.class); + } + + public static Long[] stringToLong(List list) { + Long[] arr = new Long[list.size()]; + for (int i = 0; i < list.size(); i++) { + arr[i] = Long.parseLong(list.get(i)); + } + return arr; + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/AbstractSmsService.java b/modules/msg/src/main/java/com/thing/msg/push/sms/AbstractSmsService.java new file mode 100644 index 0000000..354e5f5 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/AbstractSmsService.java @@ -0,0 +1,24 @@ + + +package com.thing.msg.push.sms; + + +import com.thing.msg.push.dto.SmsParams; + +import java.util.LinkedHashMap; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +public abstract class AbstractSmsService { + /** + * 短信配置信息 + */ + SmsConfig config; + + public abstract void sendSmsNew(SmsParams smsParams, LinkedHashMap params); + + public abstract void sendSms(SmsParams smsParams, LinkedHashMap params, String signName, String template); +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/AliyunSmsService.java b/modules/msg/src/main/java/com/thing/msg/push/sms/AliyunSmsService.java new file mode 100644 index 0000000..f787df4 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/AliyunSmsService.java @@ -0,0 +1,114 @@ + + +package com.thing.msg.push.sms; + +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.IAcsClient; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; +import com.aliyuncs.exceptions.ClientException; +import com.aliyuncs.http.MethodType; +import com.aliyuncs.profile.DefaultProfile; +import com.aliyuncs.profile.IClientProfile; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.SmsParams; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.LinkedHashMap; + +/** + * 阿里云短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +public class AliyunSmsService extends AbstractSmsService { + /** + * 短信API产品名称(短信产品名固定,无需修改) + */ + private final String PRODUCT = "Dysmsapi"; + /** + * 短信API产品域名(接口地址固定,无需修改) + */ + private final String DOMAIN = "dysmsapi.aliyuncs.com"; + + private IClientProfile profile; + + @Resource + private MsgHisService msgHisService; + + + public AliyunSmsService(SmsConfig config) { + this.config = config; + + //初始化 + init(); + } + + private void init() { + try { + //初始化acsClient,暂不支持region化 + profile = DefaultProfile.getProfile("cn-hangzhou", config.getAliyunAccessKeyId(), config.getAliyunAccessKeySecret()); + DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN); + } catch (ClientException e) { + e.printStackTrace(); + } + } + + @Override + public void sendSmsNew(SmsParams smsParams, LinkedHashMap params) { + this.sendSms(smsParams, params, config.getAliyunSignName(), config.getAliyunTemplateCode()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendSms(SmsParams smsParams, LinkedHashMap params, String signName, String template) { + System.setProperty("sun.net.client.defaultConnectTimeout", "30000"); + System.setProperty("sun.net.client.defaultReadTimeout", "30000"); + + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(Constant.PUBLISHER_TYPE_SMS + smsParams.getPlatform()); + msgHisEntity.setReceiver(smsParams.getMobile()); + msgHisEntity.setPushConfigId(String.valueOf(smsParams.getConfigId())); + msgHisEntity.setHisMsg(template); + + //组装请求对象 + SendSmsRequest request = new SendSmsRequest(); + request.setMethod(MethodType.POST); + //待发送手机号,支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 + //发送国际/港澳台消息时,接收号码格式为00+国际区号+号码,如"0085200000000" + request.setPhoneNumbers(smsParams.getMobile()); + //短信签名-可在短信控制台中找到 + request.setSignName(signName); + //短信模板-可在短信控制台中找到 + request.setTemplateCode(template); + //参数 + if (MapUtil.isNotEmpty(params)) { + request.setTemplateParam(JSON.toJSONString(params)); + } + + SendSmsResponse response; + try { + IAcsClient acsClient = new DefaultAcsClient(profile); + response = acsClient.getAcsResponse(request); + } catch (ClientException e) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, e, ""); + } + + int status = Constant.SUCCESS; + if (!Constant.OK.equalsIgnoreCase(response.getCode())) { + status = Constant.FAIL; + } + + if (status == Constant.FAIL) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, response.getMessage()); + } + msgHisService.save(msgHisEntity); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/QcloudSmsService.java b/modules/msg/src/main/java/com/thing/msg/push/sms/QcloudSmsService.java new file mode 100644 index 0000000..159f5f3 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/QcloudSmsService.java @@ -0,0 +1,73 @@ + + +package com.thing.msg.push.sms; + +import cn.hutool.core.map.MapUtil; +import com.github.qcloudsms.SmsSingleSender; +import com.github.qcloudsms.SmsSingleSenderResult; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.SmsParams; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.LinkedHashMap; + +/** + * 腾讯云短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +public class QcloudSmsService extends AbstractSmsService { + + @Resource + private MsgHisService msgHisService; + + public QcloudSmsService(SmsConfig config){ + this.config = config; + } + + @Override + public void sendSmsNew(SmsParams smsParams, LinkedHashMap params) { + this.sendSms(smsParams, params, config.getQcloudSignName(), config.getQcloudTemplateId()); + } + @Override + @Transactional(rollbackFor = Exception.class) + public void sendSms(SmsParams smsParams, LinkedHashMap params, String signName, String template) { + //保存短信记录 + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(Constant.PUBLISHER_TYPE_SMS + smsParams.getPlatform()); + msgHisEntity.setReceiver(smsParams.getMobile()); + msgHisEntity.setPushConfigId(String.valueOf(smsParams.getConfigId())); + msgHisEntity.setHisMsg(template); + + SmsSingleSender sender = new SmsSingleSender(config.getQcloudAppId(), config.getQcloudAppKey()); + //短信参数 + ArrayList paramsList = new ArrayList<>(); + if(MapUtil.isNotEmpty(params)){ + for(String value : params.values()){ + paramsList.add(value); + } + } + SmsSingleSenderResult result; + try { + result = sender.sendWithParam("86", smsParams.getMobile(), Integer.parseInt(template), paramsList, signName, null, null); + } catch (Exception e) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, e, ""); + } + + int status = Constant.SUCCESS; + if(result.result != 0){ + status = Constant.FAIL; + } + + if(status == Constant.FAIL){ + throw new SysException(ErrorCode.SEND_SMS_ERROR, result.errMsg); + } + msgHisService.save(msgHisEntity); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/QiniuSmsService.java b/modules/msg/src/main/java/com/thing/msg/push/sms/QiniuSmsService.java new file mode 100644 index 0000000..f1e4ac1 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/QiniuSmsService.java @@ -0,0 +1,74 @@ + + +package com.thing.msg.push.sms; + +import com.qiniu.http.Response; +import com.qiniu.sms.SmsManager; +import com.qiniu.util.Auth; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.msg.history.entity.MsgHisEntity; +import com.thing.msg.history.service.MsgHisService; +import com.thing.msg.push.dto.SmsParams; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.LinkedHashMap; + +/** + * 七牛短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +public class QiniuSmsService extends AbstractSmsService { + private SmsManager smsManager; + + @Resource + private MsgHisService msgHisService; + + public QiniuSmsService(SmsConfig config){ + this.config = config; + + //初始化 + init(); + } + + private void init(){ + Auth auth = Auth.create(config.getQiniuAccessKey(), config.getQiniuSecretKey()); + smsManager = new SmsManager(auth); + } + + @Override + public void sendSmsNew(SmsParams smsParams, LinkedHashMap params) { + this.sendSms(smsParams, params, null, config.getQiniuTemplateId()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sendSms(SmsParams smsParams, LinkedHashMap params, String signName, String template) { + //保存短信记录 + MsgHisEntity msgHisEntity = new MsgHisEntity(); + msgHisEntity.setPublisher(Constant.PUBLISHER_TYPE_SMS + smsParams.getPlatform()); + msgHisEntity.setReceiver(smsParams.getMobile()); + msgHisEntity.setPushConfigId(String.valueOf(smsParams.getConfigId())); + msgHisEntity.setHisMsg(template); + + Response response; + try { + response = smsManager.sendSingleMessage(template, smsParams.getMobile(), params); + } catch (Exception e) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, e, ""); + } + + int status = Constant.SUCCESS; + if(!response.isOK()){ + status = Constant.FAIL; + } + + if(status == Constant.FAIL){ + throw new SysException(ErrorCode.SEND_SMS_ERROR, response.error); + } + msgHisService.save(msgHisEntity); + } +} diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/SmsConfig.java b/modules/msg/src/main/java/com/thing/msg/push/sms/SmsConfig.java new file mode 100644 index 0000000..0e53462 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/SmsConfig.java @@ -0,0 +1,75 @@ + + +package com.thing.msg.push.sms; + +import com.thing.common.core.validator.group.AliyunGroup; +import com.thing.common.core.validator.group.QcloudGroup; +import com.thing.common.core.validator.group.QiniuGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; + +/** + * 短信配置信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "短信配置信息") +@Valid +public class SmsConfig implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "阿里云AccessKeyId") + @NotBlank(message="configParams中缺少必填项aliyunAccessKeyId", groups = AliyunGroup.class) +// @NotEmpty(message="aliyunAccessKeyId为必填项", groups = AliyunGroup.class) + private String aliyunAccessKeyId; + + @Schema(description = "阿里云AccessKeySecret") + @NotBlank(message="configParams中缺少必填项aliyunAccessKeySecret", groups = AliyunGroup.class) + private String aliyunAccessKeySecret; + + @Schema(description = "阿里云短信签名") + @NotBlank(message="configParams中缺少必填项aliyunSignName", groups = AliyunGroup.class) + private String aliyunSignName; + + @Schema(description = "阿里云短信模板") + @NotBlank(message="configParams中缺少必填项aliyunTemplateCode", groups = AliyunGroup.class) + private String aliyunTemplateCode; + + @Schema(description = "腾讯云AppId") + @NotNull(message="configParams中缺少必填项qcloudAppId", groups = QcloudGroup.class) + private Integer qcloudAppId; + + @Schema(description = "腾讯云AppKey") + @NotBlank(message="configParams中缺少必填项qcloudAppKey", groups = QcloudGroup.class) + private String qcloudAppKey; + + @Schema(description = "腾讯云短信签名") + @NotBlank(message="configParams中缺少必填项qcloudSignName", groups = QcloudGroup.class) + private String qcloudSignName; + + @Schema(description = "腾讯云短信模板ID") + @NotBlank(message="configParams中缺少必填项qcloudTemplateId", groups = QcloudGroup.class) + private String qcloudTemplateId; + + @Schema(description = "七牛accesskey") + @NotNull(message="configParams中缺少必填项qiniuAccessKey", groups = QiniuGroup.class) + private String qiniuAccessKey; + + @Schema(description = "七牛SecretKey") + @NotBlank(message="configParams中缺少必填项qiniuSecretKey", groups = QiniuGroup.class) + private String qiniuSecretKey; + + @Schema(description = "七牛短信模板ID") + @NotBlank(message="configParams中缺少必填项qiniuTemplateId", groups = QiniuGroup.class) + private String qiniuTemplateId; +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/push/sms/SmsFactory.java b/modules/msg/src/main/java/com/thing/msg/push/sms/SmsFactory.java new file mode 100644 index 0000000..f96fb4a --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/push/sms/SmsFactory.java @@ -0,0 +1,27 @@ + + +package com.thing.msg.push.sms; + + +import com.thing.common.core.enumeration.SmsService; +import com.thing.msg.push.dto.SmsParams; + +/** + * 短信Factory + * + * @author Mark sunlightcs@gmail.com + */ +public class SmsFactory { + + public static AbstractSmsService build(SmsParams smsParams) { + if (smsParams.getPlatform() == SmsService.ALIYUN.getValue()) { + return new AliyunSmsService(smsParams); + } else if (smsParams.getPlatform() == SmsService.QCLOUD.getValue()) { + return new QcloudSmsService(smsParams); + } else if (smsParams.getPlatform() == SmsService.QINIU.getValue()) { + return new QiniuSmsService(smsParams); + } + + return null; + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/controller/MsgTemplateController.java b/modules/msg/src/main/java/com/thing/msg/template/controller/MsgTemplateController.java new file mode 100644 index 0000000..9495d58 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/controller/MsgTemplateController.java @@ -0,0 +1,100 @@ +package com.thing.msg.template.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.service.MsgTemplateService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** +* 消息模板 +* +* @author zy zy@xxx.com +* @since 3.0 2021-12-29 +*/ +@RestController +@RequestMapping("msgtemplate/msgtemplate") +@Tag(name="消息模板") +public class MsgTemplateController { + @Autowired + private MsgTemplateService msgTemplateService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="模板名称") + }) + public Result> page( @RequestParam Map params){ + PageData page = msgTemplateService.getPageData(params,MsgTemplateDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + MsgTemplateDTO data = msgTemplateService.getByIdAs(id,MsgTemplateDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody MsgTemplateDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + msgTemplateService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody MsgTemplateDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgTemplateService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + msgTemplateService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("getAllTemplate") + @Operation(summary="查询所有模板") + public Result> getAllTemplate(@RequestParam(required = false) String name,@RequestParam(required = false) String type) { + List allTemplate = msgTemplateService.getAllTemplate(name,type); + return new Result>().ok(allTemplate); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/dto/MsgTemplateDTO.java b/modules/msg/src/main/java/com/thing/msg/template/dto/MsgTemplateDTO.java new file mode 100644 index 0000000..43a1bf4 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/dto/MsgTemplateDTO.java @@ -0,0 +1,56 @@ +package com.thing.msg.template.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 消息模板 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-29 + */ +@Data +@Schema( name= "消息模板") +@Valid +public class MsgTemplateDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @Schema(description = "模板id(修改时必填)") + @NotNull(message = "模板id为必填项", groups = UpdateGroup.class) + private Long id; + @Schema(description = "模板名称") + @NotBlank(message = "模板名称为必填项", groups = DefaultGroup.class) + private String name; + @Schema(description = "模板类型(暂时仅支持邮箱、钉钉) 1:邮箱模板 3:钉钉模板") + @NotBlank(message = "模板类型为必填项", groups = DefaultGroup.class) + private String type; + @Schema(description = "模板主题") + private String subject; + @Schema(description = "消息正文内容(如果有变量,变量需定义为${name}的方式)") + private String content; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "企业ID") + private Long companyId; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/entity/MsgTemplateEntity.java b/modules/msg/src/main/java/com/thing/msg/template/entity/MsgTemplateEntity.java new file mode 100644 index 0000000..d352069 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/entity/MsgTemplateEntity.java @@ -0,0 +1,44 @@ +package com.thing.msg.template.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 消息模板 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-29 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_template") +public class MsgTemplateEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模板名称 + */ + private String name; + /** + * 模板类型(暂时仅支持邮箱) 1:邮箱模板 + */ + private String type; + /** + * 模板主题 + */ + private String subject; + /** + * 消息正文内容 + */ + private String content; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/mapper/MsgTemplateMapper.java b/modules/msg/src/main/java/com/thing/msg/template/mapper/MsgTemplateMapper.java new file mode 100644 index 0000000..d245ba6 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/mapper/MsgTemplateMapper.java @@ -0,0 +1,16 @@ +package com.thing.msg.template.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.msg.template.entity.MsgTemplateEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 消息模板 +* +* @author zy zy@xxx.com +* @since 3.0 2021-12-29 +*/ +@Mapper +public interface MsgTemplateMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/service/MsgTemplateService.java b/modules/msg/src/main/java/com/thing/msg/template/service/MsgTemplateService.java new file mode 100644 index 0000000..79a4156 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/service/MsgTemplateService.java @@ -0,0 +1,17 @@ +package com.thing.msg.template.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.entity.MsgTemplateEntity; + +import java.util.List; + +/** + * 消息模板 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-29 + */ +public interface MsgTemplateService extends IBaseService { + List getAllTemplate(String name, String type); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/template/service/impl/MsgTemplateServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/template/service/impl/MsgTemplateServiceImpl.java new file mode 100644 index 0000000..256173f --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/template/service/impl/MsgTemplateServiceImpl.java @@ -0,0 +1,49 @@ +package com.thing.msg.template.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.template.mapper.MsgTemplateMapper; +import com.thing.msg.template.dto.MsgTemplateDTO; +import com.thing.msg.template.entity.MsgTemplateEntity; +import com.thing.msg.template.service.MsgTemplateService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 消息模板 + * + * @author zy zy@xxx.com + * @since 3.0 2021-12-29 + */ +@Service +public class MsgTemplateServiceImpl extends BaseServiceImpl implements MsgTemplateService { + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String name = String.valueOf(params.get("name")); + if (!Objects.isNull(params.get("name")) && StringUtils.isNotBlank(name)) { + wrapper.like("name", name); + } + return wrapper; + } + + @Override + public List getAllTemplate(String name, String type) { + QueryWrapper wrapper = new QueryWrapper(); + if (StringUtils.isNotBlank(name)) { + wrapper.like("name", name); + } + if (StringUtils.isNotBlank(type)) { + wrapper.eq("type", type); + } + //wrapper.eq(MsgTemplateEntity::getTenantCode, UserContext.getRealTenantCode()); + List msgTemplateEntities = mapper.selectListExt(wrapper); + return ConvertUtils.sourceToTarget(msgTemplateEntities, MsgTemplateDTO.class); + } +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/controller/MsgUserController.java b/modules/msg/src/main/java/com/thing/msg/userinfo/controller/MsgUserController.java new file mode 100644 index 0000000..2911423 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/controller/MsgUserController.java @@ -0,0 +1,106 @@ +package com.thing.msg.userinfo.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Map; + + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@RestController +@RequestMapping("msguser/msguser") +@Tag(name="用户信息推送相关绑定信息") +public class MsgUserController { + @Autowired + private MsgUserService msgUserService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + //@RequiresPermissions("bi:msguser:page") + public Result> page( @RequestParam Map params){ + PageData page = msgUserService.getPageData(params,MsgUserDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("bi:msguser:info") + public Result get(@PathVariable("id") Long id){ + MsgUserDTO data = msgUserService.getByIdAs(id,MsgUserDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("bi:msguser:save") + public Result save(@RequestBody MsgUserDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + msgUserService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("bi:msguser:update") + public Result update(@RequestBody MsgUserDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + msgUserService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("bi:msguser:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + msgUserService.removeByIds(Arrays.asList(ids)); + return new Result(); + } + + @GetMapping("getInfoByUserId") + @Operation(summary="通过用户id获取用户配置的推送设置") + public Result getInfoByUserId(@RequestParam Long userId){ + return new Result().ok(msgUserService.getInfoByUserId(userId)); + } + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/dto/MsgUserDTO.java b/modules/msg/src/main/java/com/thing/msg/userinfo/dto/MsgUserDTO.java new file mode 100644 index 0000000..af80d96 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/dto/MsgUserDTO.java @@ -0,0 +1,55 @@ +package com.thing.msg.userinfo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Data +@Schema( name= "用户信息推送相关绑定信息") +public class MsgUserDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "用户id") + private Long userId; + + @Schema(description = "邮箱用户名,用逗号隔开") + private String emailUsername; + + @Schema(description = "微信openId,用逗号隔开") + private String wxOpenId; + + @Schema(description = "钉钉电话,用逗号隔开") + private String dingdingPhone; + + @Schema(description = "企业微信电话,用逗号隔开") + private String wxPhone; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; + + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/dto/SysUserInfoDTO.java b/modules/msg/src/main/java/com/thing/msg/userinfo/dto/SysUserInfoDTO.java new file mode 100644 index 0000000..9d86ef2 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/dto/SysUserInfoDTO.java @@ -0,0 +1,41 @@ +package com.thing.msg.userinfo.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Data +@Schema( name= "用户信息") +public class SysUserInfoDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "姓名") + private String realName; + + @Schema(description = "微信openId") + private String wxOpenId; + + @Schema(description = "钉钉电话") + private String dingdingPhone; + + @Schema(description = "微信手机") + private String wxPhone; + + @Schema(description = "邮箱") + private String emailUsername; +} diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/entity/MsgUserEntity.java b/modules/msg/src/main/java/com/thing/msg/userinfo/entity/MsgUserEntity.java new file mode 100644 index 0000000..479fd00 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/entity/MsgUserEntity.java @@ -0,0 +1,52 @@ +package com.thing.msg.userinfo.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("msg_user") +public class MsgUserEntity extends BaseEntity { + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + /** + * 邮箱用户名,用逗号隔开 + */ + private String emailUsername; + /** + * 微信openId,用逗号隔开 + */ + private String wxOpenId; + /** + * 钉钉电话,用逗号隔开 + */ + private String dingdingPhone; + /** + * 企业微信电话,用逗号隔开 + */ + private String wxPhone; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Date updateDate; +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/excel/MsgUserExcel.java b/modules/msg/src/main/java/com/thing/msg/userinfo/excel/MsgUserExcel.java new file mode 100644 index 0000000..ff8d59e --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/excel/MsgUserExcel.java @@ -0,0 +1,41 @@ +package com.thing.msg.userinfo.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Data +public class MsgUserExcel { + @Excel(name = "id") + private Long id; + @Excel(name = "用户id") + private Long userId; + @Excel(name = "邮箱smtp") + private String emailSmtp; + @Excel(name = "邮箱端口") + private String emailPort; + @Excel(name = "邮箱用户名") + private String emailUsername; + @Excel(name = "邮箱授权码") + private String emailAuthorizationCode; + @Excel(name = "微信openId") + private Long wxOpenId; + @Excel(name = "钉钉电话") + private String dingdingPhone; + @Excel(name = "创建者") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新者") + private Long updater; + @Excel(name = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/mapper/MsgUserMapper.java b/modules/msg/src/main/java/com/thing/msg/userinfo/mapper/MsgUserMapper.java new file mode 100644 index 0000000..9eeb988 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/mapper/MsgUserMapper.java @@ -0,0 +1,15 @@ +package com.thing.msg.userinfo.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.msg.userinfo.entity.MsgUserEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Mapper +public interface MsgUserMapper extends PowerBaseMapper { +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/service/MsgUserService.java b/modules/msg/src/main/java/com/thing/msg/userinfo/service/MsgUserService.java new file mode 100644 index 0000000..5069461 --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/service/MsgUserService.java @@ -0,0 +1,19 @@ +package com.thing.msg.userinfo.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.entity.MsgUserEntity; + +import java.util.List; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +public interface MsgUserService extends IBaseService { + MsgUserDTO getInfoByUserId(Long userId); + List getInfoByUserIds(List userIds); +} \ No newline at end of file diff --git a/modules/msg/src/main/java/com/thing/msg/userinfo/service/impl/MsgUserServiceImpl.java b/modules/msg/src/main/java/com/thing/msg/userinfo/service/impl/MsgUserServiceImpl.java new file mode 100644 index 0000000..b53865f --- /dev/null +++ b/modules/msg/src/main/java/com/thing/msg/userinfo/service/impl/MsgUserServiceImpl.java @@ -0,0 +1,47 @@ +package com.thing.msg.userinfo.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.mapper.MsgUserMapper; +import com.thing.msg.userinfo.entity.MsgUserEntity; +import com.thing.msg.userinfo.service.MsgUserService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 用户信息推送相关绑定信息 + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-06-06 + */ +@Service +public class MsgUserServiceImpl extends BaseServiceImpl implements MsgUserService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + return wrapper; + } + + + @Override + public MsgUserDTO getInfoByUserId(Long userId) { + MsgUserEntity entity = mapper.selectOneByQuery(QueryWrapper.create().eq( MsgUserEntity::getUserId, userId,ObjectUtil.isNotNull(userId))); + return ObjectUtil.isNotNull(entity) ? ConvertUtils.sourceToTarget(entity, MsgUserDTO.class) : null; + } + + @Override + public List getInfoByUserIds(List userIds) { + List entityList = mapper.selectListByQuery(QueryWrapper.create().in( MsgUserEntity::getUserId, userIds,CollectionUtil.isNotEmpty(userIds))); + return CollectionUtil.isNotEmpty(entityList) ? ConvertUtils.sourceToTarget(entityList, MsgUserDTO.class) : null; + } + + +} \ No newline at end of file diff --git a/modules/msg/src/main/resources/mapper/MsgHisMapper.xml b/modules/msg/src/main/resources/mapper/MsgHisMapper.xml new file mode 100644 index 0000000..415fd65 --- /dev/null +++ b/modules/msg/src/main/resources/mapper/MsgHisMapper.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/msg/src/main/resources/mapper/MsgUserMapper.xml b/modules/msg/src/main/resources/mapper/MsgUserMapper.xml new file mode 100644 index 0000000..136421c --- /dev/null +++ b/modules/msg/src/main/resources/mapper/MsgUserMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/pom.xml b/modules/pom.xml new file mode 100644 index 0000000..c5ae496 --- /dev/null +++ b/modules/pom.xml @@ -0,0 +1,36 @@ + + + 4.0.0 + + com.thing + thingbi + 5.1 + + pom + + + dequeue + thing + msg + quartz + configuration + report-analysis + calculation + mock + fix + publicorg + alarm + filter-rule + equipment + carbon-track + carbon-public + + + modules + ThingBI Server modules + + ${basedir}/.. + + diff --git a/modules/publicorg/pom.xml b/modules/publicorg/pom.xml new file mode 100644 index 0000000..7c81187 --- /dev/null +++ b/modules/publicorg/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + com.thing.modules + publicorg + jar + ThingBI Server Modules publicorg + + UTF-8 + + + + com.thing.common + orm + + + com.thing.common + core + + + com.thing.common + queue + + + com.thing.modules + thing + + + com.alibaba + easyexcel + + + + \ No newline at end of file diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/controller/PublicBenchmarkingProjectController.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/controller/PublicBenchmarkingProjectController.java new file mode 100644 index 0000000..f1cf21a --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/controller/PublicBenchmarkingProjectController.java @@ -0,0 +1,125 @@ +package com.thing.publicorg.benchmarkingproject.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.benchmarkingproject.dto.PublicBenchmarkingProjectDTO; +import com.thing.publicorg.benchmarkingproject.excel.PublicBenchmarkingProjectExcel; +import com.thing.publicorg.benchmarkingproject.service.PublicBenchmarkingProjectService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 标杆项目 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@RestController +@RequestMapping("benchmarkingproject") +@Tag(name = "标杆项目") +public class PublicBenchmarkingProjectController { + @Autowired + private PublicBenchmarkingProjectService publicBenchmarkingProjectService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "projectName", description = "项目名称"), + @Parameter(name = "startTime", description = "开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime", description = "结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type", description = "类型 0-区域企业 1-管理端") + }) +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params) { + PageData page = publicBenchmarkingProjectService.pageList(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:info") + public Result get(@PathVariable("id") Long id) { + PublicBenchmarkingProjectDTO data = publicBenchmarkingProjectService.getByIdAs(id, PublicBenchmarkingProjectDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:save") + public Result save(@RequestBody PublicBenchmarkingProjectDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setFileUploadDate(new Date()); + dto.setTenantCode(TenantContext.getTenantCode(SecurityUser.getUser())); + dto.setDeptId(TenantContext.getTenantCode(SecurityUser.getUser())); + publicBenchmarkingProjectService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:update") + public Result update(@RequestBody PublicBenchmarkingProjectDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicBenchmarkingProjectService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicBenchmarkingProjectService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") +// @RequiresPermissions("benchmarkingproject:publicbenchmarkingproject:export") + public void export(@RequestParam Map params, HttpServletResponse response) throws Exception { + List publicBenchmarkingProjectDTOPageData = publicBenchmarkingProjectService.pageLists(params); + List list1 = ConvertUtils.sourceToTarget(publicBenchmarkingProjectDTOPageData, PublicBenchmarkingProjectExcel.class); + ExcelUtils.exportExcel(list1, "标杆项目", "标杆项目", PublicBenchmarkingProjectExcel.class, "标杆项目.xls", response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/PublicBenchmarkingProjectDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/PublicBenchmarkingProjectDTO.java new file mode 100644 index 0000000..bf07cdd --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/PublicBenchmarkingProjectDTO.java @@ -0,0 +1,85 @@ +package com.thing.publicorg.benchmarkingproject.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 标杆项目 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "标杆项目") +public class PublicBenchmarkingProjectDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "项目名称") + private String projectName; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "文件时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date fileDate; + @Schema(description = "文件上传时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date fileUploadDate; + @Schema(description = "备注") + private String remark; + @Schema(description = "地区id") + private Long regionId; + @Schema(description = "地区编号") + private String regionCode; + @Schema(description = "行业代码") + private String industryCode; + @Schema(description = "节能总量") + private BigDecimal energyConservationValue; + + @Schema(description = "节能类型") + private String energyConservationType; + /** + * 节能率 + */ + private String fractionalEnergySaving; + @Schema(description = "类型 0-区域企业 1-管理端") + private Integer type; + + @Schema(description = "地区名称") + private String regionCodeName; + @Schema(description = "行业代码名称") + private String industryTypeName; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialInfoParamDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialInfoParamDTO.java new file mode 100644 index 0000000..01dfe88 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialInfoParamDTO.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.benchmarkingproject.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "tb时序属性映射表") +public class ThingSequentialInfoParamDTO implements Serializable { + + @Serial private static final long serialVersionUID = 5882380723595861216L; + + @Schema(description = "物编码") + private String code; + @Schema(description = "属性名对应值") + private List tsKvParams; + + @Schema(description = "是否计算月数据") + private Boolean calculateMonth; + + @Schema(description = "是否计算年数据") + private Boolean calculateYear; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialParamDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialParamDTO.java new file mode 100644 index 0000000..a8851d5 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/dto/ThingSequentialParamDTO.java @@ -0,0 +1,29 @@ +package com.thing.publicorg.benchmarkingproject.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * 设备在tb中的时序属性 + * + * @author zzx + * @since 2022-03-09 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "") +public class ThingSequentialParamDTO implements Serializable { + + @Schema(description = "时间 yyyy-MM-dd HH:mm:ss") + private String ts; + + @Schema(description = "属性key") + private String key; + @Schema(description = "属性值") + private String val; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/entity/PublicBenchmarkingProjectEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/entity/PublicBenchmarkingProjectEntity.java new file mode 100644 index 0000000..1e1cca6 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/entity/PublicBenchmarkingProjectEntity.java @@ -0,0 +1,130 @@ +package com.thing.publicorg.benchmarkingproject.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 标杆项目 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_benchmarking_project") +public class PublicBenchmarkingProjectEntity implements Serializable { + private static final long serialVersionUID = 1L; + /** + * id + */ + @Id + private Long id; + /** + * 文件名 + */ + private String fileName; + /** + * 文件路径 + */ + private String filePath; + + /** + * 项目名称 + */ + private String projectName; + /** + * 机构名称 + */ + private String orgName; + /** + * 文件时间 + */ + private Date fileDate; + /** + * 文件上传时间 + */ + private Date fileUploadDate; + /** + * 备注 + */ + private String remark; + /** + * 地区id + */ + private Long regionId; + /** + * 地区名称 + */ + private String regionName; + /** + * 地区编号 + */ + private String regionCode; + /** + * 行业代码 + */ + private String industryCode; + /** + * 节能总量 + */ + private BigDecimal energyConservationValue; + + /** + * 节能类型 + */ + private String energyConservationType; + + /** + * 类型 0-区域企业 1-管理端 + */ + private Integer type; + + /** + * 节能率 + */ + private String fractionalEnergySaving; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 部门id + */ + private Long deptId; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Date updateDate; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/excel/PublicBenchmarkingProjectExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/excel/PublicBenchmarkingProjectExcel.java new file mode 100644 index 0000000..5e03994 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/excel/PublicBenchmarkingProjectExcel.java @@ -0,0 +1,67 @@ +package com.thing.publicorg.benchmarkingproject.excel; + + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 标杆项目 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(30) +@HeadRowHeight(30) +@ColumnWidth(35) +public class PublicBenchmarkingProjectExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "文件名", index = 1) + private String fileName; + @ExcelProperty(value = "文件路径", index = 2) + private String filePath; + @ExcelProperty(value = "创建人", index = 3) + private Long creator; + @ExcelProperty(value = "创建时间", index = 4) + private Date createDate; + @ExcelProperty(value = "更新人", index = 5) + private Long updater; + @ExcelProperty(value = "更新时间", index = 6) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 7) + private Long deptId; + @ExcelProperty(value = "租户code", index = 8) + private Long tenantCode; + @ExcelProperty(value = "项目名称", index = 9) + private String projectName; + @ExcelProperty(value = "机构名称", index = 10) + private String orgName; + @ExcelProperty(value = "文件时间", index = 11) + private Date fileDate; + @ExcelProperty(value = "文件上传时间", index = 12) + private Date fileUploadDate; + @ExcelProperty(value = "备注", index = 13) + private String remark; + @ExcelProperty(value = "地区id", index = 14) + private Long regionId; + @ExcelProperty(value = "地区名称", index = 15) + private String regionName; + @ExcelProperty(value = "地区编号", index = 16) + private String regionCode; + @ExcelProperty(value = "行业代码", index = 17) + private String industryCode; + @ExcelProperty(value = "节能总量", index = 18) + private BigDecimal energyConservationValue; +// @ExcelProperty(value = "节能类型", index = 19) +// private String energyConservationType; + @ExcelProperty(value = "类型", index = 19) + private Integer type; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/mapper/PublicBenchmarkingProjectMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/mapper/PublicBenchmarkingProjectMapper.java new file mode 100644 index 0000000..76064ae --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/mapper/PublicBenchmarkingProjectMapper.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.benchmarkingproject.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.benchmarkingproject.dto.PublicBenchmarkingProjectDTO; +import com.thing.publicorg.benchmarkingproject.entity.PublicBenchmarkingProjectEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 标杆项目 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicBenchmarkingProjectMapper extends PowerBaseMapper { + + List getListData(Map params); + + List getList(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/PublicBenchmarkingProjectService.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/PublicBenchmarkingProjectService.java new file mode 100644 index 0000000..6984736 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/PublicBenchmarkingProjectService.java @@ -0,0 +1,25 @@ +package com.thing.publicorg.benchmarkingproject.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.benchmarkingproject.dto.PublicBenchmarkingProjectDTO; +import com.thing.publicorg.benchmarkingproject.entity.PublicBenchmarkingProjectEntity; + +import java.util.List; +import java.util.Map; + +/** + * 标杆项目 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicBenchmarkingProjectService extends IBaseService { + + PageData page(Map params); + + PageData pageList(Map params); + + List pageLists(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/impl/PublicBenchmarkingProjectServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/impl/PublicBenchmarkingProjectServiceImpl.java new file mode 100644 index 0000000..9f05e41 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/benchmarkingproject/service/impl/PublicBenchmarkingProjectServiceImpl.java @@ -0,0 +1,78 @@ +package com.thing.publicorg.benchmarkingproject.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.benchmarkingproject.dto.PublicBenchmarkingProjectDTO; +import com.thing.publicorg.benchmarkingproject.entity.PublicBenchmarkingProjectEntity; +import com.thing.publicorg.benchmarkingproject.mapper.PublicBenchmarkingProjectMapper; +import com.thing.publicorg.benchmarkingproject.service.PublicBenchmarkingProjectService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 标杆项目 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicBenchmarkingProjectServiceImpl extends BaseServiceImpl implements PublicBenchmarkingProjectService { + + @Autowired + private PublicBenchmarkingProjectMapper publicBenchmarkingProjectMapper; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + wrapper.eq("type", 0); + return wrapper; + } + + @Override + public PageData page(Map params) { + PageData pageData = getPageData(params,PublicBenchmarkingProjectDTO.class); + +// UserDetail userDetail = SecurityUser.getUser(); +// params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); +// //如果是admin,并且没有切租户,则查全部 +// if (userDetail.getSuperAdmin()== SuperAdminEnum.YES.value()){ +// if (userDetail.getTenantCode().equals(TenantContext.getTenantCode(SecurityUser.getUser()))){ +// params.put("tenantCode", null); +// } +// } +// if (ObjectUtil.isNotEmpty(params.get("endTime"))){ +// params.put("endTime", TimeUtils.parse(TimeUtils.addDateDays(TimeUtils.parse(params.get("endTime").toString(),TimeUtils.DATE_PATTERN),1),TimeUtils.DATE_PATTERN)); +// } + //List projectDTOS = mapper.getListData(params); + return pageData; + } + + @Override + public PageData pageList(Map params) { + params.put("tenant_code",TenantContext.getTenantCode(SecurityUser.getUser())); + List list = publicBenchmarkingProjectMapper.getListData(params); + List publicFinancialDTOS = startPage(list, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); + + return new PageData<>(publicFinancialDTOS,list.size()); + } + + @Override + public List pageLists(Map params) { + List list = publicBenchmarkingProjectMapper.getList(params); + return list; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/controller/PublicEnergySavingReportController.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/controller/PublicEnergySavingReportController.java new file mode 100644 index 0000000..9e699e7 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/controller/PublicEnergySavingReportController.java @@ -0,0 +1,132 @@ +package com.thing.publicorg.energysavingreport.controller; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.energysavingreport.dto.PublicEnergySavingReportDTO; +import com.thing.publicorg.energysavingreport.dto.PublicOrgEnergyTotalDataDTO; +import com.thing.publicorg.energysavingreport.entity.PublicEnergySavingReportEntity; +import com.thing.publicorg.energysavingreport.excel.PublicEnergySavingReportExcel; +import com.thing.publicorg.energysavingreport.service.PublicEnergySavingReportService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + + +/** +* 节能报告 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("energysavingreport") +@Tag(name="节能报告") +public class PublicEnergySavingReportController { + @Autowired + private PublicEnergySavingReportService publicEnergySavingReportService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "projectName",description ="项目名称"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00") + }) +// @RequiresPermissions("energysavingreport:publicenergysavingreport:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map< String, Object> params){ + PageData page = publicEnergySavingReportService.getPageData(params,PublicEnergySavingReportDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("getOrgEnergyData") + @Operation(summary="获取机构能源数据") + @Parameters({ + @Parameter(name = "orgId",description ="机构id") , + @Parameter(name = "startDate",description ="开始时间,格式2023-08-01") , + @Parameter(name = "endDate",description ="结束时间 格式2023-08-16") + }) +// @RequiresPermissions("energysavingreport:publicenergysavingreport:page") + public Result getOrgEnergyData( @RequestParam Map params){ + PublicOrgEnergyTotalDataDTO page = publicEnergySavingReportService.getOrgEnergyData(params); + + return new Result().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="获取详情") +// @RequiresPermissions("energysavingreport:publicenergysavingreport:info") + public Result get(@PathVariable("id") Long id){ + PublicOrgEnergyTotalDataDTO data = publicEnergySavingReportService.getById(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("energysavingreport:publicenergysavingreport:save") + public Result save(@RequestBody PublicOrgEnergyTotalDataDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + publicEnergySavingReportService.saveData(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("energysavingreport:publicenergysavingreport:update") + public Result update(@RequestBody PublicOrgEnergyTotalDataDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicEnergySavingReportService.updateData(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("energysavingreport:publicenergysavingreport:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicEnergySavingReportService.deleteByIds(ids); + return new Result(); + } + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("energysavingreport:publicenergysavingreport:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicEnergySavingReportService.list(QueryWrapper.create(params)); + ExcelUtils.exportExcel(list,null, "节能报告", PublicEnergySavingReportExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicEnergySavingReportDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicEnergySavingReportDTO.java new file mode 100644 index 0000000..4c965a7 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicEnergySavingReportDTO.java @@ -0,0 +1,113 @@ +package com.thing.publicorg.energysavingreport.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Date; + +/** +* 节能报告 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "节能报告") +public class PublicEnergySavingReportDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "企业名称") + private String orgName; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "评价开始时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date startDate; + @Schema(description = "评价结束时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date endDate; + @Schema(description = "联系人") + private String contactUser; + @Schema(description = "联系人电话") + private String contactUserPhone; + @Schema(description = "总能耗") + private BigDecimal totalEnergyConsumption; + @Schema(description = "建筑能耗") + private BigDecimal buildEnergyConsumption; + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuild; + @Schema(description = "人均建筑能耗") + private BigDecimal unitUserBuild; + @Schema(description = "能源利用效果评价") + private String evaluate; + @Schema(description = "节能改进建议") + private String advise; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + + @Schema(description = "建筑面积") + private BigDecimal buildArea; + @Schema(description = "日均用能人数") + private Integer dayUserCount; + @Schema(description = "类型 0-区域企业 1-管理端") + private Integer type; + + public BigDecimal getTotalEnergyConsumption() { + if (totalEnergyConsumption!=null){ + return totalEnergyConsumption.setScale(2, RoundingMode.HALF_UP); + }else{ + return totalEnergyConsumption; + } + } + + public BigDecimal getBuildEnergyConsumption() { + if (buildEnergyConsumption!=null){ + return buildEnergyConsumption.setScale(2, RoundingMode.HALF_UP); + }else{ + return buildEnergyConsumption; + } + } + + public BigDecimal getUnitBuild() { + if (unitBuild!=null){ + return unitBuild.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuild; + } + } + + public BigDecimal getUnitUserBuild() { + if (unitUserBuild!=null){ + return unitUserBuild.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitUserBuild; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyDataDTO.java new file mode 100644 index 0000000..bd136e8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyDataDTO.java @@ -0,0 +1,71 @@ +package com.thing.publicorg.energysavingreport.dto; + +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** +* 节能报告 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "机构能源数据") +public class PublicOrgEnergyDataDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "企业名称") + private String orgName; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "评价开始时间") + private Date startDate; + @Schema(description = "评价结束时间") + private Date endDate; + @Schema(description = "联系人") + private String contactUser; + @Schema(description = "联系人电话") + private String contactUserPhone; + @Schema(description = "总能耗") + private BigDecimal totalEnergyConsumption; + @Schema(description = "建筑能耗") + private BigDecimal buildEnergyConsumption; + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuild; + @Schema(description = "人均建筑能耗") + private BigDecimal unitUserBulid; + @Schema(description = "能源利用效果评价") + private String evaluate; + @Schema(description = "节能改进建议") + private String advise; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + + @Schema(description = "建筑面积") + private BigDecimal buildArea; + @Schema(description = "日均用能人数") + private Integer dayUserCount; + + @Schema(description = "机构明细数据") + private List details; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyTotalDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyTotalDataDTO.java new file mode 100644 index 0000000..4a58976 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/dto/PublicOrgEnergyTotalDataDTO.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.energysavingreport.dto; + + +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** +* 节能报告 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "机构能源数据总") +public class PublicOrgEnergyTotalDataDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "机构能源数据") + private PublicEnergySavingReportDTO totalData; + @Schema(description = "机构明细数据") + private List details; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/entity/PublicEnergySavingReportEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/entity/PublicEnergySavingReportEntity.java new file mode 100644 index 0000000..4903c09 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/entity/PublicEnergySavingReportEntity.java @@ -0,0 +1,134 @@ +package com.thing.publicorg.energysavingreport.entity; + +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 节能报告 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_energy_saving_report") +public class PublicEnergySavingReportEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + private Long id; + /** + * 企业名称 + */ + private String orgName; + + /** + * 评价开始时间 + */ + private Date startDate; + /** + * 评价结束时间 + */ + private Date endDate; + /** + * 联系人 + */ + private String contactUser; + /** + * 联系人电话 + */ + private String contactUserPhone; + /** + * 总能耗 + */ + private BigDecimal totalEnergyConsumption; + /** + * 建筑能耗 + */ + private BigDecimal buildEnergyConsumption; + /** + * 单位建筑面积能耗 + */ + private BigDecimal unitBuild; + /** + * 人均建筑能耗 + */ + private BigDecimal unitUserBuild; + /** + * 能源利用效果评价 + */ + private String evaluate; + /** + * 节能改进建议 + */ + //@TableField(value = "\"advise\"") + private String advise; + /** + * 文件名 + */ + private String fileName; + /** + * 文件路径 + */ + private String filePath; + + /** + * 建筑面积 + */ + private BigDecimal buildArea; + + /** + * 日均用能人数 + */ + private Integer dayUserCount; + + /** + * 类型 0-区域企业 1-管理端 + */ + private Integer type; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 部门id + */ + private Long deptId; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Long updateDate; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/excel/PublicEnergySavingReportExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/excel/PublicEnergySavingReportExcel.java new file mode 100644 index 0000000..96a6ed8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/excel/PublicEnergySavingReportExcel.java @@ -0,0 +1,65 @@ +package com.thing.publicorg.energysavingreport.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 节能报告 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicEnergySavingReportExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "企业名称", index = 1) + private String orgName; + @ExcelProperty(value = "创建人", index = 2) + private Long creator; + @ExcelProperty(value = "创建时间", index = 3) + private Date createDate; + @ExcelProperty(value = "更新人", index = 4) + private Long updater; + @ExcelProperty(value = "更新时间", index = 5) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 6) + private Long deptId; + @ExcelProperty(value = "租户code", index = 7) + private Long tenantCode; + @ExcelProperty(value = "评价开始时间", index = 8) + private Date startDate; + @ExcelProperty(value = "评价结束时间", index = 9) + private Date endDate; + @ExcelProperty(value = "联系人", index = 10) + private String contactUser; + @ExcelProperty(value = "联系人电话", index = 11) + private String contactUserPhone; + @ExcelProperty(value = "总能耗", index = 12) + private BigDecimal totalEnergyConsumption; + @ExcelProperty(value = "建筑能耗", index = 13) + private BigDecimal buildEnergyConsumption; + @ExcelProperty(value = "单位建筑面积能耗", index = 14) + private BigDecimal unitBuild; + @ExcelProperty(value = "人均建筑能耗", index = 15) + private BigDecimal unitUserBulid; + @ExcelProperty(value = "能源利用效果评价", index = 16) + private String evaluate; + @ExcelProperty(value = "节能改进建议", index = 17) + private String advise; + @ExcelProperty(value = "文件名", index = 18) + private String fileName; + @ExcelProperty(value = "文件路径", index = 19) + private String filePath; + @ExcelProperty(value = "类型 0-区域企业 1-管理端",index = 20) + private Integer type; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/mapper/PublicEnergySavingReportMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/mapper/PublicEnergySavingReportMapper.java new file mode 100644 index 0000000..bafe8f4 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/mapper/PublicEnergySavingReportMapper.java @@ -0,0 +1,25 @@ +package com.thing.publicorg.energysavingreport.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.energysavingreport.dto.PublicEnergySavingReportDTO; +import com.thing.publicorg.energysavingreport.entity.PublicEnergySavingReportEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 节能报告 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicEnergySavingReportMapper extends PowerBaseMapper { + + PublicEnergySavingReportDTO getByDeptId(@Param("deptId") Long deptId); + + List getListData(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/PublicEnergySavingReportService.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/PublicEnergySavingReportService.java new file mode 100644 index 0000000..638b5eb --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/PublicEnergySavingReportService.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.energysavingreport.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.energysavingreport.dto.PublicEnergySavingReportDTO; +import com.thing.publicorg.energysavingreport.dto.PublicOrgEnergyTotalDataDTO; +import com.thing.publicorg.energysavingreport.entity.PublicEnergySavingReportEntity; + +import java.util.Map; + +/** + * 节能报告 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicEnergySavingReportService extends IBaseService { + + PageData page(Map params); + + PublicEnergySavingReportDTO getByDeptId(Long id); + + PublicOrgEnergyTotalDataDTO getOrgEnergyData(Map params); + + PublicOrgEnergyTotalDataDTO getById(Long id); + + void saveData(PublicOrgEnergyTotalDataDTO dto); + + void updateData(PublicOrgEnergyTotalDataDTO dto); + + void deleteByIds(Long[] ids); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/impl/PublicEnergySavingReportServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/impl/PublicEnergySavingReportServiceImpl.java new file mode 100644 index 0000000..e4a107c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreport/service/impl/PublicEnergySavingReportServiceImpl.java @@ -0,0 +1,308 @@ +package com.thing.publicorg.energysavingreport.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.publicorg.energysavingreport.dto.PublicEnergySavingReportDTO; +import com.thing.publicorg.energysavingreport.dto.PublicOrgEnergyTotalDataDTO; +import com.thing.publicorg.energysavingreport.entity.PublicEnergySavingReportEntity; +import com.thing.publicorg.energysavingreport.mapper.PublicEnergySavingReportMapper; +import com.thing.publicorg.energysavingreport.service.PublicEnergySavingReportService; +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import com.thing.publicorg.energysavingreportdetail.entity.PublicEnergySavingReportDetailEntity; +import com.thing.publicorg.energysavingreportdetail.service.PublicEnergySavingReportDetailService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.publicboard.dto.PublicOrgDetailInfoDTO; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 节能报告 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicEnergySavingReportServiceImpl extends BaseServiceImpl + implements PublicEnergySavingReportService { + + + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private SysTenantService sysTenantService; + + @Autowired + private PublicEnergySavingReportDetailService publicEnergySavingReportDetailService; + + @Resource + private ThingManageContextService thingManageContextService; + + @Resource + private TsKvService tsKvService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + wrapper.eq("type", 0); + return wrapper; + } + + @Override + public PageData page(Map params) { + PageData pageData = getPageData(params, PublicEnergySavingReportDTO.class); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + return pageData; + } + + @Override + public PublicEnergySavingReportDTO getByDeptId(Long deptId) { + return mapper.getByDeptId(deptId); + } + + @Override + public PublicOrgEnergyTotalDataDTO getOrgEnergyData(Map params) { + PublicOrgEnergyTotalDataDTO orgEnergyTotalDataDTO = new PublicOrgEnergyTotalDataDTO(); + PublicEnergySavingReportDTO totalData = new PublicEnergySavingReportDTO(); + List details = new ArrayList<>(); + + Long deptId = Long.parseLong(params.get("orgId").toString()); + String startTimeStr = params.get("startDate").toString(); + String endTimeStr = params.get("endDate").toString(); + Long startTime = DateTimeUtils.dateToStamp(startTimeStr + " 00:00:00"); + Long endTime = DateTimeUtils.dateToStamp(endTimeStr + " 01:00:00"); + String year = startTimeStr.substring(0, 4); + + PublicOrgDetailInfoDTO orgDetailInfoDTO = publicOrgService.getByDeptId(deptId, year); + totalData.setDeptId(deptId); + totalData.setTenantCode(orgDetailInfoDTO.getTenantCode()); + + //建筑面积 + BigDecimal areaValue = null; + + if (ObjectUtil.isNotEmpty(orgDetailInfoDTO)) { + //建筑面积 + areaValue = orgDetailInfoDTO.getOrgDetail().getBuildArea(); + //用能人数 + Integer sum = orgDetailInfoDTO.getOrgDetail().getDayUserCount(); + + totalData.setOrgName(orgDetailInfoDTO.getOrgName()); + totalData.setContactUser(orgDetailInfoDTO.getStatLeader()); + totalData.setContactUserPhone(orgDetailInfoDTO.getStatLeaderPhone()); + totalData.setBuildArea(areaValue); + totalData.setDayUserCount(sum); + } + + SysTenantDTO tenantCode1 = sysTenantService.getTenantCode(orgDetailInfoDTO.getTenantCode()); + if (ObjectUtil.isNotEmpty(tenantCode1)) { + + Optional optional = thingManageContextService.findViewByCode(tenantCode1.getThingCode(), UserContext.getRealTenantCode(), true); + if (optional.isPresent()) { + Collection keyList = new ArrayList<>(); + //月 + keyList.add("tce_rundd"); + keyList.add("tce_builddd"); + keyList.add("A29dd"); + keyList.add("tce_A29dd"); + keyList.add("C6dd"); + keyList.add("tce_C6dd"); + keyList.add("B2dd"); + keyList.add("E3dd"); + Map> thingAttrParam = new HashMap<>(); + thingAttrParam.put(optional.get().getEntityCode(), keyList); + + PublicEnergySavingReportDetailDTO publicEnergySavingReportDetailDTO = new PublicEnergySavingReportDetailDTO(); + publicEnergySavingReportDetailDTO.setAttributeName("电"); + publicEnergySavingReportDetailDTO.setAttributeUnit("kWh"); + publicEnergySavingReportDetailDTO.setDeptId(deptId); + publicEnergySavingReportDetailDTO.setTenantCode(orgDetailInfoDTO.getTenantCode()); + publicEnergySavingReportDetailDTO.setAttributeKey("A"); + PublicEnergySavingReportDetailDTO publicEnergySavingReportDetailDTO1 = new PublicEnergySavingReportDetailDTO(); + publicEnergySavingReportDetailDTO1.setAttributeName("天然气"); + publicEnergySavingReportDetailDTO1.setAttributeUnit("Nm³"); + publicEnergySavingReportDetailDTO1.setDeptId(deptId); + publicEnergySavingReportDetailDTO1.setTenantCode(orgDetailInfoDTO.getTenantCode()); + publicEnergySavingReportDetailDTO1.setAttributeKey("C"); + PublicEnergySavingReportDetailDTO publicEnergySavingReportDetailDTO2 = new PublicEnergySavingReportDetailDTO(); + publicEnergySavingReportDetailDTO2.setAttributeName("水"); + publicEnergySavingReportDetailDTO2.setAttributeUnit("t"); + publicEnergySavingReportDetailDTO2.setDeptId(deptId); + publicEnergySavingReportDetailDTO2.setTenantCode(orgDetailInfoDTO.getTenantCode()); + publicEnergySavingReportDetailDTO2.setAttributeKey("B"); + PublicEnergySavingReportDetailDTO publicEnergySavingReportDetailDTO3 = new PublicEnergySavingReportDetailDTO(); + publicEnergySavingReportDetailDTO3.setAttributeName("蒸汽"); + publicEnergySavingReportDetailDTO3.setAttributeUnit("Nm³"); + publicEnergySavingReportDetailDTO3.setDeptId(deptId); + publicEnergySavingReportDetailDTO3.setTenantCode(orgDetailInfoDTO.getTenantCode()); + publicEnergySavingReportDetailDTO3.setAttributeKey("E"); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, startTime, endTime, true); + if (CollectionUtil.isNotEmpty(timeseries)) { + Map SummedMap = timeseries.stream() + .collect(Collectors.groupingBy(TsKvDTO::getAttrKey, + Collectors.mapping(dto -> Double.parseDouble(dto.getVal()), Collectors.reducing(0.0, Double::sum)))); + if (!SummedMap.isEmpty()) { + if (SummedMap.get("tce_rundd") != null) { + //总能耗 + totalData.setTotalEnergyConsumption(new BigDecimal(SummedMap.get("tce_rundd")).setScale(2, BigDecimal.ROUND_HALF_UP)); + } + if (SummedMap.get("tce_builddd") != null) { + //建筑能耗 + totalData.setBuildEnergyConsumption(new BigDecimal(SummedMap.get("tce_builddd")).setScale(2, BigDecimal.ROUND_HALF_UP)); + } + + if (SummedMap.get("A29dd") != null) { + //用电量 + BigDecimal attributeValueA = new BigDecimal(SummedMap.get("A29dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO.setAttributeValue(attributeValueA); + } + if (SummedMap.get("tce_A29dd") != null) { + //折标煤-电 + BigDecimal attributeEnergyConsumptionA = new BigDecimal(SummedMap.get("tce_A29dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO.setAttributeEnergyConsumption(attributeEnergyConsumptionA); + } + + if (SummedMap.get("C6dd") != null) { + //天然气用量 + BigDecimal attributeValueC = new BigDecimal(SummedMap.get("C6dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO1.setAttributeValue(attributeValueC); + } + if (SummedMap.get("tce_C6dd") != null) { + //折标煤-天然气 + BigDecimal attributeEnergyConsumptionC = new BigDecimal(SummedMap.get("tce_C6dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO1.setAttributeEnergyConsumption(attributeEnergyConsumptionC); + } + + if (SummedMap.get("B2dd") != null) { + //水用量 + BigDecimal attributeValueB = new BigDecimal(SummedMap.get("B2dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO1.setAttributeValue(attributeValueB); + } + + if (SummedMap.get("E3dd") != null) { + //蒸汽用量 + BigDecimal attributeValueE = new BigDecimal(SummedMap.get("E3dd")).setScale(2, BigDecimal.ROUND_HALF_UP); + publicEnergySavingReportDetailDTO1.setAttributeValue(attributeValueE); + } + } + } + details.add(publicEnergySavingReportDetailDTO); + details.add(publicEnergySavingReportDetailDTO1); + details.add(publicEnergySavingReportDetailDTO2); + details.add(publicEnergySavingReportDetailDTO3); + } + } + if (ObjectUtil.isNotEmpty(totalData)) { + //建筑面积 + BigDecimal buildArea = totalData.getBuildArea(); + //总能耗 + BigDecimal totalEnergyConsumption = totalData.getTotalEnergyConsumption(); + //用能人数 + Integer dayUserCount = totalData.getDayUserCount(); + //建筑能耗 + BigDecimal buildEnergyConsumption = totalData.getBuildEnergyConsumption(); + + if (buildArea != null && totalEnergyConsumption != null) { + BigDecimal divide = totalEnergyConsumption.divide(buildArea, 2, BigDecimal.ROUND_HALF_UP); + totalData.setUnitBuild(divide); + } + + if (dayUserCount != null && buildEnergyConsumption != null) { + BigDecimal divide = buildEnergyConsumption.divide(new BigDecimal(dayUserCount), 2, BigDecimal.ROUND_HALF_UP); + totalData.setUnitUserBuild(divide); + } + } + orgEnergyTotalDataDTO.setTotalData(totalData); + orgEnergyTotalDataDTO.setDetails(details); + return orgEnergyTotalDataDTO; + } + + @Override + public PublicOrgEnergyTotalDataDTO getById(Long id) { + PublicOrgEnergyTotalDataDTO orgEnergyTotalDataDTO = new PublicOrgEnergyTotalDataDTO(); + PublicEnergySavingReportDTO totalData = getByIdAs(id, PublicEnergySavingReportDTO.class); + List details = publicEnergySavingReportDetailService.getByReportId(id); + orgEnergyTotalDataDTO.setTotalData(totalData); + orgEnergyTotalDataDTO.setDetails(details); + return orgEnergyTotalDataDTO; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveData(PublicOrgEnergyTotalDataDTO dto) { + Long Id = new SnowFlakeIDKeyGenerator().nextId(); + ; + dto.getTotalData().setId(Id); + saveDto(dto.getTotalData()); + for (PublicEnergySavingReportDetailDTO detailDTO : dto.getDetails()) { + detailDTO.setReportId(Id); + } + publicEnergySavingReportDetailService.saveBatch(ConvertUtils.sourceToTarget(dto.getDetails(), PublicEnergySavingReportDetailEntity.class)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateData(PublicOrgEnergyTotalDataDTO dto) { + updateDto(dto.getTotalData()); + publicEnergySavingReportDetailService.deleteByReportId(dto.getTotalData().getId()); + publicEnergySavingReportDetailService.saveBatch(ConvertUtils.sourceToTarget(dto.getDetails(), PublicEnergySavingReportDetailEntity.class)); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByIds(Long[] ids) { + removeByIds(Arrays.asList(ids)); + for (Long id : ids) { + publicEnergySavingReportDetailService.deleteByReportId(id); + } + } + + private String getAttribute(String key) { + String attributeUnit = null; + if ("A".equals(key)) { + attributeUnit = "kWh"; + } + if ("B".equals(key)) { + attributeUnit = "t"; + } + if ("C".equals(key)) { + attributeUnit = "Nm³"; + } + if ("E".equals(key)) { + attributeUnit = "Nm³"; + } + if ("O".equals(key)) { + attributeUnit = "t"; + } + if ("U".equals(key)) { + attributeUnit = "t"; + } + if ("N".equals(key)) { + attributeUnit = "t"; + } + + + return attributeUnit; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/controller/PublicEnergySavingReportDetailController.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/controller/PublicEnergySavingReportDetailController.java new file mode 100644 index 0000000..3bba81b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/controller/PublicEnergySavingReportDetailController.java @@ -0,0 +1,119 @@ +package com.thing.publicorg.energysavingreportdetail.controller; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import com.thing.publicorg.energysavingreportdetail.entity.PublicEnergySavingReportDetailEntity; +import com.thing.publicorg.energysavingreportdetail.excel.PublicEnergySavingReportDetailExcel; +import com.thing.publicorg.energysavingreportdetail.service.PublicEnergySavingReportDetailService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** +* 能源节能报告明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("energysavingreportdetail") +@Tag(name="能源节能报告明细") +public class PublicEnergySavingReportDetailController { + @Autowired + private PublicEnergySavingReportDetailService publicEnergySavingReportDetailService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:page") + public Result> page( @RequestParam Map params){ + PageData page = publicEnergySavingReportDetailService.getPageData(params,PublicEnergySavingReportDetailDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:info") + public Result get(@PathVariable("id") Long id){ + PublicEnergySavingReportDetailDTO data = publicEnergySavingReportDetailService.getByIdAs(id,PublicEnergySavingReportDetailDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:save") + public Result save(@RequestBody PublicEnergySavingReportDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + publicEnergySavingReportDetailService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:update") + public Result update(@RequestBody PublicEnergySavingReportDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicEnergySavingReportDetailService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicEnergySavingReportDetailService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("energysavingreportdetail:publicenergysavingreportdetail:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicEnergySavingReportDetailService.list(QueryWrapper.create(params)); + + ExcelUtils.exportExcel(list,null, "能源节能报告明细", PublicEnergySavingReportDetailExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/dto/PublicEnergySavingReportDetailDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/dto/PublicEnergySavingReportDetailDTO.java new file mode 100644 index 0000000..5883de4 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/dto/PublicEnergySavingReportDetailDTO.java @@ -0,0 +1,49 @@ +package com.thing.publicorg.energysavingreportdetail.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 能源节能报告明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "能源节能报告明细") +public class PublicEnergySavingReportDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "节能报告id") + private Long reportId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "属性key") + private String attributeKey; + @Schema(description = "属性名称") + private String attributeName; + @Schema(description = "属性单位") + private String attributeUnit; + @Schema(description = "消耗量") + private BigDecimal attributeValue; + @Schema(description = "能耗量") + private BigDecimal attributeEnergyConsumption; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/entity/PublicEnergySavingReportDetailEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/entity/PublicEnergySavingReportDetailEntity.java new file mode 100644 index 0000000..5eb95a2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/entity/PublicEnergySavingReportDetailEntity.java @@ -0,0 +1,50 @@ +package com.thing.publicorg.energysavingreportdetail.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 能源节能报告明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_energy_saving_report_detail") +public class PublicEnergySavingReportDetailEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + + /** + * 节能报告id + */ + private Long reportId; + /** + * 属性key + */ + private String attributeKey; + /** + * 属性名称 + */ + private String attributeName; + /** + * 属性单位 + */ + private String attributeUnit; + /** + * 消耗量 + */ + private BigDecimal attributeValue; + /** + * 能耗量 + */ + private BigDecimal attributeEnergyConsumption; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/excel/PublicEnergySavingReportDetailExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/excel/PublicEnergySavingReportDetailExcel.java new file mode 100644 index 0000000..be8099e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/excel/PublicEnergySavingReportDetailExcel.java @@ -0,0 +1,49 @@ +package com.thing.publicorg.energysavingreportdetail.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 能源节能报告明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicEnergySavingReportDetailExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "节能报告id", index = 1) + private Long reportId; + @ExcelProperty(value = "创建人", index = 2) + private Long creator; + @ExcelProperty(value = "创建时间", index = 3) + private Date createDate; + @ExcelProperty(value = "更新人", index = 4) + private Long updater; + @ExcelProperty(value = "更新时间", index = 5) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 6) + private Long deptId; + @ExcelProperty(value = "租户code", index = 7) + private Long tenantCode; + @ExcelProperty(value = "属性key", index = 8) + private String attributeKey; + @ExcelProperty(value = "属性名称", index = 9) + private String attributeName; + @ExcelProperty(value = "属性单位", index = 10) + private String attributeUnit; + @ExcelProperty(value = "消耗量", index = 11) + private BigDecimal attributeValue; + @ExcelProperty(value = "能耗量", index = 12) + private BigDecimal attributeEnergyConsumption; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/mapper/PublicEnergySavingReportDetailMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/mapper/PublicEnergySavingReportDetailMapper.java new file mode 100644 index 0000000..3612a06 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/mapper/PublicEnergySavingReportDetailMapper.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.energysavingreportdetail.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import com.thing.publicorg.energysavingreportdetail.entity.PublicEnergySavingReportDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 能源节能报告明细 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicEnergySavingReportDetailMapper extends PowerBaseMapper { + + List getByReportId(@Param("reportId") Long reportId); + + void deleteByReportId(@Param("reportId")Long reportId); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/PublicEnergySavingReportDetailService.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/PublicEnergySavingReportDetailService.java new file mode 100644 index 0000000..587425f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/PublicEnergySavingReportDetailService.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.energysavingreportdetail.service; + + + +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import com.thing.publicorg.energysavingreportdetail.entity.PublicEnergySavingReportDetailEntity; + +import java.util.List; + + +/** + * 能源节能报告明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicEnergySavingReportDetailService extends IBaseService { + + List getByReportId(Long id); + + void deleteByReportId(Long id); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/impl/PublicEnergySavingReportDetailServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/impl/PublicEnergySavingReportDetailServiceImpl.java new file mode 100644 index 0000000..497cb0a --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/energysavingreportdetail/service/impl/PublicEnergySavingReportDetailServiceImpl.java @@ -0,0 +1,43 @@ +package com.thing.publicorg.energysavingreportdetail.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.energysavingreportdetail.dto.PublicEnergySavingReportDetailDTO; +import com.thing.publicorg.energysavingreportdetail.entity.PublicEnergySavingReportDetailEntity; +import com.thing.publicorg.energysavingreportdetail.mapper.PublicEnergySavingReportDetailMapper; +import com.thing.publicorg.energysavingreportdetail.service.PublicEnergySavingReportDetailService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 能源节能报告明细 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicEnergySavingReportDetailServiceImpl extends + BaseServiceImpl implements PublicEnergySavingReportDetailService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + + @Override + public List getByReportId(Long reportId) { + return mapper.getByReportId(reportId); + } + + @Override + public void deleteByReportId(Long reportId) { + mapper.deleteByReportId(reportId); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/controller/PublicFinancialController.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/controller/PublicFinancialController.java new file mode 100644 index 0000000..50d267e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/controller/PublicFinancialController.java @@ -0,0 +1,108 @@ +package com.thing.publicorg.financial.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + + +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.publicorg.financial.service.PublicFinancialService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 金融服务 +* +* @author system system@lrd.com +* @since 5.1 2024-01-26 +*/ +@RestController +@RequestMapping("v2/publicorg/publicfinancial") +@Tag(name="金融服务") +@RequiredArgsConstructor +public class PublicFinancialController { + + private final PublicFinancialService publicFinancialService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="企业名称"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type",description ="类型 0-区域企业 1-管理端"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = publicFinancialService.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + PublicFinancialDTO data = publicFinancialService.getByIdAs(id, PublicFinancialDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody PublicFinancialDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + publicFinancialService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody PublicFinancialDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + publicFinancialService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicFinancialService.batchDelete(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = publicFinancialService.listAs(params, PublicFinancialDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "金融服务", list, PublicFinancialExcel.class); + *} + */ + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/dto/PublicFinancialDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/dto/PublicFinancialDTO.java new file mode 100644 index 0000000..59b66b7 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/dto/PublicFinancialDTO.java @@ -0,0 +1,51 @@ +package com.thing.publicorg.financial.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 金融服务 +* +* @author system system@lrd.com +* @since 5.1 2024-01-26 +*/ +@Data +@Schema(description = "金融服务") +public class PublicFinancialDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "租户编码") + private String name; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "修改人") + private Long updater; + @Schema(description = "地址") + private String address; + @Schema(description = "联系方式") + private String phone; + @Schema(description = "描述") + private String description; + @Schema(description = "类型 0-区域企业 1-管理端") + private Integer type; + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "修改时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/entity/PublicFinancialEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/entity/PublicFinancialEntity.java new file mode 100644 index 0000000..03fcf32 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/entity/PublicFinancialEntity.java @@ -0,0 +1,67 @@ +package com.thing.publicorg.financial.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 金融服务 + * + * @author system system@lrd.com + * @since 5.1 2024-01-26 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("public_financial") +public class PublicFinancialEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + *主键 + */ + @Id + private Long id; + /** + * 租户编码 + */ + private String name; + /** + * 创建人 + */ + private Long creator; + /** + * 修改人 + */ + private Long updater; + /** + * 地址 + */ + private String address; + /** + * 联系方式 + */ + private String phone; + /** + * 描述 + */ + private String description; + /** + * 类型 0-区域企业 1-管理端 + */ + private Integer type; + /** + * 创建时间 + */ + private Date createDate; + /** + * 修改时间 + */ + private Date updateDate; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/mapper/PublicFinancialMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/mapper/PublicFinancialMapper.java new file mode 100644 index 0000000..50af6b1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/mapper/PublicFinancialMapper.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.financial.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.publicorg.financial.entity.PublicFinancialEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 金融服务 +* +* @author system system@lrd.com +* @since 5.1 2024-01-26 +*/ +@Mapper +public interface PublicFinancialMapper extends PowerBaseMapper { + + + List pageList(Map params); + + List pageList1(); + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/PublicFinancialService.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/PublicFinancialService.java new file mode 100644 index 0000000..288f910 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/PublicFinancialService.java @@ -0,0 +1,20 @@ +package com.thing.publicorg.financial.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.publicorg.financial.entity.PublicFinancialEntity; + +import java.util.Map; + +/** + * 金融服务 + * + * @author system system@lrd.com + * @since 5.1 2024-01-26 + */ +public interface PublicFinancialService extends IBaseService { + + PageData pageList(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/impl/PublicFinancialServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/impl/PublicFinancialServiceImpl.java new file mode 100644 index 0000000..90719ec --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/financial/service/impl/PublicFinancialServiceImpl.java @@ -0,0 +1,43 @@ +package com.thing.publicorg.financial.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.publicorg.financial.entity.PublicFinancialEntity; +import com.thing.publicorg.financial.mapper.PublicFinancialMapper; +import com.thing.publicorg.financial.service.PublicFinancialService; +import com.thing.publicorg.publicboard.dto.PublicEnergyWarningDataDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 金融服务 + * + * @author system system@lrd.com + * @since 5.1 2024-01-26 + */ +@Service +public class PublicFinancialServiceImpl extends BaseServiceImpl implements PublicFinancialService { + + @Autowired + private PublicFinancialMapper publicFinancialMapper; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + return wrapper; + } + + + @Override + public PageData pageList(Map params) { + List list = publicFinancialMapper.pageList(params); + List publicFinancialDTOS = startPage(list, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); + return new PageData<>(publicFinancialDTOS,list.size()); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/controller/PublicIndexStandardController.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/controller/PublicIndexStandardController.java new file mode 100644 index 0000000..f2b5dd1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/controller/PublicIndexStandardController.java @@ -0,0 +1,98 @@ +package com.thing.publicorg.indexstandard.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.indexstandard.dto.PublicIndexStandardDTO; +import com.thing.publicorg.indexstandard.service.PublicIndexStandardService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Map; + + +/** +* 指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicindexstandard") +@Tag(name="指标阈值") +public class PublicIndexStandardController { + @Autowired + private PublicIndexStandardService publicIndexStandardService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "quotaType",description ="定额类型") + }) +// @RequiresPermissions("indexstandard:publicindexstandard:page") + public Result> page( @RequestParam Map params){ + PageData page = publicIndexStandardService.getPageData(params,PublicIndexStandardDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("indexstandard:publicindexstandard:info") + public Result get(@PathVariable("id") Long id){ + PublicIndexStandardDTO data = publicIndexStandardService.getByIdAs(id,PublicIndexStandardDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("indexstandard:publicindexstandard:save") + public Result save(@RequestBody PublicIndexStandardDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + publicIndexStandardService.saveDto(dto); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("indexstandard:publicindexstandard:update") + public Result update(@RequestBody PublicIndexStandardDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicIndexStandardService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("indexstandard:publicindexstandard:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicIndexStandardService.removeByIds(Arrays.asList(ids)); + return new Result(); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/dto/PublicIndexStandardDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/dto/PublicIndexStandardDTO.java new file mode 100644 index 0000000..16d29a6 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/dto/PublicIndexStandardDTO.java @@ -0,0 +1,53 @@ +package com.thing.publicorg.indexstandard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "指标阈值") +public class PublicIndexStandardDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "租户Code") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "机构类别归属") + private String orgType; + @Schema(description = "指标名称") + private String indexName; + @Schema(description = "指标限制形式") + private String indexType; + @Schema(description = "指标限制形式值") + private String indexTypeValue; + @Schema(description = "单位") + private String unit; + @Schema(description = "年度约束值") + private BigDecimal yearValue; + @Schema(description = "月度约束值") + private BigDecimal monthValue; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/entity/PublicIndexStandardEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/entity/PublicIndexStandardEntity.java new file mode 100644 index 0000000..ac12831 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/entity/PublicIndexStandardEntity.java @@ -0,0 +1,58 @@ +package com.thing.publicorg.indexstandard.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_index_standard") +public class PublicIndexStandardEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 定额类型 + */ + private String quotaType; + /** + * 机构类别归属 + */ + private String orgType; + /** + * 指标名称 + */ + private String indexName; + /** + * 指标限制形式 + */ + private String indexType; + /** + * 指标限制形式值 + */ + private String indexTypeValue; + /** + * 单位 + */ + private String unit; + /** + * 年度约束值 + */ + private BigDecimal yearValue; + /** + * 月度约束值 + */ + private BigDecimal monthValue; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/excel/PublicIndexStandardExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/excel/PublicIndexStandardExcel.java new file mode 100644 index 0000000..dfc0bcb --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/excel/PublicIndexStandardExcel.java @@ -0,0 +1,53 @@ +package com.thing.publicorg.indexstandard.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicIndexStandardExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "企业id", index = 1) + private Long deptId; + @ExcelProperty(value = "租户Code", index = 2) + private Long tenantCode; + @ExcelProperty(value = "创建者", index = 3) + private Long creator; + @ExcelProperty(value = "创建时间", index = 4) + private Date createDate; + @ExcelProperty(value = "更新者", index = 5) + private Long updater; + @ExcelProperty(value = "更新时间", index = 6) + private Date updateDate; + @ExcelProperty(value = "定额类型", index = 7) + private String quotaType; + @ExcelProperty(value = "机构类别归属", index = 8) + private String orgType; + @ExcelProperty(value = "指标名称", index = 9) + private String indexName; + @ExcelProperty(value = "指标限制形式", index = 10) + private String indexType; + @ExcelProperty(value = "指标限制形式值", index = 11) + private String indexTypeValue; + @ExcelProperty(value = "单位", index = 12) + private String unit; + @ExcelProperty(value = "年度约束值", index = 13) + private BigDecimal yearValue; + @ExcelProperty(value = "月度约束值", index = 14) + private BigDecimal monthValue; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/mapper/PublicIndexStandardMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/mapper/PublicIndexStandardMapper.java new file mode 100644 index 0000000..48f6ef9 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/mapper/PublicIndexStandardMapper.java @@ -0,0 +1,25 @@ +package com.thing.publicorg.indexstandard.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.indexstandard.dto.PublicIndexStandardDTO; +import com.thing.publicorg.indexstandard.entity.PublicIndexStandardEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicIndexStandardMapper extends PowerBaseMapper { + + List getListData(Map params); + + List getByOrgInfo(@Param("quotaType") String quotaType, @Param("orgType") String orgType); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/PublicIndexStandardService.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/PublicIndexStandardService.java new file mode 100644 index 0000000..ebcc3a5 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/PublicIndexStandardService.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.indexstandard.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.indexstandard.dto.PublicIndexStandardDTO; +import com.thing.publicorg.indexstandard.entity.PublicIndexStandardEntity; + +import java.util.List; +import java.util.Map; + +/** + * 指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicIndexStandardService extends IBaseService { + + PageData page(Map params); + + List getByOrgInfo(String quotaType, String orgType); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/impl/PublicIndexStandardServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/impl/PublicIndexStandardServiceImpl.java new file mode 100644 index 0000000..0aa2886 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/indexstandard/service/impl/PublicIndexStandardServiceImpl.java @@ -0,0 +1,54 @@ +package com.thing.publicorg.indexstandard.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.indexstandard.mapper.PublicIndexStandardMapper; +import com.thing.publicorg.indexstandard.dto.PublicIndexStandardDTO; +import com.thing.publicorg.indexstandard.entity.PublicIndexStandardEntity; +import com.thing.publicorg.indexstandard.service.PublicIndexStandardService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicIndexStandardServiceImpl extends BaseServiceImpl implements PublicIndexStandardService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + return wrapper; + } + + @Override + public PageData page(Map params) { + PageData pageData = getPageData(params, PublicIndexStandardDTO.class); + // IPage page = getPage(params, "create_date", false); + // UserDetail userDetail = SecurityUser.getUser(); +// params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); +// //如果是admin,并且没有切租户,则查全部 +// if (userDetail.getSuperAdmin()== SuperAdminEnum.YES.value()){ +// if (userDetail.getTenantCode().equals(TenantContext.getTenantCode(SecurityUser.getUser()))){ +// params.put("tenantCode", null); +// } +// } + //List indexStandardDTOS = mapper.getListData(params); + return pageData; + } + + @Override + public List getByOrgInfo(String quotaType, String orgType) { + return mapper.getByOrgInfo(quotaType,orgType); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/controller/PublicNoticeController.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/controller/PublicNoticeController.java new file mode 100644 index 0000000..6325aed --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/controller/PublicNoticeController.java @@ -0,0 +1,202 @@ +package com.thing.publicorg.notice.controller; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; + +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.notice.dto.PublicNoticeDTO; +import com.thing.publicorg.notice.dto.PublicNoticeIsViewDTO; +import com.thing.publicorg.notice.entity.PublicNoticeEntity; +import com.thing.publicorg.notice.excel.PublicNoticeExcel; +import com.thing.publicorg.notice.service.PublicNoticeService; +import com.thing.publicorg.noticeviewrecord.entity.PublicNoticeViewRecordEntity; +import com.thing.publicorg.noticeviewrecord.service.PublicNoticeViewRecordService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.Map; + + +/** +* 通知 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicnotice") +@Tag(name="通知") +public class PublicNoticeController { + @Autowired + private PublicNoticeService publicNoticeService; + @Autowired + private PublicNoticeViewRecordService publicNoticeViewRecordService; + + @GetMapping("page") + @Operation(summary="分页(通知维护-管理员用)") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "title",description ="标题"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type",description ="类型 0-区域企业 1-管理端"), + }) +// @RequiresPermissions("notice:publicnotice:page") + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = publicNoticeService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("pageList") + @Operation(summary="分页(租户通知列表-租户用)") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "title",description ="标题"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type",description ="类型 0-区域企业 1-管理端"), + }) +// @RequiresPermissions("notice:publicnotice:page") + public Result> pageList(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = publicNoticeService.pageList(params); + + return new Result>().ok(page); + } + + @PostMapping("view/{id}") + @Operation(summary="点击查看通知-租户用") + @LogOperation("点击查看通知-租户用") +// @RequiresPermissions("notice:publicnotice:save") + public Result view(@PathVariable("id") Long id){ + //效验数据 + PublicNoticeViewRecordEntity data = new PublicNoticeViewRecordEntity(); + data.setNoticeId(id); + publicNoticeViewRecordService.save(data); + return new Result(); + } + + @PostMapping("unView/count") + @Operation(summary="未查看通知数量-租户用") + @LogOperation("未查看通知数量-租户用") +// @RequiresPermissions("notice:publicnotice:save") + public Result view(){ + Integer count = publicNoticeService.getUnViewCount(); + + return new Result().ok(count); + } + + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("notice:publicnotice:info") + public Result get(@PathVariable("id") Long id){ + PublicNoticeDTO data = publicNoticeService.getByIdAs(id,PublicNoticeDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("notice:publicnotice:save") + public Result save(@RequestBody PublicNoticeDTO dto){ + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + dto.setTenantCode(tenantCode); + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + if (dto.getPubStatus().equals("1")){ + dto.setPubDate(new Date()); + } + publicNoticeService.saveDto(dto); + return new Result(); + } + + @PostMapping("pub/{id}") + @Operation(summary="发布") + @LogOperation("发布") +// @RequiresPermissions("notice:publicnotice:save") + public Result pub(@PathVariable("id") Long id){ + //效验数据 + PublicNoticeDTO data = publicNoticeService.getByIdAs(id,PublicNoticeDTO.class); + data.setPubStatus("1"); + data.setPubDate(new Date()); + PublicNoticeEntity publicNoticeEntity = new PublicNoticeEntity(); + BeanUtils.copyProperties(data, publicNoticeEntity); + int i = publicNoticeService.updateDto(publicNoticeEntity); + + return new Result(); + } + + @PostMapping("updateStatus/{id}/{status}") + @Operation(summary="修改状态") + @LogOperation("修改状态") +// @RequiresPermissions("notice:publicnotice:save") + public Result updateStatus(@PathVariable("id") Long id,@PathVariable("status") String status){ + //效验数据 + PublicNoticeDTO data = publicNoticeService.getByIdAs(id,PublicNoticeDTO.class); + data.setFileStatus(status); + publicNoticeService.updateDto(data); + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("notice:publicnotice:update") + public Result update(@RequestBody PublicNoticeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + publicNoticeService.updateDto(dto); + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("notice:publicnotice:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicNoticeService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("notice:publicnotice:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicNoticeService.list(QueryWrapper.create(params)); + ExcelUtils.exportExcel(list,null, "通知", PublicNoticeExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeDTO.java new file mode 100644 index 0000000..42b3e03 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeDTO.java @@ -0,0 +1,65 @@ +package com.thing.publicorg.notice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 通知 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "通知") +public class PublicNoticeDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "通知标题") + private String title; + @Schema(description = "发布机构") + private String orgName; + @Schema(description = "文件编号") + private String fileNo; + @Schema(description = "备注") + private String remark; + @Schema(description = "通知状态 0无效,1有效") + private String fileStatus; + @Schema(description = "发布状态 0未发布 1已发布") + private String pubStatus; + @Schema(description = "发布时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date pubDate; + @Schema(description = "类型") + private Integer type; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeIsViewDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeIsViewDTO.java new file mode 100644 index 0000000..b7c0173 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/dto/PublicNoticeIsViewDTO.java @@ -0,0 +1,66 @@ +package com.thing.publicorg.notice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 通知 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "通知列表") +public class PublicNoticeIsViewDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "通知标题") + private String title; + @Schema(description = "发布机构") + private String orgName; + @Schema(description = "文件编号") + private String fileNo; + @Schema(description = "备注") + private String remark; + @Schema(description = "通知状态 0无效,1有效") + private String fileStatus; + @Schema(description = "发布状态 0未发布 1已发布") + private String pubStatus; + @Schema(description = "发布时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date pubDate; + + @Schema(description = "是否查看 0未查看,1已查看") + private String viewStatus; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/entity/PublicNoticeEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/entity/PublicNoticeEntity.java new file mode 100644 index 0000000..8de6a7a --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/entity/PublicNoticeEntity.java @@ -0,0 +1,96 @@ +package com.thing.publicorg.notice.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 通知 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_notice") +public class PublicNoticeEntity { + private static final long serialVersionUID = 1L; + + /** + * 文件名 + */ + private String fileName; + /** + * 文件路径 + */ + private String filePath; + /** + * 通知标题 + */ + private String title; + /** + * 发布机构 + */ + private String orgName; + /** + * 文件编号 + */ + private String fileNo; + /** + * 备注 + */ + private String remark; + /** + * 通知状态 0无效,1有效 + */ + private String fileStatus; + /** + * 发布状态 0未发布 1已发布 + */ + private String pubStatus; + /** + * 发布时间 + */ + private Date pubDate; + /** + *类型 + */ + private Integer type; + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Date updateDate; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/excel/PublicNoticeExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/excel/PublicNoticeExcel.java new file mode 100644 index 0000000..d804c04 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/excel/PublicNoticeExcel.java @@ -0,0 +1,57 @@ +package com.thing.publicorg.notice.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.Date; + +/** + * 通知 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicNoticeExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "文件名", index = 1) + private String fileName; + @ExcelProperty(value = "文件路径", index = 2) + private String filePath; + @ExcelProperty(value = "创建人", index = 3) + private Long creator; + @ExcelProperty(value = "创建时间", index = 4) + private Date createDate; + @ExcelProperty(value = "更新人", index = 5) + private Long updater; + @ExcelProperty(value = "更新时间", index = 6) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 7) + private Long deptId; + @ExcelProperty(value = "租户code", index = 8) + private Long tenantCode; + @ExcelProperty(value = "通知标题", index = 9) + private String title; + @ExcelProperty(value = "发布机构", index = 10) + private String orgName; + @ExcelProperty(value = "文件编号", index = 11) + private String fileNo; + @ExcelProperty(value = "备注", index = 12) + private String remark; + @ExcelProperty(value = "通知状态 0吴效,1有效", index = 13) + private String fileStatus; + @ExcelProperty(value = "发布状态 0未发布 1已发布", index = 14) + private String pubStatus; + @ExcelProperty(value = "发布时间", index = 15) + private Date pubDate; + @ExcelProperty(value = "类型", index = 16) + private Integer type; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/mapper/PublicNoticeMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/mapper/PublicNoticeMapper.java new file mode 100644 index 0000000..ea0441e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/mapper/PublicNoticeMapper.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.notice.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.notice.dto.PublicNoticeDTO; +import com.thing.publicorg.notice.dto.PublicNoticeIsViewDTO; +import com.thing.publicorg.notice.entity.PublicNoticeEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 通知 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicNoticeMapper extends PowerBaseMapper { + + List getListData(Map params); + + Integer getUnViewCount(Long tenantCode); + + List getLists(Map params); + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/PublicNoticeService.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/PublicNoticeService.java new file mode 100644 index 0000000..1a64942 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/PublicNoticeService.java @@ -0,0 +1,25 @@ +package com.thing.publicorg.notice.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.notice.dto.PublicNoticeDTO; +import com.thing.publicorg.notice.dto.PublicNoticeIsViewDTO; +import com.thing.publicorg.notice.entity.PublicNoticeEntity; + +import java.util.Map; + +/** + * 通知 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicNoticeService extends IBaseService { + + PageData page(Map params); + + PageData pageList(Map params); + + Integer getUnViewCount(); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/impl/PublicNoticeServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/impl/PublicNoticeServiceImpl.java new file mode 100644 index 0000000..5dbcb63 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/notice/service/impl/PublicNoticeServiceImpl.java @@ -0,0 +1,104 @@ +package com.thing.publicorg.notice.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.notice.dto.PublicNoticeDTO; +import com.thing.publicorg.notice.dto.PublicNoticeIsViewDTO; +import com.thing.publicorg.notice.entity.PublicNoticeEntity; +import com.thing.publicorg.notice.mapper.PublicNoticeMapper; +import com.thing.publicorg.notice.service.PublicNoticeService; +import com.thing.publicorg.noticeviewrecord.dto.PublicNoticeViewRecordDTO; +import com.thing.publicorg.noticeviewrecord.service.PublicNoticeViewRecordService; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 通知 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicNoticeServiceImpl extends BaseServiceImpl implements PublicNoticeService { + + @Autowired + private PublicNoticeViewRecordService publicNoticeViewRecordService; + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + @Override + public PageData page(Map params) { + // IPage page = getPage(params, "create_date", false); + +// UserDetail userDetail = SecurityUser.getUser(); +// params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); +// //如果是admin,并且没有切租户,则查全部 +// if (userDetail.getSuperAdmin()== SuperAdminEnum.YES.value()){ +// if (userDetail.getTenantCode().equals(TenantContext.getTenantCode(SecurityUser.getUser()))){ +// params.put("tenantCode", null); +// } +// }tenantCode -> {Long@20156} 1749690716702932992 + if (ObjectUtil.isNotEmpty(params.get("endTime"))){ + params.put("endTime", DateTimeUtils.addDateDays(DateTimeUtils.parse(params.get("endTime").toString(),DateTimeUtils.DATE_PATTERN_STR),1)); + } + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List noticeDTOS = mapper.getLists(params); + List publicNoticeIsViewDTOS = startPage(noticeDTOS, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); +// PageData pageData = getPageData(params, PublicNoticeDTO.class); + return new PageData<>(publicNoticeIsViewDTOS,noticeDTOS.size()); + } + + @Override + public PageData pageList(Map params) { + + // UserDetail userDetail = SecurityUser.getUser(); + + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + //如果是admin +// if (userDetail.getSuperAdmin()== SuperAdminEnum.YES.value() && tenantCode.equals(userDetail.getTenantCode())){ +// return null; +// } +// params.put("tenantCode", tenantCode); + params.put("status","1"); + params.put("pubStatus","1"); + if (ObjectUtil.isNotEmpty(params.get("endTime"))){ + params.put("endTime", DateTimeUtils.addDateDays(DateTimeUtils.parse(params.get("endTime").toString(),DateTimeUtils.DATE_PATTERN_STR),1)); + } + List noticeDTOS = mapper.getListData(params); +// List result = new ArrayList<>(); +// for (PublicNoticeDTO noticeDTO:noticeDTOS){ +// +// List recordDTO = publicNoticeViewRecordService.getByNoticeIdAndTenantCode(noticeDTO.getId(),tenantCode); +// if (CollectionUtil.isNotEmpty(recordDTO)){ +// noticeIsViewDTO.setViewStatus("1"); +// }else{ +// noticeIsViewDTO.setViewStatus("0"); +// } +// result.add(noticeIsViewDTO); +// } + List publicNoticeIsViewDTOS = startPage(noticeDTOS, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); + return new PageData<>(publicNoticeIsViewDTOS,noticeDTOS.size()); + } + + @Override + public Integer getUnViewCount() { + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + return mapper.getUnViewCount(tenantCode); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/controller/PublicNoticeViewRecordController.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/controller/PublicNoticeViewRecordController.java new file mode 100644 index 0000000..3e846b2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/controller/PublicNoticeViewRecordController.java @@ -0,0 +1,119 @@ +package com.thing.publicorg.noticeviewrecord.controller; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.noticeviewrecord.dto.PublicNoticeViewRecordDTO; +import com.thing.publicorg.noticeviewrecord.entity.PublicNoticeViewRecordEntity; +import com.thing.publicorg.noticeviewrecord.excel.PublicNoticeViewRecordExcel; +import com.thing.publicorg.noticeviewrecord.service.PublicNoticeViewRecordService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + + +/** +* 通知查看记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicnoticeviewrecord") +@Tag(name="通知查看记录") +public class PublicNoticeViewRecordController { + @Autowired + private PublicNoticeViewRecordService publicNoticeViewRecordService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:page") + public Result> page( @RequestParam Map params){ + PageData page = publicNoticeViewRecordService.getPageData(params,PublicNoticeViewRecordDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:info") + public Result get(@PathVariable("id") Long id){ + PublicNoticeViewRecordDTO data = publicNoticeViewRecordService.getByIdAs(id,PublicNoticeViewRecordDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:save") + public Result save(@RequestBody PublicNoticeViewRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + publicNoticeViewRecordService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:update") + public Result update(@RequestBody PublicNoticeViewRecordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicNoticeViewRecordService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicNoticeViewRecordService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("noticeviewrecord:publicnoticeviewrecord:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicNoticeViewRecordService.list(QueryWrapper.create(params)); + + ExcelUtils.exportExcel(list,null, "通知查看记录", PublicNoticeViewRecordExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/dto/PublicNoticeViewRecordDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/dto/PublicNoticeViewRecordDTO.java new file mode 100644 index 0000000..cd1369c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/dto/PublicNoticeViewRecordDTO.java @@ -0,0 +1,38 @@ +package com.thing.publicorg.noticeviewrecord.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 通知查看记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "通知查看记录") +public class PublicNoticeViewRecordDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "通知id") + private Long noticeId; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/entity/PublicNoticeViewRecordEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/entity/PublicNoticeViewRecordEntity.java new file mode 100644 index 0000000..b80eb7c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/entity/PublicNoticeViewRecordEntity.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.noticeviewrecord.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + + +/** + * 通知查看记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_notice_view_record") +public class PublicNoticeViewRecordEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 通知id + */ + private Long noticeId; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/excel/PublicNoticeViewRecordExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/excel/PublicNoticeViewRecordExcel.java new file mode 100644 index 0000000..fd1867c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/excel/PublicNoticeViewRecordExcel.java @@ -0,0 +1,38 @@ +package com.thing.publicorg.noticeviewrecord.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 通知查看记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicNoticeViewRecordExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "通知id", index = 1) + private Long noticeId; + @ExcelProperty(value = "创建人", index = 2) + private Long creator; + @ExcelProperty(value = "创建时间", index = 3) + private Date createDate; + @ExcelProperty(value = "更新人", index = 4) + private Long updater; + @ExcelProperty(value = "更新时间", index = 5) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 6) + private Long deptId; + @ExcelProperty(value = "租户code", index = 7) + private Long tenantCode; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/mapper/PublicNoticeViewRecordMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/mapper/PublicNoticeViewRecordMapper.java new file mode 100644 index 0000000..14d9f60 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/mapper/PublicNoticeViewRecordMapper.java @@ -0,0 +1,22 @@ +package com.thing.publicorg.noticeviewrecord.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.noticeviewrecord.dto.PublicNoticeViewRecordDTO; +import com.thing.publicorg.noticeviewrecord.entity.PublicNoticeViewRecordEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 通知查看记录 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicNoticeViewRecordMapper extends PowerBaseMapper { + + List getByNoticeIdAndTenantCode(@Param("noticeId") Long noticeId, @Param("tenantCode")Long tenantCode); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/PublicNoticeViewRecordService.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/PublicNoticeViewRecordService.java new file mode 100644 index 0000000..2232697 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/PublicNoticeViewRecordService.java @@ -0,0 +1,19 @@ +package com.thing.publicorg.noticeviewrecord.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.noticeviewrecord.dto.PublicNoticeViewRecordDTO; +import com.thing.publicorg.noticeviewrecord.entity.PublicNoticeViewRecordEntity; + +import java.util.List; + +/** + * 通知查看记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicNoticeViewRecordService extends IBaseService { + + List getByNoticeIdAndTenantCode(Long id, Long tenantCode); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/impl/PublicNoticeViewRecordServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/impl/PublicNoticeViewRecordServiceImpl.java new file mode 100644 index 0000000..cbfb1b4 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/noticeviewrecord/service/impl/PublicNoticeViewRecordServiceImpl.java @@ -0,0 +1,39 @@ +package com.thing.publicorg.noticeviewrecord.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.noticeviewrecord.dto.PublicNoticeViewRecordDTO; +import com.thing.publicorg.noticeviewrecord.entity.PublicNoticeViewRecordEntity; +import com.thing.publicorg.noticeviewrecord.mapper.PublicNoticeViewRecordMapper; +import com.thing.publicorg.noticeviewrecord.service.PublicNoticeViewRecordService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 通知查看记录 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicNoticeViewRecordServiceImpl extends BaseServiceImpl implements PublicNoticeViewRecordService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + return wrapper; + } + + + @Override + public List getByNoticeIdAndTenantCode(Long noticeId, Long tenantCode) { + return mapper.getByNoticeIdAndTenantCode(noticeId,tenantCode); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/controller/PublicOrgController.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/controller/PublicOrgController.java new file mode 100644 index 0000000..4e4d65e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/controller/PublicOrgController.java @@ -0,0 +1,78 @@ +package com.thing.publicorg.org.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.org.dto.PublicOrgDTO; +import com.thing.publicorg.org.dto.PublicOrgInfoDTO; +import com.thing.publicorg.org.service.PublicOrgService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + + +/** +* 公共机构 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicorg") +@Tag(name="公共机构") +public class PublicOrgController { + @Autowired + private PublicOrgService publicOrgService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "quotaType",description ="定额类型") , + @Parameter(name = "industryCode",description ="行业代码") , + @Parameter(name = "orgName",description ="机构名称") + }) +// @RequiresPermissions("org:publicorg:page") + public Result> page( @RequestParam Map params){ + PageData page = publicOrgService.getPageData(params,PublicOrgDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("/orgInfo/{id}") + @Operation(summary="获取机构信息") +// @RequiresPermissions("org:publicorg:info") + public Result getOrgInfo(@PathVariable("id") Long id){ + PublicOrgInfoDTO data = publicOrgService.getOrgInfo(id); + + return new Result().ok(data); + } + + @PutMapping + @Operation(summary="编辑机构信息") + @LogOperation("编辑机构信息") +// @RequiresPermissions("org:publicorg:update") + public Result updateOrgInfo(@RequestBody PublicOrgInfoDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicOrgService.updateOrgInfo(dto); + + return new Result(); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgDTO.java new file mode 100644 index 0000000..de04f82 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgDTO.java @@ -0,0 +1,81 @@ +package com.thing.publicorg.org.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 公共机构 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "公共机构") +public class PublicOrgDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "地区id") + private Long regionId; + @Schema(description = "机构地址") + private String address; + @Schema(description = "租户Code") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern =DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构坐标") + private String coordinate; + @Schema(description = "备注") + private String description; + @Schema(description = "机构图片") + private String picture; + @Schema(description = "行业代码") + private String industryCode; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "机构类别归属") + private String orgType; + @Schema(description = "单位负责人") + private String orgLeader; + @Schema(description = "统计负责人") + private String statLeader; + @Schema(description = "统计负责人手机") + private String statLeaderPhone; + @Schema(description = "统计员") + private String statUser; + @Schema(description = "统计员手机") + private String statUserPhone; + @Schema(description = "固定电话") + private String telephone; + @Schema(description = "空调形式") + private String airConditioningType; + @Schema(description = "社会信用代码") + private String corporationCode; + @Schema(description = "地区名称") + private String regionName; + @Schema(description = "地区编号") + private String regionCode; + @Schema(description = "机构名称") + private String orgName; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgInfoDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgInfoDTO.java new file mode 100644 index 0000000..4f04da7 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/dto/PublicOrgInfoDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.org.dto; + +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** +* 公共机构 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "公共机构信息") +public class PublicOrgInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "orgInfo") + private PublicOrgDTO orgInfo; + @Schema(description = "orgDetails") + private List orgDetails; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/entity/PublicOrgEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/entity/PublicOrgEntity.java new file mode 100644 index 0000000..8a50569 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/entity/PublicOrgEntity.java @@ -0,0 +1,99 @@ +package com.thing.publicorg.org.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 公共机构 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_org") +public class PublicOrgEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + + /** + * 地区id + */ + private Long regionId; + /** + * 机构地址 + */ + private String address; + /** + * 机构坐标 + */ + private String coordinate; + /** + * 备注 + */ + private String description; + /** + * 机构图片 + */ + private String picture; + /** + * 行业代码 + */ + private String industryCode; + /** + * 定额类型 + */ + private String quotaType; + /** + * 机构类别归属 + */ + private String orgType; + /** + * 单位负责人 + */ + private String orgLeader; + /** + * 统计负责人 + */ + private String statLeader; + /** + * 统计负责人手机 + */ + private String statLeaderPhone; + /** + * 统计员 + */ + private String statUser; + /** + * 统计员手机 + */ + private String statUserPhone; + /** + * 固定电话 + */ + private String telephone; + /** + * 空调形式 + */ + private String airConditioningType; + /** + * 社会信用代码 + */ + private String corporationCode; + /** + * 地区名称 + */ + private String regionName; + /** + * 地区编号 + */ + private String regionCode; + /** + * 机构名称 + */ + private String orgName; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/excel/PublicOrgExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/excel/PublicOrgExcel.java new file mode 100644 index 0000000..cbac220 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/excel/PublicOrgExcel.java @@ -0,0 +1,74 @@ +package com.thing.publicorg.org.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 公共机构 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicOrgExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "企业id", index = 1) + private Long deptId; + @ExcelProperty(value = "地区id", index = 2) + private Long regionId; + @ExcelProperty(value = "机构地址", index = 3) + private String address; + @ExcelProperty(value = "租户Code", index = 4) + private Long tenantCode; + @ExcelProperty(value = "创建者", index = 5) + private Long creator; + @ExcelProperty(value = "创建时间", index = 6) + private Date createDate; + @ExcelProperty(value = "更新者", index = 7) + private Long updater; + @ExcelProperty(value = "更新时间", index = 8) + private Date updateDate; + @ExcelProperty(value = "机构坐标", index = 9) + private String coordinate; + @ExcelProperty(value = "备注", index = 10) + private String description; + @ExcelProperty(value = "机构图片", index = 11) + private Object picture; + @ExcelProperty(value = "行业代码", index = 12) + private String industryCode; + @ExcelProperty(value = "定额类型", index = 13) + private String quotaType; + @ExcelProperty(value = "机构类别归属", index = 14) + private String orgType; + @ExcelProperty(value = "单位负责人", index = 15) + private String orgLeader; + @ExcelProperty(value = "统计负责人", index = 16) + private String statLeader; + @ExcelProperty(value = "统计负责人手机", index = 17) + private String statLeaderPhone; + @ExcelProperty(value = "统计员", index = 18) + private String statUser; + @ExcelProperty(value = "统计员手机", index = 19) + private String statUserPhone; + @ExcelProperty(value = "固定电话", index = 20) + private String telephone; + @ExcelProperty(value = "空调形式", index = 21) + private String airConditioningType; + @ExcelProperty(value = "社会信用代码", index = 22) + private String corporationCode; + @ExcelProperty(value = "地区名称", index = 23) + private String regionName; + @ExcelProperty(value = "地区编号", index = 24) + private String regionCode; + @ExcelProperty(value = "机构名称", index = 25) + private String orgName; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/mapper/PublicOrgMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/mapper/PublicOrgMapper.java new file mode 100644 index 0000000..5dfef5d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/mapper/PublicOrgMapper.java @@ -0,0 +1,54 @@ +package com.thing.publicorg.org.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.org.dto.PublicOrgDTO; +import com.thing.publicorg.org.entity.PublicOrgEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** +* 公共机构 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicOrgMapper extends PowerBaseMapper { + + List getListData(Map params); + + List getAllOrg(); + + List getAllDeptIds(); + + List getCurrentAllOrg(@Param("yearStartTime") Timestamp yearStartTime, @Param("yearEndTime")Timestamp yearEndTime); + + List getAreaAllOrg(@Param("regionCode") String regionCode); + + List getOrgUnits(@Param("industryCode")String industryCode); + + List getOrgListByParams(Map params); + + List getOrgListByParams1(Map params); + + List getOrgListByParams2(Map params); + + PublicOrgDTO getDeptIds(@Param("deptId") Long deptId); + + PublicOrgDTO getByTenantCode(@Param("tenantCode")Long tenantCode); + + List getAllDeptIdsByIndustryCode(@Param("industryCode") String industryCode); + + List getOrgUnites(@Param("industryCode")String industryCode,@Param("unitName")String unitName,@Param("deptId") Long deptId,@Param("yearStartTime")Timestamp yearStartTime,@Param("yearEndTime") Timestamp yearEndTime); + + List getUnitsIndustryCode(@Param("unitsName")String unitsName, @Param("industryCode")String industryCode,@Param("yearStartTimes")Timestamp yearStartTimes,@Param("yearEndTimes")Timestamp yearEndTimes); + + List getByIdNameOrgs(@Param("deptId")Long deptId, @Param("industryCode")String industryCode, @Param("unitName")String unitName); + + List getByQuotaType(@Param("quotaType")String quotaType); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/service/PublicOrgService.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/service/PublicOrgService.java new file mode 100644 index 0000000..ec61c38 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/service/PublicOrgService.java @@ -0,0 +1,67 @@ +package com.thing.publicorg.org.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.org.dto.PublicOrgDTO; +import com.thing.publicorg.org.dto.PublicOrgInfoDTO; +import com.thing.publicorg.org.entity.PublicOrgEntity; +import com.thing.publicorg.publicboard.dto.PublicOrgDetailInfoDTO; + +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** + * 公共机构 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicOrgService extends IBaseService { + + PageData page(Map params); + + PublicOrgInfoDTO getOrgInfo(Long id); + + void updateOrgInfo(PublicOrgInfoDTO dto); + + List getAllOrgInfo(); + + List getAllDeptIds(); + + List getAllOrg(); + + List getCurrentAllOrg(Timestamp yearStartTime, Timestamp yearEndTime); + + List getAreaOrgDetails(String year, String regionCode); + + List getOrgDetails(String year); + + List getOrgUnites(String industryCode,String unitName,Long deptId,Timestamp yearStartTime, Timestamp yearEndTime); + + List getOrgDetailsByParams(String year, Map params); + + List getOrgDetailsByParams1(String year, Map params); + + List getOrgDetailsByParams2(String year, Map params); + + PublicOrgDTO getDeptIds(Long deptId); + + PublicOrgDetailInfoDTO getByDeptId(Long deptId,String year); + + PublicOrgDetailInfoDTO getByTenantCode(String year, Long tenantCode); + + List getOrgDetailsByIndustryCode(String year, String industryCode); + + List getAllDeptIdsByIndustryCode(String industryCode); + + List getOrgUnits(String industryCode); + + List getUnitsIndustryCode(String unitsName,String IndustryCode,Timestamp yearStartTimes,Timestamp yearEndTimes); + + List getAllOrgInfos(Long deptId, String industryCode, String unitName); + + List getByQuotaType(String dictValue); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/org/service/impl/PublicOrgServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/org/service/impl/PublicOrgServiceImpl.java new file mode 100644 index 0000000..9a940fe --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/org/service/impl/PublicOrgServiceImpl.java @@ -0,0 +1,795 @@ +package com.thing.publicorg.org.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.dto.TelemetrySaveDTO; +import com.thing.common.data.dto.TsKvReqParam; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.indexstandard.dto.PublicIndexStandardDTO; +import com.thing.publicorg.indexstandard.service.PublicIndexStandardService; +import com.thing.publicorg.org.dto.PublicOrgDTO; +import com.thing.publicorg.org.dto.PublicOrgInfoDTO; +import com.thing.publicorg.org.entity.PublicOrgEntity; +import com.thing.publicorg.org.mapper.PublicOrgMapper; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import com.thing.publicorg.orgdetail.entity.PublicOrgDetailEntity; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import com.thing.publicorg.publicboard.dto.PublicOrgDetailInfoDTO; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.entity.PublicOrgLimitEntity; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +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.math.BigDecimal; +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 公共机构 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicOrgServiceImpl extends BaseServiceImpl implements PublicOrgService { + + + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + @Autowired + private PublicOrgLimitService publicOrgLimitService; + + @Autowired + private PublicIndexStandardService publicIndexStandardService; + + @Autowired + private SysTenantService sysTenantService; + + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + + @Override + public PageData page(Map params) { + + UserDetail userDetail = SecurityUser.getUser(); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + //如果是admin,并且没有切租户,则查全部 + if (userDetail.getSuperAdmin()== SuperAdminEnum.YES.value()){ + if (userDetail.getTenantCode().equals(TenantContext.getTenantCode(SecurityUser.getUser()))){ + params.put("tenantCode", null); + } + } + PageData pageData = getPageData(params, PublicOrgDTO.class); + return pageData; + } + + @Override + public PublicOrgInfoDTO getOrgInfo(Long id) { + PublicOrgInfoDTO result = new PublicOrgInfoDTO(); + PublicOrgEntity publicOrgEntity = mapper.selectOneById(id); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(id); + result.setOrgInfo(ConvertUtils.sourceToTarget(publicOrgEntity,PublicOrgDTO.class)); + result.setOrgDetails(orgDetailDTOS); + + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateOrgInfo(PublicOrgInfoDTO dto) { + PublicOrgDTO orgInfo = dto.getOrgInfo(); + List orgDetails = dto.getOrgDetails(); + if (CollectionUtil.isEmpty(orgDetails)){ + throw new SysException("机构详细信息不可为空"); + } + List insertList = new ArrayList<>(); + List updateList = new ArrayList<>(); + + List ids = new ArrayList<>(); + for (PublicOrgDetailDTO orgDetailDTO:orgDetails){ + orgDetailDTO.setDeptId(orgInfo.getDeptId()); + orgDetailDTO.setTenantCode(orgInfo.getTenantCode()); + if (orgDetailDTO.getId()!=null){ + updateList.add(orgDetailDTO); + ids.add(orgDetailDTO.getId()); + }else{ + insertList.add(orgDetailDTO); + } + } + updateDto(orgInfo); + //保存公共机构对应的指标阈值 + List limitDTOS = new ArrayList<>(); + setOrgLimt(dto, orgInfo, limitDTOS); + if (CollectionUtil.isNotEmpty(limitDTOS)){ + publicOrgLimitService.deleteByOrgId(orgInfo.getId()); + publicOrgLimitService.saveBatch(ConvertUtils.sourceToTarget(limitDTOS, PublicOrgLimitEntity.class)); + } + if (CollectionUtil.isNotEmpty(updateList)){ + publicOrgDetailService.deleteNotInIds(ids); + publicOrgDetailService.updateBatch(ConvertUtils.sourceToTarget(updateList, PublicOrgDetailEntity.class)); + tbLists(updateList); + } + if (CollectionUtil.isNotEmpty(insertList)){ + publicOrgDetailService.saveBatch(ConvertUtils.sourceToTarget(insertList, PublicOrgDetailEntity.class)); + tbLists(insertList); + } + } + + private void tbLists(List insertList) { + SysTenantDTO tenantCode = sysTenantService.getTenantCode(insertList.get(0).getTenantCode()); + if(tenantCode!=null){ + for (PublicOrgDetailDTO publicOrgDetailDTO : insertList) { + TelemetrySaveDTO telemetrySaveDTO = new TelemetrySaveDTO(); + telemetrySaveDTO.setThingCode(tenantCode.getThingCode()); + List tsKvReqParams = new ArrayList<>(); + TsKvReqParam tsKvReqParam = new TsKvReqParam(); + String year = publicOrgDetailDTO.getYear()+"-01-01 00:00:00"; + Long aLong = DateTimeUtils.dateToStamp(year); + tsKvReqParam.setTs(aLong); + Map values = new HashMap<>(); + if(publicOrgDetailDTO.getBuildArea()!=null){ + values.put("buildSpaceyy",publicOrgDetailDTO.getBuildArea()); + } + if(publicOrgDetailDTO.getDayUserCount()!=null){ + values.put("perDailyCap",publicOrgDetailDTO.getDayUserCount()); + } + if(publicOrgDetailDTO.getCanteenUserCount()!=null){ + values.put("perDailyCapCanteen",publicOrgDetailDTO.getCanteenUserCount()); + } + tsKvReqParam.setValues(values); + tsKvReqParams.add(tsKvReqParam); + telemetrySaveDTO.setTsKvList(tsKvReqParams); + // iDeviceDataFacade.saveEntityTelemetryByRuleEngine(Lists.newArrayList(telemetrySaveDTO)); + + List monthList = DateTimeUtils.getMonthList(year.substring(0,4)); + for (String s : monthList) { + TelemetrySaveDTO telemetrySaveDTO1 = new TelemetrySaveDTO(); + telemetrySaveDTO1.setThingCode(tenantCode.getThingCode()); + List tsKvReqParams1 = new ArrayList<>(); + TsKvReqParam tsKvReqParam1 = new TsKvReqParam(); + Long aLong1 = DateTimeUtils.dateToStamp(s+"-01 00:00:00"); + tsKvReqParam1.setTs(aLong1); + Map values1 = new HashMap<>(); + if(publicOrgDetailDTO.getBuildArea()!=null){ + values1.put("buildSpacemm",publicOrgDetailDTO.getBuildArea()); + } + if(publicOrgDetailDTO.getDayUserCount()!=null){ + values1.put("perDailyCapmm",publicOrgDetailDTO.getDayUserCount()); + } + if(publicOrgDetailDTO.getCanteenUserCount()!=null){ + values1.put("perDailyCapCanteenmm",publicOrgDetailDTO.getCanteenUserCount()); + } + tsKvReqParam1.setValues(values1); + tsKvReqParams1.add(tsKvReqParam1); + telemetrySaveDTO1.setTsKvList(tsKvReqParams1); + // iDeviceDataFacade.saveEntityTelemetryByRuleEngine(Lists.newArrayList(telemetrySaveDTO1)); + } + } + } + } + + private void setOrgLimt(PublicOrgInfoDTO dto, PublicOrgDTO orgInfo, List limitDTOS) { + //获取第一条机构明细信息 + List details = dto.getOrgDetails().stream().sorted(Comparator.comparing(PublicOrgDetailDTO::getYear).reversed()).collect(Collectors.toList()); + PublicOrgDetailDTO detailDTO = details.get(0); + //获取建筑面积,用能人数 + BigDecimal buildArea = detailDTO.getBuildArea(); + int dayUserCount = detailDTO.getDayUserCount(); + //根据指标阈值和机构类型归属获取机构对应的指标阈值 + List indexStandardDTOS = publicIndexStandardService.getByOrgInfo(dto.getOrgInfo().getQuotaType(),dto.getOrgInfo().getOrgType()); + //各种判断 + //集中办公区,四种阈值都需要 + if (orgInfo.getQuotaType().equals(Constant.CENTRALIZED_OFFICE_AREA)){ + for (PublicIndexStandardDTO standardDTO:indexStandardDTOS){ + //单位建筑面积并且空调形式相同 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERBULID) + && standardDTO.getIndexTypeValue().equals(orgInfo.getAirConditioningType())){ + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //人均建筑面积并且空调形式相同 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERPERSON) + && standardDTO.getIndexTypeValue().equals(orgInfo.getAirConditioningType())){ + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //数据中心使用效率 + if (standardDTO.getIndexName().equals(Constant.ENERGY_UTILIZATION_EFFICIENCY)){ + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_UTILIZATION_EFFICIENCY); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //食堂使用效率 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERCAFETERIA_PERSON)){ + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERCAFETERIA_PERSON); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + } + }else{ + //独立办公区的党政机关 + if (orgInfo.getQuotaType().equals(Constant.INDEPENDENT_OFFICE_AREA)) { + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + //单位建筑面积并且空调形式相同 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERBULID) + && standardDTO.getIndexTypeValue().equals(orgInfo.getAirConditioningType())) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //人均建筑面积并且空调形式相同 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERPERSON) + && standardDTO.getIndexTypeValue().equals(orgInfo.getAirConditioningType())) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + } + } + //中小学公共机构||高等教育机构 + if (orgInfo.getQuotaType().equals(Constant.PRIMARY_SECONDARY_SCHOOLS) || orgInfo.getQuotaType().equals(Constant.HIGHER_EDUCATION)){ + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + //单位建筑面积 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERBULID)) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //人均建筑面积 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERPERSON)) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + } + } + + //医疗卫生机构 + if (orgInfo.getQuotaType().equals(Constant.MEDICAL_HYGIENE)){ + //三级医院 + if (orgInfo.getOrgType().equals(Constant.TERTIARY_HOSPITAL)){ + Map map = new HashMap<>(); + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + map.put(standardDTO.getIndexTypeValue(),standardDTO); + } + //单位建筑面积 + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + + PublicOrgLimitDTO orgLimitDTO1 = new PublicOrgLimitDTO(); + orgLimitDTO1.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO1.setDeptId(orgInfo.getDeptId()); + orgLimitDTO1.setTenantCode(orgInfo.getTenantCode()); + + //建筑面积比较 + if (buildArea.compareTo(new BigDecimal(90000))==1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_A); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + if (buildArea.compareTo(new BigDecimal(45000))==1 && buildArea.compareTo(new BigDecimal(90000))!=1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_B); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + if (buildArea.compareTo(new BigDecimal(45000))!=1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_C); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + + //用能人数比较 + if (dayUserCount>6300){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_A); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + if (dayUserCount>3000 && dayUserCount<=6300){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_B); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + if (dayUserCount<=3000){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_C); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + limitDTOS.add(orgLimitDTO); + limitDTOS.add(orgLimitDTO1); + }else if (orgInfo.getOrgType().equals(Constant.SECONDARY_HOSPITAL)){ + //二级医院 + Map map = new HashMap<>(); + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + map.put(standardDTO.getIndexTypeValue(),standardDTO); + } + //单位建筑面积 + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + + PublicOrgLimitDTO orgLimitDTO1 = new PublicOrgLimitDTO(); + orgLimitDTO1.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO1.setDeptId(orgInfo.getDeptId()); + orgLimitDTO1.setTenantCode(orgInfo.getTenantCode()); + + //建筑面积比较 + if (buildArea.compareTo(new BigDecimal(25000))==1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_D); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + if (buildArea.compareTo(new BigDecimal(11000))==1 && buildArea.compareTo(new BigDecimal(25000))!=1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_E); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + if (buildArea.compareTo(new BigDecimal(11000))!=1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_F); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + + //用能人数比较 + if (dayUserCount>1700){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_D); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + if (dayUserCount>600 && dayUserCount<=1700){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_E); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + if (dayUserCount<=600){ + PublicIndexStandardDTO standardDTO = map.get(Constant.NUMBER_F); + orgLimitDTO1.setYearValue(standardDTO.getYearValue()); + orgLimitDTO1.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO1.setUnit(standardDTO.getUnit()); + } + limitDTOS.add(orgLimitDTO); + limitDTOS.add(orgLimitDTO1); + }else{ + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + //单位建筑面积 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERBULID)) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + //人均建筑面积 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERPERSON)) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERPERSON); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + } + } + } + if (orgInfo.getQuotaType().equals(Constant.VENUE_CATEGORY)){ + //非演出类 + if (orgInfo.getOrgType().equals(Constant.NON_PERFORMANCE_VENUES_ONE)||orgInfo.getOrgType().equals(Constant.NON_PERFORMANCE_VENUES_TWO)){ + Map map = new HashMap<>(); + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + map.put(standardDTO.getIndexTypeValue(),standardDTO); + } + //单位建筑面积 + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + + //建筑面积比较 + if (buildArea.compareTo(new BigDecimal(5000))!=-1){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_G); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + if (buildArea.compareTo(new BigDecimal(5000))==-1 ){ + PublicIndexStandardDTO standardDTO = map.get(Constant.BUILD_H); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + } + limitDTOS.add(orgLimitDTO); + }else{ + for (PublicIndexStandardDTO standardDTO : indexStandardDTOS) { + //单位建筑面积 + if (standardDTO.getIndexName().equals(Constant.ENERGY_PERBULID)) { + PublicOrgLimitDTO orgLimitDTO = new PublicOrgLimitDTO(); + orgLimitDTO.setIndexName(Constant.ENERGY_PERBULID); + orgLimitDTO.setYearValue(standardDTO.getYearValue()); + orgLimitDTO.setMonthValue(standardDTO.getMonthValue()); + orgLimitDTO.setUnit(standardDTO.getUnit()); + orgLimitDTO.setDeptId(orgInfo.getDeptId()); + orgLimitDTO.setTenantCode(orgInfo.getTenantCode()); + limitDTOS.add(orgLimitDTO); + continue; + } + } + } + } + } + } + + @Override + public List getAllOrgInfo() { + List result = new ArrayList<>(); + List orgDTOS = mapper.getAllOrg(); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgInfoDTO publicOrgInfoDTO = new PublicOrgInfoDTO(); + publicOrgInfoDTO.setOrgInfo(orgDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + publicOrgInfoDTO.setOrgDetails(orgDetailDTOS); + result.add(publicOrgInfoDTO); + } + } + + return result; + } + + @Override + public List getAllDeptIds() { + return mapper.getAllDeptIds(); + } + + @Override + public List getAllOrg() { + return mapper.getAllOrg(); + } + + @Override + public List getCurrentAllOrg(Timestamp yearStartTime, Timestamp yearEndTime) { + return mapper.getCurrentAllOrg(yearStartTime,yearEndTime); + } + + @Override + public List getAreaOrgDetails(String year, String regionCode) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getAreaAllOrg(regionCode); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public List getOrgDetails(String year) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getAllOrg(); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public List getOrgUnites(String industryCode,String unitName,Long deptId,Timestamp yearStartTime, Timestamp yearEndTime) { + return mapper.getOrgUnites(industryCode,unitName,deptId,yearStartTime,yearEndTime); + } + + + @Override + public List getOrgDetailsByParams(String year, Map params) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getOrgListByParams(params); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public List getOrgDetailsByParams1(String year, Map params) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getOrgListByParams1(params); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public List getOrgDetailsByParams2(String year, Map params) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getOrgListByParams2(params); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public PublicOrgDTO getDeptIds(Long deptId) { + return mapper.getDeptIds(deptId); + } + + @Override + public PublicOrgDetailInfoDTO getByDeptId(Long deptId,String year) { + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + PublicOrgDTO orgDTO = getByIdAs(deptId,PublicOrgDTO.class); + if (orgDTO!=null){ + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isNotEmpty(orgDetailDTOS)){ + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + } + } + return orgDetailInfoDTO; + } + + @Override + public PublicOrgDetailInfoDTO getByTenantCode(String year, Long tenantCode) { + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + PublicOrgDTO orgDTO = mapper.getByTenantCode(tenantCode); + if (orgDTO!=null){ + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + if (CollectionUtil.isNotEmpty(orgDetailDTOS)){ + PublicOrgDetailDTO orgDetailDTO = new PublicOrgDetailDTO(); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + } + } + return orgDetailInfoDTO; + } + + @Override + public List getOrgDetailsByIndustryCode(String year, String industryCode) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getOrgUnits(industryCode); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgDetailInfoDTO orgDetailInfoDTO = new PublicOrgDetailInfoDTO(); + BeanUtils.copyProperties(orgDTO,orgDetailInfoDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getDeptId()); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + continue; + } + PublicOrgDetailDTO orgDetailDTO = orgDetailDTOS.get(0); + for (PublicOrgDetailDTO orgDetailDTO1:orgDetailDTOS){ + if (year.equals(orgDetailDTO1.getYear())){ + orgDetailDTO = orgDetailDTO1; + } + } + orgDetailInfoDTO.setOrgDetail(orgDetailDTO); + result.add(orgDetailInfoDTO); + } + } + return result; + } + + @Override + public List getAllDeptIdsByIndustryCode(String industryCode) { + return mapper.getAllDeptIdsByIndustryCode(industryCode); + } + + @Override + public List getOrgUnits(String industryCode) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getOrgUnits(industryCode); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgInfoDTO publicOrgInfoDTO = new PublicOrgInfoDTO(); + publicOrgInfoDTO.setOrgInfo(orgDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + publicOrgInfoDTO.setOrgDetails(orgDetailDTOS); + result.add(publicOrgInfoDTO); + } + } + + return result; + } + + @Override + public List getUnitsIndustryCode(String unitsName,String IndustryCode,Timestamp yearStartTimes,Timestamp yearEndTimes) { + return mapper.getUnitsIndustryCode(unitsName,IndustryCode,yearStartTimes,yearEndTimes); + } + + @Override + public List getAllOrgInfos(Long deptId, String industryCode, String unitName) { + List result = new ArrayList<>(); + List orgDTOS = mapper.getByIdNameOrgs(deptId,industryCode,unitName); + if (CollectionUtil.isNotEmpty(orgDTOS)){ + for (PublicOrgDTO orgDTO: orgDTOS){ + PublicOrgInfoDTO publicOrgInfoDTO = new PublicOrgInfoDTO(); + publicOrgInfoDTO.setOrgInfo(orgDTO); + List orgDetailDTOS = publicOrgDetailService.getByDeptId(orgDTO.getId()); + publicOrgInfoDTO.setOrgDetails(orgDetailDTOS); + result.add(publicOrgInfoDTO); + } + } + + return result; + } + + @Override + public List getByQuotaType(String quotaType) { + return mapper.getByQuotaType(quotaType); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/controller/PublicOrgDetailController.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/controller/PublicOrgDetailController.java new file mode 100644 index 0000000..2ab3a96 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/controller/PublicOrgDetailController.java @@ -0,0 +1,114 @@ +package com.thing.publicorg.orgdetail.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import com.thing.publicorg.orgdetail.excel.PublicOrgDetailExcel; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + + +/** +* 公共机构明细数据 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicorgdetail") +@Tag(name="公共机构明细数据") +public class PublicOrgDetailController { + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("orgdetail:publicorgdetail:page") + public Result> page( @RequestParam Map params){ + PageData page = publicOrgDetailService.getPageData(params,PublicOrgDetailDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("orgdetail:publicorgdetail:info") + public Result get(@PathVariable("id") Long id){ + PublicOrgDetailDTO data = publicOrgDetailService.getByIdAs(id,PublicOrgDetailDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("orgdetail:publicorgdetail:save") + public Result save(@RequestBody PublicOrgDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + publicOrgDetailService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("orgdetail:publicorgdetail:update") + public Result update(@RequestBody PublicOrgDetailDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicOrgDetailService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("orgdetail:publicorgdetail:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicOrgDetailService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("orgdetail:publicorgdetail:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicOrgDetailService.listAs(params,PublicOrgDetailDTO.class); + + ExcelUtils.exportExcel(list,null, "公共机构明细数据", PublicOrgDetailExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/dto/PublicOrgDetailDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/dto/PublicOrgDetailDTO.java new file mode 100644 index 0000000..a039cfe --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/dto/PublicOrgDetailDTO.java @@ -0,0 +1,65 @@ +package com.thing.publicorg.orgdetail.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 公共机构明细数据 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "公共机构明细数据") +public class PublicOrgDetailDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "租户Code") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "年份") + private String year; + @Schema(description = "用地面积") + private BigDecimal landArea; + @Schema(description = "建筑面积") + private BigDecimal buildArea; + @Schema(description = "编制人数") + private Integer headCount; + @Schema(description = "日均用能人数") + private Integer dayUserCount; + @Schema(description = "食堂日均人数") + private Integer canteenUserCount; + @Schema(description = "车辆数量") + private Integer carCount; + @Schema(description = "汽油车数量") + private Integer gasCarCount; + @Schema(description = "新能源车数量") + private Integer greenCarCount; + @Schema(description = "柴油车数量") + private Integer dieselCarCount; + @Schema(description = "太阳能集热器面积") + private BigDecimal solarCollectorArea; + @Schema(description = "充电桩数量") + private Integer chargingStationCount; + @Schema(description = "地热能利用装机容量") + private BigDecimal geothermalEnergyCapacity; + @Schema(description = "太阳能光电装机容量") + private Integer solarCapacity; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/entity/PublicOrgDetailEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/entity/PublicOrgDetailEntity.java new file mode 100644 index 0000000..d10bc74 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/entity/PublicOrgDetailEntity.java @@ -0,0 +1,82 @@ +package com.thing.publicorg.orgdetail.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/** + * 公共机构明细数据 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_org_detail") +public class PublicOrgDetailEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + + /** + * 年份 + */ + private String year; + /** + * 用地面积 + */ + + private BigDecimal landArea; + /** + * 建筑面积 + */ + private BigDecimal buildArea; + /** + * 编制人数 + */ + private Integer headCount; + /** + * 日均用能人数 + */ + private Integer dayUserCount; + /** + * 食堂日均人数 + */ + private Integer canteenUserCount; + /** + * 车辆数量 + */ + private Integer carCount; + /** + * 汽油车数量 + */ + private Integer gasCarCount; + /** + * 新能源车数量 + */ + private Integer greenCarCount; + /** + * 柴油车数量 + */ + private Integer dieselCarCount; + /** + * 太阳能集热器面积 + */ + private BigDecimal solarCollectorArea; + /** + * 充电桩数量 + */ + private Integer chargingStationCount; + /** + * 地热能利用装机容量 + */ + private BigDecimal geothermalEnergyCapacity; + /** + * 太阳能光电装机容量 + */ + private Integer solarCapacity; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/excel/PublicOrgDetailExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/excel/PublicOrgDetailExcel.java new file mode 100644 index 0000000..6598487 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/excel/PublicOrgDetailExcel.java @@ -0,0 +1,65 @@ +package com.thing.publicorg.orgdetail.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 公共机构明细数据 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicOrgDetailExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "企业id", index = 1) + private Long deptId; + @ExcelProperty(value = "租户Code", index = 2) + private Long tenantCode; + @ExcelProperty(value = "创建者", index = 3) + private Long creator; + @ExcelProperty(value = "创建时间", index = 4) + private Date createDate; + @ExcelProperty(value = "更新者", index = 5) + private Long updater; + @ExcelProperty(value = "更新时间", index = 6) + private Date updateDate; + @ExcelProperty(value = "年份", index = 7) + private String year; + @ExcelProperty(value = "用地面积", index = 8) + private BigDecimal landArea; + @ExcelProperty(value = "建筑面积", index = 9) + private BigDecimal buildArea; + @ExcelProperty(value = "编制人数", index = 10) + private Integer headCount; + @ExcelProperty(value = "日均用能人数", index = 11) + private Integer dayUserCount; + @ExcelProperty(value = "食堂日均人数", index = 12) + private Integer canteenUserCount; + @ExcelProperty(value = "车辆数量", index = 13) + private Integer carCount; + @ExcelProperty(value = "汽油车数量", index = 14) + private Integer gasCarCount; + @ExcelProperty(value = "新能源车数量", index = 15) + private Integer greenCarCount; + @ExcelProperty(value = "柴油车数量", index = 16) + private Integer dieselCarCount; + @ExcelProperty(value = "太阳能集热器面积", index = 17) + private BigDecimal solarCollectorArea; + @ExcelProperty(value = "充电桩数量", index = 18) + private Integer chargingStationCount; + @ExcelProperty(value = "地热能利用装机容量", index = 19) + private BigDecimal geothermalEnergyCapacity; + @ExcelProperty(value = "太阳能光电装机容量", index = 20) + private Integer solarCapacity; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/mapper/PublicOrgDetailMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/mapper/PublicOrgDetailMapper.java new file mode 100644 index 0000000..6e74e87 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/mapper/PublicOrgDetailMapper.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.orgdetail.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import com.thing.publicorg.orgdetail.entity.PublicOrgDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; + +; + +/** +* 公共机构明细数据 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicOrgDetailMapper extends PowerBaseMapper { + + List getByDeptId(@Param("deptId") Long deptId); + + List getByDeptIdYear(@Param("deptId") Long deptId,@Param("year") String year); + + BigDecimal getInstalledCapacity(@Param("deptIds") List deptIds); + + BigDecimal getChargingPileByid(@Param("deptIds") List deptId, @Param("yearStartTime") Timestamp yearStartTime, @Param("yearEndTime") Timestamp yearEndTime); + + void deleteNotInIds(@Param("ids")List ids); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/PublicOrgDetailService.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/PublicOrgDetailService.java new file mode 100644 index 0000000..257d3a1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/PublicOrgDetailService.java @@ -0,0 +1,29 @@ +package com.thing.publicorg.orgdetail.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import com.thing.publicorg.orgdetail.entity.PublicOrgDetailEntity; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; + +/** + * 公共机构明细数据 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicOrgDetailService extends IBaseService { + + List getByDeptId(Long id); + + List getByDeptIdYear(Long deptId, String year); + + BigDecimal getInstalledCapacity(List deptIds); + + BigDecimal getChargingPileByid(List deptIds, Timestamp yearStartTime, Timestamp yearEndTime); + + void deleteNotInIds(List ids); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/impl/PublicOrgDetailServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/impl/PublicOrgDetailServiceImpl.java new file mode 100644 index 0000000..4cebdcd --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/orgdetail/service/impl/PublicOrgDetailServiceImpl.java @@ -0,0 +1,60 @@ +package com.thing.publicorg.orgdetail.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import com.thing.publicorg.orgdetail.entity.PublicOrgDetailEntity; +import com.thing.publicorg.orgdetail.mapper.PublicOrgDetailMapper; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.sql.Timestamp; +import java.util.List; +import java.util.Map; + +/** + * 公共机构明细数据 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicOrgDetailServiceImpl extends BaseServiceImpl implements PublicOrgDetailService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + + @Override + public List getByDeptId(Long deptId) { + return mapper.getByDeptId(deptId); + } + + @Override + public List getByDeptIdYear(Long deptId, String year) { + return mapper.getByDeptIdYear(deptId,year); + } + + @Override + public BigDecimal getInstalledCapacity(List deptIds) { + return mapper.getInstalledCapacity(deptIds); + } + + @Override + public BigDecimal getChargingPileByid(List deptIds, Timestamp yearStartTime, Timestamp yearEndTime) { + return mapper.getChargingPileByid(deptIds,yearStartTime,yearEndTime); + } + + @Override + public void deleteNotInIds(List ids) { + mapper.deleteNotInIds(ids); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/controller/PublicPolicyAdvocacyController.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/controller/PublicPolicyAdvocacyController.java new file mode 100644 index 0000000..b99f9b1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/controller/PublicPolicyAdvocacyController.java @@ -0,0 +1,133 @@ +package com.thing.publicorg.policyadvocacy.controller; + + +import cn.afterturn.easypoi.excel.entity.ExportParams; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.benchmarkingproject.entity.PublicBenchmarkingProjectEntity; +import com.thing.publicorg.benchmarkingproject.excel.PublicBenchmarkingProjectExcel; +import com.thing.publicorg.policyadvocacy.dto.PublicPolicyAdvocacyDTO; +import com.thing.publicorg.policyadvocacy.excel.PublicPolicyAdvocacyExcel; +import com.thing.publicorg.policyadvocacy.service.PublicPolicyAdvocacyService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Date; +import java.util.List; +import java.util.Map; + + +/** +* 政策宣传 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@RestController +@RequestMapping("publicpolicyadvocacy") +@Tag(name="政策宣传") +public class PublicPolicyAdvocacyController { + @Autowired + private PublicPolicyAdvocacyService publicPolicyAdvocacyService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "publicityName",description ="公示名称"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type",description ="类型 0-区域企业 1-管理端"), + }) +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:page") + public Result> page( @RequestParam Map params){ + PageData page = publicPolicyAdvocacyService.pageList(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:info") + public Result get(@PathVariable("id") Long id){ + PublicPolicyAdvocacyDTO data = publicPolicyAdvocacyService.getByIdAs(id,PublicPolicyAdvocacyDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:save") + public Result save(@RequestBody PublicPolicyAdvocacyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setFileUploadDate(new Date()); + String fileExtension = ""; + int lastIndexOfDot = dto.getFileName().lastIndexOf("."); + if (lastIndexOfDot > 0) { + fileExtension = dto.getFileName().substring(lastIndexOfDot + 1); + } + dto.setTenantCode(TenantContext.getTenantCode(SecurityUser.getUser())); + dto.setFileType(fileExtension); + publicPolicyAdvocacyService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:update") + public Result update(@RequestBody PublicPolicyAdvocacyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicPolicyAdvocacyService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicPolicyAdvocacyService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("policyadvocacy:publicpolicyadvocacy:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicPolicyAdvocacyService.listAs(params, PublicBenchmarkingProjectEntity.class); +// ExcelUtils.exportExcel(list,"政策宣传", null, PublicPolicyAdvocacyExcel.class,"政策宣传.xls",response); + ExcelUtils.exportExcel(list,null, "政策宣传", PublicBenchmarkingProjectEntity.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/dto/PublicPolicyAdvocacyDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/dto/PublicPolicyAdvocacyDTO.java new file mode 100644 index 0000000..a081689 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/dto/PublicPolicyAdvocacyDTO.java @@ -0,0 +1,67 @@ +package com.thing.publicorg.policyadvocacy.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 政策宣传 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "政策宣传") +public class PublicPolicyAdvocacyDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "文件名") + private String fileName; + @Schema(description = "文件路径") + private String filePath; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "公示名称") + private String publicityName; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "文件时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date fileDate; + @Schema(description = "文件上传时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date fileUploadDate; + @Schema(description = "备注") + private String remark; + + @Schema(description = "文件类型") + private String fileType; + + @Schema(description = "类型") + private Integer type; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/entity/PublicPolicyAdvocacyEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/entity/PublicPolicyAdvocacyEntity.java new file mode 100644 index 0000000..197c208 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/entity/PublicPolicyAdvocacyEntity.java @@ -0,0 +1,93 @@ +package com.thing.publicorg.policyadvocacy.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Date; + +/** + * 政策宣传 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_policy_advocacy") +public class PublicPolicyAdvocacyEntity implements Serializable { + private static final long serialVersionUID = 1L; + + /** + * 文件名 + */ + private String fileName; + /** + * 文件路径 + */ + private String filePath; + /** + * 公示名称 + */ + private String publicityName; + /** + * 机构名称 + */ + private String orgName; + /** + * 文件时间 + */ + private Date fileDate; + /** + * 文件上传时间 + */ + private Date fileUploadDate; + /** + * 备注 + */ + private String remark; + /** + * 文件类型 + */ + private String fileType; + /** + * 类型 0-区域企业 1-管理端 + */ + private Integer type; + @Id + private Long id; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Date updateDate; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/excel/PublicPolicyAdvocacyExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/excel/PublicPolicyAdvocacyExcel.java new file mode 100644 index 0000000..ec07f83 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/excel/PublicPolicyAdvocacyExcel.java @@ -0,0 +1,94 @@ +package com.thing.publicorg.policyadvocacy.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.util.Date; + +/** + * 政策宣传 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +//@ContentRowHeight(20) +//@HeadRowHeight(20) +//@ColumnWidth(25) +public class PublicPolicyAdvocacyExcel { +// @ExcelProperty(value = "id", index = 0) +// private Long id; +// @ExcelProperty(value = "文件名", index = 1) +// private String fileName; +// @ExcelProperty(value = "文件路径", index = 2) +// private String filePath; +// @ExcelProperty(value = "创建人", index = 3) +// private Long creator; +// @ExcelProperty(value = "创建时间", index = 4) +// @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// private Date createDate; +// @ExcelProperty(value = "更新人", index = 5) +// private Long updater; +// @ExcelProperty(value = "更新时间", index = 6) +// @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) +// private Date updateDate; +// @ExcelProperty(value = "机构id", index = 7) +// private Long deptId; +// @ExcelProperty(value = "租户code", index = 8) +// private Long tenantCode; +// @ExcelProperty(value = "公示名称", index = 9) +// private String publicityName; +// @ExcelProperty(value = "机构名称", index = 10) +// private String orgName; +// @ExcelProperty(value = "文件时间", index = 11) +// private Date fileDate; +// @ExcelProperty(value = "文件上传时间", index = 12) +// private Date fileUploadDate; +// @ExcelProperty(value = "备注", index = 13) +// private String remark; +// @ExcelProperty(value = "类型 ", index = 14) +// private Integer type; + + @Excel(name = "id") + private Long id; + @Excel(name = "文件名") + private String fileName; + @Excel(name = "文件路径") + private String filePath; + @Excel(name = "创建人") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新人") + private Long updater; + @Excel(name = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Excel(name = "机构id") + private Long deptId; + @Excel(name = "租户code") + private Long tenantCode; + @Excel(name = "公示名称") + private String publicityName; + @Excel(name = "机构名称") + private String orgName; + @Excel(name = "文件时间") + private Date fileDate; + @Excel(name = "文件上传时间") + private Date fileUploadDate; + @Excel(name = "备注") + private String remark; + @Excel(name = "类型") + private Integer type; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/mapper/PublicPolicyAdvocacyMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/mapper/PublicPolicyAdvocacyMapper.java new file mode 100644 index 0000000..81e8154 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/mapper/PublicPolicyAdvocacyMapper.java @@ -0,0 +1,22 @@ +package com.thing.publicorg.policyadvocacy.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.policyadvocacy.dto.PublicPolicyAdvocacyDTO; +import com.thing.publicorg.policyadvocacy.entity.PublicPolicyAdvocacyEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 政策宣传 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Mapper +public interface PublicPolicyAdvocacyMapper extends PowerBaseMapper { + + List getListData(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/PublicPolicyAdvocacyService.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/PublicPolicyAdvocacyService.java new file mode 100644 index 0000000..e1bdb3e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/PublicPolicyAdvocacyService.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.policyadvocacy.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.policyadvocacy.dto.PublicPolicyAdvocacyDTO; +import com.thing.publicorg.policyadvocacy.entity.PublicPolicyAdvocacyEntity; + +import java.util.Map; + + +/** + * 政策宣传 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +public interface PublicPolicyAdvocacyService extends IBaseService { + + PageData page(Map params); + + PageData pageList(Map params); + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/impl/PublicPolicyAdvocacyServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/impl/PublicPolicyAdvocacyServiceImpl.java new file mode 100644 index 0000000..717ac1d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/policyadvocacy/service/impl/PublicPolicyAdvocacyServiceImpl.java @@ -0,0 +1,61 @@ +package com.thing.publicorg.policyadvocacy.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.policyadvocacy.dto.PublicPolicyAdvocacyDTO; +import com.thing.publicorg.policyadvocacy.entity.PublicPolicyAdvocacyEntity; +import com.thing.publicorg.policyadvocacy.mapper.PublicPolicyAdvocacyMapper; +import com.thing.publicorg.policyadvocacy.service.PublicPolicyAdvocacyService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 政策宣传 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Service +public class PublicPolicyAdvocacyServiceImpl extends BaseServiceImpl implements PublicPolicyAdvocacyService { + + @Autowired + private PublicPolicyAdvocacyMapper publicPolicyAdvocacyMapper; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page pages = mapper.paginate( + getPage(params), + buildOrderWrapper(params, "create_date", false) + ); + if (ObjectUtil.isNotEmpty(params.get("endTime"))){ + params.put("endTime", DateTimeUtils.addDateDays(DateTimeUtils.parse(params.get("endTime").toString(),DateTimeUtils.DATE_PATTERN_STR),1)); + } + List policyAdvocacyDTOS = mapper.getListData(params); + return new PageData<>(policyAdvocacyDTOS, pages.getTotalRow()); + } + + @Override + public PageData pageList(Map params) { + params.put("tenant_code",TenantContext.getTenantCode(SecurityUser.getUser())); + List listData = publicPolicyAdvocacyMapper.getListData(params); + List publicFinancialDTOS = startPage(listData, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); + return new PageData<>(publicFinancialDTOS,listData.size()); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/controller/PublicDiagnosisEnergySavingController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/controller/PublicDiagnosisEnergySavingController.java new file mode 100644 index 0000000..9ddbbe8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/controller/PublicDiagnosisEnergySavingController.java @@ -0,0 +1,108 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.publicDiagnosisEnergySaving.dto.PublicDiagnosisEnergySavingDTO; +import com.thing.publicorg.publicDiagnosisEnergySaving.service.PublicDiagnosisEnergySavingService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 节能诊断 +* +* @author system system@lrd.com +* @since 5.1 2024-01-25 +*/ +@RestController +@RequestMapping("v2/publicorg/publicdiagnosisenergysaving") +@Tag(name="节能诊断") +@RequiredArgsConstructor +public class PublicDiagnosisEnergySavingController { + + private final PublicDiagnosisEnergySavingService publicDiagnosisEnergySavingService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="企业名称"), + @Parameter(name = "startTime",description ="开始时间 格式2023-08-01 00:00:00"), + @Parameter(name = "endTime",description ="结束时间 格式2023-08-02 00:00:00"), + @Parameter(name = "type",description ="类型 0-区域企业 1-管理端"), + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = publicDiagnosisEnergySavingService.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + PublicDiagnosisEnergySavingDTO data = publicDiagnosisEnergySavingService.getByIdAs(id, PublicDiagnosisEnergySavingDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody PublicDiagnosisEnergySavingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + publicDiagnosisEnergySavingService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody PublicDiagnosisEnergySavingDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + publicDiagnosisEnergySavingService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + publicDiagnosisEnergySavingService.batchDelete(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = publicDiagnosisEnergySavingService.listAs(params, PublicDiagnosisEnergySavingDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "节能诊断", list, PublicDiagnosisEnergySavingExcel.class); + *} + */ + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/dto/PublicDiagnosisEnergySavingDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/dto/PublicDiagnosisEnergySavingDTO.java new file mode 100644 index 0000000..00aa7c1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/dto/PublicDiagnosisEnergySavingDTO.java @@ -0,0 +1,51 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 节能诊断 +* +* @author system system@lrd.com +* @since 5.1 2024-01-25 +*/ +@Data +@Schema(description = "节能诊断") +public class PublicDiagnosisEnergySavingDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "地址") + private String address; + @Schema(description = "联系方式") + private String phone; + @Schema(description = "描述") + private String description; + @Schema(description = "类型 0-区域企业 1-管理端") + private Integer type; + @Schema(description = "企业名称") + private String name; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/entity/PublicDiagnosisEnergySavingEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/entity/PublicDiagnosisEnergySavingEntity.java new file mode 100644 index 0000000..bc806f1 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/entity/PublicDiagnosisEnergySavingEntity.java @@ -0,0 +1,73 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.entity; + + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 节能诊断 + * + * @author system system@lrd.com + * @since 5.1 2024-01-25 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("public_diagnosis_energy_saving") +public class PublicDiagnosisEnergySavingEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @Id + private Long id; + + /** + * 企业名称 + */ + private String name; + /** + * 地址 + */ + private String address; + /** + * 联系方式 + */ + private String phone; + /** + * 描述 + */ + private String description; + /** + * 类型 0-区域企业 1-管理端 + */ + private Integer type; + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Date createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Date updateDate; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/mapper/PublicDiagnosisEnergySavingMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/mapper/PublicDiagnosisEnergySavingMapper.java new file mode 100644 index 0000000..77c8027 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/mapper/PublicDiagnosisEnergySavingMapper.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.publicDiagnosisEnergySaving.dto.PublicDiagnosisEnergySavingDTO; +import com.thing.publicorg.publicDiagnosisEnergySaving.entity.PublicDiagnosisEnergySavingEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 节能诊断 +* +* @author system system@lrd.com +* @since 5.1 2024-01-25 +*/ +@Mapper +public interface PublicDiagnosisEnergySavingMapper extends PowerBaseMapper { + + List pageList(Map params); + + List pageLists(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/PublicDiagnosisEnergySavingService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/PublicDiagnosisEnergySavingService.java new file mode 100644 index 0000000..6cfa5ac --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/PublicDiagnosisEnergySavingService.java @@ -0,0 +1,20 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.publicDiagnosisEnergySaving.dto.PublicDiagnosisEnergySavingDTO; +import com.thing.publicorg.publicDiagnosisEnergySaving.entity.PublicDiagnosisEnergySavingEntity; + +import java.util.Map; + +/** + * 节能诊断 + * + * @author system system@lrd.com + * @since 5.1 2024-01-25 + */ +public interface PublicDiagnosisEnergySavingService extends IBaseService { + + PageData pageList(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/impl/PublicDiagnosisEnergySavingServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/impl/PublicDiagnosisEnergySavingServiceImpl.java new file mode 100644 index 0000000..16246d2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicDiagnosisEnergySaving/service/impl/PublicDiagnosisEnergySavingServiceImpl.java @@ -0,0 +1,48 @@ +package com.thing.publicorg.publicDiagnosisEnergySaving.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.publicorg.financial.dto.PublicFinancialDTO; +import com.thing.publicorg.publicDiagnosisEnergySaving.dto.PublicDiagnosisEnergySavingDTO; +import com.thing.publicorg.publicDiagnosisEnergySaving.entity.PublicDiagnosisEnergySavingEntity; +import com.thing.publicorg.publicDiagnosisEnergySaving.mapper.PublicDiagnosisEnergySavingMapper; +import com.thing.publicorg.publicDiagnosisEnergySaving.service.PublicDiagnosisEnergySavingService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 节能诊断 + * + * @author system system@lrd.com + * @since 5.1 2024-01-25 + */ +@Service +public class PublicDiagnosisEnergySavingServiceImpl extends BaseServiceImpl implements PublicDiagnosisEnergySavingService { + + @Autowired + private PublicDiagnosisEnergySavingMapper publicDiagnosisEnergySavingMapper; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + wrapper.eq("type",0); + return wrapper; + } + + + @Override + public PageData pageList(Map params) { + List list = publicDiagnosisEnergySavingMapper.pageList(params); + List page = startPage(list, Integer.parseInt(params.get("page").toString()), Integer.parseInt(params.get("limit").toString())); + return new PageData<>(page,list.size()); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/controller/PublicAlertNoticeController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/controller/PublicAlertNoticeController.java new file mode 100644 index 0000000..02e1e2b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/controller/PublicAlertNoticeController.java @@ -0,0 +1,139 @@ +package com.thing.publicorg.publicalertnotice.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.publicalertnotice.dto.PublicAlertNoticeDTO; +import com.thing.publicorg.publicalertnotice.excel.PublicAlertNoticeExcel; +import com.thing.publicorg.publicalertnotice.service.PublicAlertNoticeService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Date; +import java.util.List; +import java.util.Map; + + +/** +* 阈值告警提醒 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@RestController +@RequestMapping("publicalertnotice") +@Tag(name="阈值告警提醒") +public class PublicAlertNoticeController { + @Autowired + private PublicAlertNoticeService publicAlertNoticeService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("publicalertnotice:publicalertnotice:page") + public Result> page( @RequestParam Map params){ + PageData page = publicAlertNoticeService.page(params); + return new Result>().ok(page); + } + + + @PostMapping("view/{id}") + @Operation(summary="点击查看通知-租户用") + @LogOperation("点击查看通知-租户用") +// @RequiresPermissions("notice:publicnotice:save") + public Result view(@PathVariable("id") Long id){ + //效验数据 + PublicAlertNoticeDTO data = publicAlertNoticeService.getByIdAs(id,PublicAlertNoticeDTO.class); + data.setView("1"); + publicAlertNoticeService.updateDto(data); + + return new Result(); + } + + @PostMapping("unView/count") + @Operation(summary="未查看通知数量-租户用") + @LogOperation("未查看通知数量-租户用") +// @RequiresPermissions("notice:publicnotice:save") + public Result view(){ + Integer count = publicAlertNoticeService.getUnViewCount(); + + return new Result().ok(count); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("publicalertnotice:publicalertnotice:info") + public Result get(@PathVariable("id") Long id){ + PublicAlertNoticeDTO data = publicAlertNoticeService.getByIdAs(id,PublicAlertNoticeDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("publicalertnotice:publicalertnotice:save") + public Result save(@RequestBody PublicAlertNoticeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setPubDate(new Date()); + dto.setView("0"); + publicAlertNoticeService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("publicalertnotice:publicalertnotice:update") + public Result update(@RequestBody PublicAlertNoticeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicAlertNoticeService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("publicalertnotice:publicalertnotice:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicAlertNoticeService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") +// @RequiresPermissions("publicalertnotice:publicalertnotice:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicAlertNoticeService.listAs(params, PublicAlertNoticeDTO.class); + ExcelUtils.exportExcel(list,null, "阈值告警提醒", PublicAlertNoticeExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/dto/PublicAlertNoticeDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/dto/PublicAlertNoticeDTO.java new file mode 100644 index 0000000..96bb298 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/dto/PublicAlertNoticeDTO.java @@ -0,0 +1,53 @@ +package com.thing.publicorg.publicalertnotice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 阈值告警提醒 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@Data +@Schema( name= "阈值告警提醒") +public class PublicAlertNoticeDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新人") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构id") + private Long deptId; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "发布机构") + private String orgName; + @Schema(description = "发布内容") + private String content; + @Schema(description = "发布时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date pubDate; + @Schema(description = "是否查看 0未查看,1已查看") + private String view; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/entity/PublicAlertNoticeEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/entity/PublicAlertNoticeEntity.java new file mode 100644 index 0000000..6032b93 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/entity/PublicAlertNoticeEntity.java @@ -0,0 +1,41 @@ +package com.thing.publicorg.publicalertnotice.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * 阈值告警提醒 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_alert_notice") +public class PublicAlertNoticeEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + /** + * 发布机构 + */ + private String orgName; + /** + * 发布内容 + */ + private String content; + /** + * 发布时间 + */ + private Date pubDate; + /** + * 是否查看 0未查看,1已查看 + */ + private String view; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/excel/PublicAlertNoticeExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/excel/PublicAlertNoticeExcel.java new file mode 100644 index 0000000..70f8d40 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/excel/PublicAlertNoticeExcel.java @@ -0,0 +1,44 @@ +package com.thing.publicorg.publicalertnotice.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.util.Date; + +/** + * 阈值告警提醒 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicAlertNoticeExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "创建人", index = 1) + private Long creator; + @ExcelProperty(value = "创建时间", index = 2) + private Date createDate; + @ExcelProperty(value = "更新人", index = 3) + private Long updater; + @ExcelProperty(value = "更新时间", index = 4) + private Date updateDate; + @ExcelProperty(value = "机构id", index = 5) + private Long deptId; + @ExcelProperty(value = "租户code", index = 6) + private Long tenantCode; + @ExcelProperty(value = "发布机构", index = 7) + private String orgName; + @ExcelProperty(value = "发布内容", index = 8) + private String content; + @ExcelProperty(value = "发布时间", index = 9) + private Date pubDate; + @ExcelProperty(value = "是否查看 0未查看,1已查看", index = 10) + private String view; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/mapper/PublicAlertNoticeMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/mapper/PublicAlertNoticeMapper.java new file mode 100644 index 0000000..d06f0bc --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/mapper/PublicAlertNoticeMapper.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicalertnotice.mapper; + + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.publicalertnotice.dto.PublicAlertNoticeDTO; +import com.thing.publicorg.publicalertnotice.entity.PublicAlertNoticeEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** +* 阈值告警提醒 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@Mapper +public interface PublicAlertNoticeMapper extends PowerBaseMapper { + + Integer getUnViewCount(@Param("tenantCode") Long tenantCode); + + List getListData(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/PublicAlertNoticeService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/PublicAlertNoticeService.java new file mode 100644 index 0000000..189c73c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/PublicAlertNoticeService.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.publicalertnotice.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.publicalertnotice.dto.PublicAlertNoticeDTO; +import com.thing.publicorg.publicalertnotice.entity.PublicAlertNoticeEntity; + +import java.util.Map; + + +/** + * 阈值告警提醒 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +public interface PublicAlertNoticeService extends IBaseService { + + Integer getUnViewCount(); + + PageData page(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/impl/PublicAlertNoticeServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/impl/PublicAlertNoticeServiceImpl.java new file mode 100644 index 0000000..2401882 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicalertnotice/service/impl/PublicAlertNoticeServiceImpl.java @@ -0,0 +1,51 @@ +package com.thing.publicorg.publicalertnotice.service.impl; + + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.publicalertnotice.dto.PublicAlertNoticeDTO; +import com.thing.publicorg.publicalertnotice.entity.PublicAlertNoticeEntity; +import com.thing.publicorg.publicalertnotice.mapper.PublicAlertNoticeMapper; +import com.thing.publicorg.publicalertnotice.service.PublicAlertNoticeService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 阈值告警提醒 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Service +public class PublicAlertNoticeServiceImpl extends BaseServiceImpl implements PublicAlertNoticeService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + @Override + public PageData page(Map params) { + Page pages = mapper.paginate( + getPage(params), + buildOrderWrapper(params, "create_date", false) + ); + params.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + List alertNoticeDTOS = mapper.getListData(params); + return new PageData<>(alertNoticeDTOS, pages.getTotalRow()); + } + + @Override + public Integer getUnViewCount() { + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + return mapper.getUnViewCount(tenantCode); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicAppletGovernmentController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicAppletGovernmentController.java new file mode 100644 index 0000000..23a8c0c --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicAppletGovernmentController.java @@ -0,0 +1,104 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicAppletGovernmentService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +/** + * 公共机构看板 + * + * @author xiezw + * @since 2023-08-11 + */ + +@RestController +@RequestMapping("/gov") +@Tag(name = "小程序政府侧") +public class PublicAppletGovernmentController { + + @Autowired + private PublicAppletGovernmentService publicAppletGovernmentService; + + + @GetMapping("getWarningCountRatio/statistics") + @Operation(summary="单位年度用能预警统计-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result getWarningCountRatio( @RequestParam Map params){ + PublicOrgWarningCountRatioDTO result = publicAppletGovernmentService.getWarningCountRatio(params); + return new Result().ok(result); + } + + @GetMapping("orgUnitBuildConsumption") + @Operation(summary="各企业建筑能耗-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "orgName",description ="机构名称"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result> getOrgUnitBuildConsumption( @RequestParam Map params){ + List result = publicAppletGovernmentService.getOrgUnitBuildConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("energy/analyse") + @Operation(summary="各用能同期对比-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result> getEnergyAnalyse( @RequestParam Map params){ + List result = publicAppletGovernmentService.getEnergyAnalyse(params); + return new Result>().ok(result); + } + + @GetMapping("energy/trend") + @Operation(summary="年度各用能用量(电,蒸汽,天然气)-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result> getTotalConsumptionTrend( @RequestParam Map params){ + List result = publicAppletGovernmentService.getTotalConsumptionTrend(params); + return new Result>().ok(result); + } + + @GetMapping("totalConsumption/rank") + @Operation(summary="年度综合能耗机构排行top10-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result> getUnitBuildConsumption( @RequestParam Map params){ + List result = publicAppletGovernmentService.getUnitBuildConsumption(params); + return new Result>().ok(result); + } + + + @GetMapping("totalData") + @Operation(summary="总数据-小程序政府侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "industryCode",description ="行业代码") + }) + public Result getTotalData( @RequestParam Map params){ + PublicTotalDataDTO result = publicAppletGovernmentService.getTotalData(params); + return new Result().ok(result); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicBoardController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicBoardController.java new file mode 100644 index 0000000..501807b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicBoardController.java @@ -0,0 +1,121 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicBoardService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +/** + * 公共机构看板 + * + * @author xiezw + * @since 2023-08-11 + */ + +@RestController +@RequestMapping("/publicBoard") +@Tag(name = "昆山公共机构看板") +public class PublicBoardController { + + @Autowired + private PublicBoardService publicBoardService; + + + + @GetMapping("totalConsumption/trend") + @Operation(summary="总能耗趋势") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getTotalConsumptionTrend( @RequestParam Map params){ + List result = publicBoardService.getTotalConsumptionTrend(params); + return new Result>().ok(result); + } + + @GetMapping("totalCarbon/trend") + @Operation(summary="总碳排趋势") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result getTotalCarbonTrend( @RequestParam Map params){ + PublicTotalCarbonDTO result = publicBoardService.getTotalCarbonTrend(params); + return new Result().ok(result); + } + + @GetMapping("quotaTypeConsumption") + @Operation(summary="定额类型能耗/定额类型碳排") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "type",description ="0能耗,1碳排") + }) + public Result> getQuotaTypeConsumption( @RequestParam Map params){ + List result = publicBoardService.getQuotaTypeConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("totalConsumption/analyse") + @Operation(summary="总能耗同比分析") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result getTotalConsumptionAnalyse( @RequestParam Map params){ + PublicTotalConsumptionAnalyseDTO result = publicBoardService.getTotalConsumptionAnalyse(params); + return new Result().ok(result); + } + + + @GetMapping("energyFlow") + @Operation(summary="能源流向图") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result getEnergyFlow( @RequestParam Map params){ + PublicSubNodeRespDTO result = publicBoardService.getEnergyFlow(params); + return new Result().ok(result); + } + + + @GetMapping("unitBuildConsumption") + @Operation(summary="人均建筑能耗/单位建筑面积能耗") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "type",description ="0人均建筑能耗,1单位建筑面积能耗") + }) + public Result> getUnitBuildConsumption( @RequestParam Map params){ + List result = publicBoardService.getUnitBuildConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("areaEnergyData") + @Operation(summary="各区域能耗及碳排数据") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getAreaEnergyData( @RequestParam Map params){ + List result = publicBoardService.getAreaEnergyData(params); + return new Result>().ok(result); + } + + @GetMapping("totalData") + @Operation(summary="看板总数据") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result getTotalData( @RequestParam Map params){ + PublicTotalDataDTO result = publicBoardService.getTotalData(params); + return new Result().ok(result); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicChargingPileController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicChargingPileController.java new file mode 100644 index 0000000..80f161f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicChargingPileController.java @@ -0,0 +1,70 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.PublicAnnualPowerDTO; +import com.thing.publicorg.publicboard.dto.PublicChargingPileDTO; +import com.thing.publicorg.publicboard.dto.PublicUnitNamePile; +import com.thing.publicorg.publicboard.service.PublicChargingPileService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/charging") +@Tag(name = "充电桩详情") +public class PublicChargingPileController { + + + @Autowired + private PublicChargingPileService publicChargingPileService; + + @GetMapping("pile/Condition") + @Operation(summary="充电桩情况") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getChargingPileCondition( @RequestParam Map params){ + List result = publicChargingPileService.getChargingPileCondition(params); + return new Result>().ok(result); + } + + + @GetMapping("pile/ListUnits") + @Operation(summary="当前地区单位清单") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "unitName",description ="单位名称"), + @Parameter(name = "IndustryCode",description ="行业代码-字典值"), + @Parameter(name = "deptId",description ="单位编号"), + }) + public Result> getChargingPileListUnits( @RequestParam Map params){ + List result = publicChargingPileService.getChargingPileListUnits(params); + return new Result>().ok(result); + } + + + @GetMapping("pile/ChargingPiles") + @Operation(summary="单位-年度充电桩耗电趋势-年度充电桩耗电趋势") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="行业代码-字典值"), + }) + public Result getChargingPiles( @RequestParam Map params){ + PublicAnnualPowerDTO result = publicChargingPileService.getChargingPiles(params); + return new Result().ok(result); + } + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEachUnitKanbanController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEachUnitKanbanController.java new file mode 100644 index 0000000..2f2e328 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEachUnitKanbanController.java @@ -0,0 +1,97 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.PublicAnnualConsumptionRatioDTO; +import com.thing.publicorg.publicboard.dto.PublicHourElectricityUsageTrendsDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyConsumptionDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyIntegratedEnergyUseStructureDTO; +import com.thing.publicorg.publicboard.service.PublicEachUnitKanbanService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/EachUnitKanban") +@Tag(name = "各单位看板首页") +public class PublicEachUnitKanbanController { + + @Autowired + private PublicEachUnitKanbanService publicEachUnitKanbanService; + + @GetMapping("UnitOfTheYear/EnergyConsumption") + @Operation(summary="当年单位-能耗") + @Parameters({ + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间"), + @Parameter(name = "deptId",description ="单位编号"), + @Parameter(name = "type",description ="0-年份 1-月份"), + }) + public Result> getUnitOfTheYearEnergyConsumption( @RequestParam Map params){ + Map result = publicEachUnitKanbanService.getUnitOfTheYearEnergyConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("UnitOfTheYear/MonthlyConsumptionTrend") + @Operation(summary="月用能趋势") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="单位编号-必填"), + @Parameter(name = "key",description ="属性类型"), + }) + public Result> getMonthlyConsumptionTrend( @RequestParam Map params){ + List result = publicEachUnitKanbanService.getMonthlyConsumptionTrend(params); + return new Result>().ok(result); + } + + @GetMapping("UnitOfTheYear/AnnualConsumptionRatio") + @Operation(summary="年用能对比") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="单位编号"), + @Parameter(name = "key",description ="属性类型"), + }) + public Result> getAnnualConsumptionRatio( @RequestParam Map params){ + List result = publicEachUnitKanbanService.getAnnualConsumptionRatio(params); + return new Result>().ok(result); + } + + + @GetMapping("UnitOfTheYear/MonthlyIntegratedEnergyUseStructure") + @Operation(summary="月综合用能结构") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="单位编号"), + }) + public Result> getMonthlyIntegratedEnergyUseStructure( @RequestParam Map params){ + List result = publicEachUnitKanbanService.getMonthlyIntegratedEnergyUseStructure(params); + return new Result>().ok(result); + } + + @GetMapping("UnitOfTheYear/HourElectricityUsageTrends") + @Operation(summary="24小时用电趋势") + @Parameters({ + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间"), + @Parameter(name = "deptId",description ="单位编号"), + @Parameter(name = "key",description ="属性类型"), + }) + public Result getHourElectricityUsageTrends( @RequestParam Map params){ + PublicHourElectricityUsageTrendsDTO result = publicEachUnitKanbanService.getHourElectricityUsageTrends(params); + return new Result().ok(result); + } + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEnergyWarningController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEnergyWarningController.java new file mode 100644 index 0000000..3554a4b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicEnergyWarningController.java @@ -0,0 +1,120 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicEnergyWarningService; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + +/** + * 用能预警 + * + * @author xiezw + * @since 2023-08-11 + */ + +@RestController +@RequestMapping("/energy/warning") +@Tag(name = "昆山公共机构用能预警") +public class PublicEnergyWarningController { + + @Autowired + private PublicEnergyWarningService publicEnergyWarningService; + + + + @PostMapping("page") + @Operation(summary="用能预警") + public Result> getEnergyWarningPageList(@RequestBody PublicEnergyWarningRequestDTO dto){ + PageData result = publicEnergyWarningService.getEnergyWarningPageList(dto); + return new Result>().ok(result); + } + + @GetMapping("getWarningCount") + @Operation(summary="获取告警数量") + @Parameters({ + @Parameter(name = "month",description ="年份,格式2023-08") + }) + public Result getWarningCount( @RequestParam Map params){ + PublicOrgWarningCountDTO result = publicEnergyWarningService.getWarningCount(params); + return new Result().ok(result); + } + + @PostMapping("export") + @Operation(summary="用能预警导出") + public void getEnergyWarningPageList(@RequestBody PublicEnergyWarningRequestDTO dto, HttpServletResponse response) { + List list = publicEnergyWarningService.getEnergyWarningList(dto); + List lits1 = ConvertUtils.sourceToTarget(list, PublicEnergyWarningExcel.class); + ExcelUtils.exportExcel(lits1, "用能预警", "用能预警", PublicEnergyWarningExcel.class, "用能预警.xls", response); + } + + @GetMapping("orgTotalConsumption/analyse") + @Operation(summary="机构总能耗同比分析-pc-小程序政府侧-企业侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "orgId",description ="机构id") + }) + public Result getOrgTotalConsumptionAnalyse( @RequestParam Map params){ + PublicOrgTotalConsumptionAnalyseDTO result = publicEnergyWarningService.getOrgTotalConsumptionAnalyse(params); + return new Result().ok(result); + } + + @GetMapping("orgUnitBuildAreaConsumption") + @Operation(summary="月度单位建筑面积能耗预警-pc-小程序政府侧-企业侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "orgId",description ="机构id") + }) + public Result> getOrgUnitBuildAreaConsumption( @RequestParam Map params){ + List result = publicEnergyWarningService.getOrgUnitBuildAreaConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("orgUnitBuildPersonConsumption") + @Operation(summary="月度人均建筑能耗预警-pc-小程序政府侧-企业侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "orgId",description ="机构id") + }) + public Result> getOrgUnitBuildPersonConsumption( @RequestParam Map params){ + List result = publicEnergyWarningService.getOrgUnitBuildPersonConsumption(params); + return new Result>().ok(result); + } + + @GetMapping("orgYearDataCompare") + @Operation(summary="三年同期总能耗对比分析-pc-小程序政府侧-企业侧") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "orgId",description ="机构id") + }) + public Result getOrgYearDataCompare( @RequestParam Map params){ + PublicOrgYearConsumptionCompareDTO result = publicEnergyWarningService.getOrgYearDataCompare(params); + return new Result().ok(result); + } + + + @GetMapping("orgConsumption/analyse") + @Operation(summary="能耗定额指标分析-pc端") + @Parameters({ + @Parameter(name = "dateType",description ="日期类型 0年 1月"), + @Parameter(name = "date",description ="年格式:2023,日期类型为年不可选择时间,只传当前年份,月格式--2023") + }) + public Result getOrgConsumptionAnalyse( @RequestParam Map params){ + PublicOrgConsumptionAnalyseDataDTO result = publicEnergyWarningService.getOrgConsumptionAnalyse(params); + return new Result().ok(result); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicMonitoringController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicMonitoringController.java new file mode 100644 index 0000000..1d6ba36 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicMonitoringController.java @@ -0,0 +1,97 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicMonitoringService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +/** + * 看板 + * + * @author wzf 56583086@qq.com + * @since 1.0.0 2020-09-06 + */ + +@RestController +@RequestMapping("/monitoring") +@Tag(name = "监控详情") +public class PublicMonitoringController { + + + @Autowired + private PublicMonitoringService monitoringService; + + @GetMapping("details/Energy") + @Operation(summary="行业总能耗-建筑能耗") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getDetailsEnergy( @RequestParam Map params){ + List result = monitoringService.getDetailsEnergy(params); + return new Result>().ok(result); + } + + @GetMapping("details/units") + @Operation(summary="单位-总能耗") + @Parameters({ + @Parameter(name = "unitsName",description ="单位名称"), + @Parameter(name = "IndustryCode",description ="行业代码-字典值"), + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getDetailsUnits( @RequestParam Map params){ + List result = monitoringService.getDetailsUnits(params); + return new Result>().ok(result); + } + + + @GetMapping("details/consumption") + @Operation(summary="单位-平均能耗") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "unitsName",description ="单位名称"), + }) + public Result getConsumption( @RequestParam Map params){ + PublicCentralizedOfficeAreaDTO result = monitoringService.getConsumption(params); + return new Result().ok(result); + } + + @GetMapping("details/TypeEnergyUse") + @Operation(summary="用能种类") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + }) + public Result> getTypeEnergyUse( @RequestParam Map params){ + List result = monitoringService.TypeEnergyUse(params); + return new Result>().ok(result); + } + + @GetMapping("details/TypeEnergyUseYear") + @Operation(summary="用能种类-年度") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="机构编号"), + @Parameter(name = "key",description ="属性"), + }) + public Result> TypeEnergyUseYear( @RequestParam Map params){ + List result = monitoringService.TypeEnergyUseYear(params); + return new Result>().ok(result); + } + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicPhotovoltaicController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicPhotovoltaicController.java new file mode 100644 index 0000000..af06f80 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicPhotovoltaicController.java @@ -0,0 +1,70 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.PublicElectricityUnitDTO; +import com.thing.publicorg.publicboard.dto.PublicInstalledCapacityDTO; +import com.thing.publicorg.publicboard.dto.PublicPhotovoltaicGenerationDTO; +import com.thing.publicorg.publicboard.service.PublicPhotovoltaicService; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("/photovoltail") +@Tag(name = "光伏详情") +public class PublicPhotovoltaicController { + + + @Autowired + private PublicPhotovoltaicService publicPhotovoltaicService; + + @GetMapping("photovoltail/Electricity") + @Operation(summary="行业-装机容量-发电量") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023") + }) + public Result> getInstalledCapacityElectricityGeneration( @RequestParam Map params){ + List result = publicPhotovoltaicService.getInstalledCapacityElectricityGeneration(params); + return new Result>().ok(result); + } + + + @GetMapping("photovoltail/ListUnits") + @Operation(summary="当前地区单位清单") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "unitName",description ="单位名称"), + @Parameter(name = "IndustryCode",description ="行业代码-字典值"), + @Parameter(name = "deptId",description ="单位编号"), + }) + public Result> getListUnits( @RequestParam Map params){ + List result = publicPhotovoltaicService.getListUnits(params); + return new Result>().ok(result); + } + + + @GetMapping("photovoltail/Generation") + @Operation(summary="单位-年度光伏发电趋势-光伏发电同比分析") + @Parameters({ + @Parameter(name = "year",description ="年份,格式2023"), + @Parameter(name = "deptId",description ="单位编号"), + }) + public Result getAnnualPhotovoltaicGeneration( @RequestParam Map params){ + PublicPhotovoltaicGenerationDTO result = publicPhotovoltaicService.getAnnualPhotovoltaicGeneration(params); + return new Result().ok(result); + } + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicReportController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicReportController.java new file mode 100644 index 0000000..3da006f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/controller/PublicReportController.java @@ -0,0 +1,99 @@ +package com.thing.publicorg.publicboard.controller; + + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import com.thing.publicorg.publicboard.dto.PublicReportDataDTO; +import com.thing.publicorg.publicboard.dto.PublicReportExcel; +import com.thing.publicorg.publicboard.service.PublicReportService; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.List; +import java.util.Map; + +/** + * 报表下载 + * + * @author xiezw + * @since 2023-08-11 + */ + +@RestController +@RequestMapping("/report") +@Tag(name = "昆山公共机构报表下载") +public class PublicReportController { + + @Autowired + private PublicReportService publicReportService; + + + + @GetMapping("page") + @Operation(summary="用能报表") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "quotaType",description ="定额类型") , + @Parameter(name = "industryCode",description ="行业代码") , + @Parameter(name = "orgName",description ="机构名称"), + @Parameter(name = "dateType",description ="日期类型 0年 1月"), + @Parameter(name = "date",description ="年格式:2023,月格式2023-08") + }) + public Result> getEnergyReportPageList( @RequestParam Map params){ + PageData result = publicReportService.getEnergyReportPageList(params); + return new Result>().ok(result); + } + + + @GetMapping("export") + @Operation(summary="用能报表导出") + @Parameters({ + @Parameter(name = "industryCode",description ="行业代码") , + @Parameter(name = "orgName",description ="机构名称"), + @Parameter(name = "dateType",description ="日期类型 0年 1月"), + @Parameter(name = "date",description ="年格式:2023,月格式2023-08") + }) + public void getEnergyReportExport( @RequestParam Map params, HttpServletResponse response){ + List list = publicReportService.getEnergyReportList(params); + List list1 = ConvertUtils.sourceToTarget(list, PublicReportExcel.class); + ExcelUtils.exportExcel(list1, "用能报表", "用能报表", PublicReportExcel.class, "用能报表.xls", response); + } + + @GetMapping("orgEnergyData") + @Operation(summary="能耗指标报表-pc端") + @Parameters({ + @Parameter(name = "dateType",description ="日期类型 0年 1月"), + @Parameter(name = "date",description ="年格式:2023,月格式2023-08") + }) + public Result> getEnergyReportList( @RequestParam Map params){ + List result = publicReportService.getOrgEnergyReportList(params); + return new Result>().ok(result); + } + + @GetMapping("orgEnergyData/export") + @Operation(summary="能耗指标报表导出-pc端") + @Parameters({ + @Parameter(name = "dateType",description ="日期类型 0年 1月"), + @Parameter(name = "date",description ="年格式:2023,月格式2023-08") + }) + public void getEnergyReportExportList( @RequestParam Map params, HttpServletResponse response){ + List result = publicReportService.getOrgEnergyReportList(params); + List list1 = ConvertUtils.sourceToTarget(result, PublicReportExcel.class); + ExcelUtils.exportExcel(list1, "能耗指标报表", "能耗指标报表", PublicReportExcel.class, "能耗指标报表.xls", response); + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/BasePageData.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/BasePageData.java new file mode 100644 index 0000000..5577955 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/BasePageData.java @@ -0,0 +1,30 @@ +package com.thing.publicorg.publicboard.dto; +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @author :xiezw + * @date :2023/8/11 10:57 + * @description : + */ +@Data +public class BasePageData { + @Schema(description = "当前页码") + @NotNull(message = "页码不能为空") + private String page; + + + @Schema(description ="每页显示记录数") + @NotNull(message = "每页显示记录数不能为空") + private String limit; + + @Schema(description ="排序字段") + private String orderField; + + @Schema(description ="排序方式") + private String order; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualConsumptionRatioDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualConsumptionRatioDTO.java new file mode 100644 index 0000000..fdd79c8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualConsumptionRatioDTO.java @@ -0,0 +1,25 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +@Data +public class PublicAnnualConsumptionRatioDTO { + + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + + @Schema(description = "当年") + private PublicEnergyConsumptionDTO current; + + + @Schema(description = "去年") + private PublicEnergyConsumptionDTO last; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualPowerDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualPowerDTO.java new file mode 100644 index 0000000..a6f7833 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAnnualPowerDTO.java @@ -0,0 +1,20 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class PublicAnnualPowerDTO { + + @Schema(description = "当年") + private List current; + + + @Schema(description = "去年") + private List last; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAreaConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAreaConsumptionDTO.java new file mode 100644 index 0000000..8317f80 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicAreaConsumptionDTO.java @@ -0,0 +1,65 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicAreaConsumptionDTO { + + @Schema(description = "地区名称") + private String regionName; + @Schema(description = "地区编号") + private Long regionCode; + @Schema(description = "监测单位数量") + private Integer orgCount; + @Schema(description = "总能耗") + private BigDecimal totalConsumption; + @Schema(description = "总碳排") + private BigDecimal totalCarbon; + + @Schema(description = "建筑能耗") + private BigDecimal totalBuildConsumption; + @Schema(description = "建筑碳排") + private BigDecimal totalBuildCarbon; + + public BigDecimal getTotalConsumption() { + if (totalConsumption!=null){ + return totalConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalConsumption; + } + } + + public BigDecimal getTotalCarbon() { + if (totalCarbon!=null){ + return totalCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalCarbon; + } + } + + public BigDecimal getTotalBuildConsumption() { + if (totalBuildConsumption!=null){ + return totalBuildConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalBuildConsumption; + } + } + + public BigDecimal getTotalBuildCarbon() { + if (totalBuildCarbon!=null){ + return totalBuildCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalBuildCarbon; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCapacityGenerationDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCapacityGenerationDTO.java new file mode 100644 index 0000000..9772d0e --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCapacityGenerationDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PublicCapacityGenerationDTO { + + + @Schema(description = "单位名称") + private String unitName; + + @Schema(description = "单位图片") + private String picture; + + @Schema(description = "装机容量") + private BigDecimal installedCapacity; + + @Schema(description = "发电量") + private BigDecimal electricityGeneration; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCentralizedOfficeAreaDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCentralizedOfficeAreaDTO.java new file mode 100644 index 0000000..de54656 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicCentralizedOfficeAreaDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 集中办公区 + */ +@Data +@Schema( name= "单位-集中公办区") +public class PublicCentralizedOfficeAreaDTO extends PublicNonCentralizedOfficeAreaDTO { + + private static final long serialVersionUID = 1L; + + @Schema(description = "总能耗") + private BigDecimal totalEnergy; + + @Schema(description = "总碳排") + private BigDecimal totalCarbonEmission; + + @Schema(description = "数据中心能耗") + private BigDecimal centerEnergy; + + @Schema(description = "食堂能耗") + private BigDecimal canteenEnergy; + + @Schema(description = "数据中心电能使用效率") + private BigDecimal centerPowerEfficiency; + + @Schema(description = "食堂人均能耗") + private BigDecimal perCapitaEnergy; + + + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicChargingPileDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicChargingPileDTO.java new file mode 100644 index 0000000..2814060 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicChargingPileDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicChargingPileDTO { + + @Schema(description = "业务代码字典值") + private String industryCode; + + @Schema(description = "业务代码") + private String industryCodeName; + + @Schema(description = "充电桩数量") + private BigDecimal chargingPileNumber; + + @Schema(description = "耗电量") + private BigDecimal Consumption; + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionAndCarbonDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionAndCarbonDTO.java new file mode 100644 index 0000000..350acec --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionAndCarbonDTO.java @@ -0,0 +1,22 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicConsumptionAndCarbonDTO { + @Schema(description = "能耗") + private BigDecimal consumption; + + @Schema(description = "碳排") + private BigDecimal carbon; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionDTO.java new file mode 100644 index 0000000..c662d7b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicConsumptionDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicConsumptionDTO { + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + + @Schema(description = "数据") + private List datas; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicDataValueDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicDataValueDTO.java new file mode 100644 index 0000000..a92375b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicDataValueDTO.java @@ -0,0 +1,19 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicDataValueDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicElectricityUnitDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicElectricityUnitDTO.java new file mode 100644 index 0000000..2a499b3 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicElectricityUnitDTO.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicElectricityUnitDTO { + + @Schema(description = "单位编号") + private Long deptId; + + @Schema(description = "单位名称") + private String unitName; + + @Schema(description = "单位图片") + private String picture; + + @Schema(description = "装机容量") + private BigDecimal installedCapacity; + + @Schema(description = "发电量") + private BigDecimal electricityGeneration; + + @Schema(description = "机构坐标") + private String coordinate; + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumption.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumption.java new file mode 100644 index 0000000..7e9808d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumption.java @@ -0,0 +1,22 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicEnergyConsumption { + + @Schema(description = "建筑面积能耗") + private List floorArea; + + @Schema(description = "约束值") + private BigDecimal constraintValue; + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionAnalyseDTO.java new file mode 100644 index 0000000..4008da6 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionAnalyseDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicEnergyConsumptionAnalyseDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "同比") + private BigDecimal yoy; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + + public BigDecimal getYoy() { + if (yoy!=null){ + return yoy.setScale(2, RoundingMode.HALF_UP); + }else{ + return yoy; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionDTO.java new file mode 100644 index 0000000..0dc8e7f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyConsumptionDTO.java @@ -0,0 +1,31 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicEnergyConsumptionDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyDTO.java new file mode 100644 index 0000000..ed84296 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyDTO.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicEnergyDTO { + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + + @Schema(description = "数据") + private List currentDatas; + + @Schema(description = "数据") + private List lastDatas; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarning1Excel.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarning1Excel.java new file mode 100644 index 0000000..e70617d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarning1Excel.java @@ -0,0 +1,47 @@ +package com.thing.publicorg.publicboard.dto; + + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 用能预警导出 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicEnergyWarning1Excel { + + @Excel(name = "机构名称") + private String orgName; + @Excel(name = "定额类型") + private String quotaTypeName; + @Excel(name = "行业名称") + private String industryName; + + @Excel(name = "总能耗") + private BigDecimal totalConsumption; + @Excel(name = "建筑能耗") + private BigDecimal totalBuildConsumption; + + @Excel(name = "单位建筑面积能耗约束值") + private BigDecimal unitBuildAreaLimitValue; + @Excel(name = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + + @Excel(name = "人均建筑能耗约束值") + private BigDecimal unitBuildPersonLimitValue; + @Excel(name = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningDataDTO.java new file mode 100644 index 0000000..6ae4cd8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningDataDTO.java @@ -0,0 +1,163 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicEnergyWarningDataDTO{ + + @Schema(description = "机构id") + private Long id; + @Schema(description = "租户code") + private Long tenantCode; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "定额类型名称") + private String quotaTypeName; + @Schema(description = "行业代码") + private String industryCode; + @Schema(description = "行业名称") + private String industryName; + + @Schema(description = "总能耗") + private BigDecimal totalConsumption; + @Schema(description = "建筑能耗") + private BigDecimal totalBuildConsumption; + @Schema(description = "数据中心能耗") + private BigDecimal dataCenterConsumption; + @Schema(description = "食堂能耗") + private BigDecimal canteenConsumption; + + @Schema(description = "单位建筑面积能耗约束值") + private BigDecimal unitBuildAreaLimitValue; + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + @Schema(description = "单位建筑面积能耗是否超限 false未超限 true超限") + private Boolean buildAreaUpLimit=false; + @Schema(description = "人均建筑能耗约束值") + private BigDecimal unitBuildPersonLimitValue; + @Schema(description = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + @Schema(description = "人均建筑能耗是否超限 false未超限 true超限") + private Boolean buildUpPersonLimit=false; + + @Schema(description = "数据中心电能使用率约束值") + private BigDecimal unitDataCenterLimitValue; + @Schema(description = "数据中心电能使用率") + private BigDecimal unitDataCenterValue; + @Schema(description = "数据中心电能使用率是否超限 false未超限 true超限") + private Boolean dataCenterUpLimit =false; + @Schema(description = "食堂人均能耗约束值") + private BigDecimal unitCanteenPersonLimitValue; + @Schema(description = "食堂人均能耗") + private BigDecimal unitCanteenPersonValue; + @Schema(description = "食堂人均能耗是否超限 false未超限 true超限") + private Boolean canteenPersonUpLimit=false; + + public BigDecimal getTotalConsumption() { + if (totalConsumption!=null){ + return totalConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalConsumption; + } + } + + public BigDecimal getTotalBuildConsumption() { + if (totalBuildConsumption!=null){ + return totalBuildConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalBuildConsumption; + } + } + + public BigDecimal getDataCenterConsumption() { + if (dataCenterConsumption!=null){ + return dataCenterConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return dataCenterConsumption; + } + } + + public BigDecimal getCanteenConsumption() { + if (canteenConsumption!=null){ + return canteenConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return canteenConsumption; + } + } + + public BigDecimal getUnitBuildAreaLimitValue() { + if (unitBuildAreaLimitValue!=null){ + return unitBuildAreaLimitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaLimitValue; + } + } + + public BigDecimal getUnitBuildAreaValue() { + if (unitBuildAreaValue!=null){ + return unitBuildAreaValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaValue; + } + } + + public BigDecimal getUnitBuildPersonLimitValue() { + if (unitBuildPersonLimitValue!=null){ + return unitBuildPersonLimitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonLimitValue; + } + } + + public BigDecimal getUnitBuildPersonValue() { + if (unitBuildPersonValue!=null){ + return unitBuildPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonValue; + } + } + + public BigDecimal getUnitDataCenterLimitValue() { + if (unitDataCenterLimitValue!=null){ + return unitDataCenterLimitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitDataCenterLimitValue; + } + } + + public BigDecimal getUnitDataCenterValue() { + if (unitDataCenterValue!=null){ + return unitDataCenterValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitDataCenterValue; + } + } + + public BigDecimal getUnitCanteenPersonLimitValue() { + if (unitCanteenPersonLimitValue!=null){ + return unitCanteenPersonLimitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitCanteenPersonLimitValue; + } + } + + public BigDecimal getUnitCanteenPersonValue() { + if (unitCanteenPersonValue!=null){ + return unitCanteenPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitCanteenPersonValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningExcel.java new file mode 100644 index 0000000..61de26f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningExcel.java @@ -0,0 +1,60 @@ +package com.thing.publicorg.publicboard.dto; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 用能预警导出 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicEnergyWarningExcel { + + @Excel(name = "机构名称") + private String orgName; + @Excel(name = "定额类型") + private String quotaTypeName; + @Excel(name = "行业名称") + private String industryName; + + @Excel(name = "总能耗") + private BigDecimal totalConsumption; + @Excel(name = "建筑能耗") + private BigDecimal totalBuildConsumption; + @Excel(name = "数据中心能耗") + private BigDecimal dataCenterConsumption; + @Excel(name = "食堂能耗") + private BigDecimal canteenConsumption; + + @Excel(name = "单位建筑面积能耗约束值") + private BigDecimal unitBuildAreaLimitValue; + @Excel(name = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + + @Excel(name = "人均建筑能耗约束值") + private BigDecimal unitBuildPersonLimitValue; + @Excel(name = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + + + @Excel(name = "数据中心电能使用率约束值") + private BigDecimal unitDataCenterLimitValue; + @Excel(name = "数据中心电能使用率") + private BigDecimal unitDataCenterValue; + + @Excel(name = "食堂人均能耗约束值") + private BigDecimal unitCanteenPersonLimitValue; + @Excel(name = "食堂人均能耗") + private BigDecimal unitCanteenPersonValue; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningRequestDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningRequestDTO.java new file mode 100644 index 0000000..7a1ad33 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicEnergyWarningRequestDTO.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicEnergyWarningRequestDTO extends BasePageData{ + +// @Schema(description = "是否集中办公区 0非集中办公区 1集中办公区 默认传0") +// private String orgType; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "行业代码") + private String industryCode; + + @Schema(description = "时间类型 0年 1月 ----当点击页面上方 年或月超限/安全单位,类型按年月区分对应传参") + private String dateType; + @Schema(description = "年格式2023,月份格式2023-08,年-默认当前年,月默认当前月,----当点击页面上方 年或月超限/安全单位,默认当前年或月") + private String date; + + @Schema(description = "机构idList,点击页面上方超限/安全单位 传入该参") + private List id; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicHourElectricityUsageTrendsDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicHourElectricityUsageTrendsDTO.java new file mode 100644 index 0000000..bc2dfeb --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicHourElectricityUsageTrendsDTO.java @@ -0,0 +1,15 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicHourElectricityUsageTrendsDTO { + + @Schema(description = "24小时实时数据") + private List HourElectricity; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicInstalledCapacityDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicInstalledCapacityDTO.java new file mode 100644 index 0000000..acf9119 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicInstalledCapacityDTO.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema( name= "业务-装机容量-发电量") +public class PublicInstalledCapacityDTO { + + @Schema(description = "业务代码字典值") + private String industryCode; + + @Schema(description = "业务代码") + private String industryCodeName; + + @Schema(description = "装机容量") + private BigDecimal installedCapacity; + + @Schema(description = "发电量") + private BigDecimal electricityGeneration; + +// @Schema(description = "发电总量") +// private BigDecimal TotalPowerGeneration; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicLinkDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicLinkDTO.java new file mode 100644 index 0000000..3d0721d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicLinkDTO.java @@ -0,0 +1,32 @@ +package com.thing.publicorg.publicboard.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * @author xzw + */ +@Data +public class PublicLinkDTO implements Serializable { + + private static final long serialVersionUID = -8762182832474042437L; + + private String source; + + private String target; + + private BigDecimal value; + private String unit; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonitorDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonitorDTO.java new file mode 100644 index 0000000..a28ed13 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonitorDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema( name= "行业总能耗-建筑能耗") +public class PublicMonitorDTO { + + @Schema(description = "业务代码-字典值") + private String industryCode; + + @Schema(description = "业务代码-名称") + private String industryCodeName; + + @Schema(description = "总能耗") + private BigDecimal totalEnergy; + + @Schema(description = "建筑能耗") + private BigDecimal buildingEnergy; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyConsumptionDTO.java new file mode 100644 index 0000000..f37e8ee --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyConsumptionDTO.java @@ -0,0 +1,22 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicMonthlyConsumptionDTO { + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + + @Schema(description = "每天用量") + private List DailyElectricityConsumption; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyIntegratedEnergyUseStructureDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyIntegratedEnergyUseStructureDTO.java new file mode 100644 index 0000000..173f2ef --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicMonthlyIntegratedEnergyUseStructureDTO.java @@ -0,0 +1,28 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicMonthlyIntegratedEnergyUseStructureDTO { + + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + + @Schema(description = "数据") + private BigDecimal values; + + @Schema(description = "占比情况") + private BigDecimal ProportionSituation; + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNodeDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNodeDTO.java new file mode 100644 index 0000000..33735f3 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNodeDTO.java @@ -0,0 +1,18 @@ +package com.thing.publicorg.publicboard.dto; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author xzw + */ +@Data +public class PublicNodeDTO implements Serializable { + + private static final long serialVersionUID = 2846662403940590185L; + + private String name; + + private String id; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNonCentralizedOfficeAreaDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNonCentralizedOfficeAreaDTO.java new file mode 100644 index 0000000..26c9d99 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicNonCentralizedOfficeAreaDTO.java @@ -0,0 +1,49 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 非集中办公区 + */ +@Data +@Schema( name= "单位-非集中办公区") +public class PublicNonCentralizedOfficeAreaDTO { + + private static final long serialVersionUID = 1L; + + @Schema(description = "单位名称") + private String unitsName; + + @Schema(description = "机构展示图") + private String mechanismDisplay; + + @Schema(description = "建筑能耗") + private BigDecimal buildingEnergy; + + @Schema(description = "建筑碳排") + private BigDecimal carbonEmission; + +// @Schema(description = "超月次数") +// private BigDecimal FrequencyPassingMoon; + + @Schema(description = "单位建筑面积能耗") + private BigDecimal buildingAreaEnergy; + + @Schema(description = "人均建筑能耗") + private BigDecimal capitaBuildingEnergy; + + @Schema(description = "月度单位建筑面积能耗") + private BigDecimal monthlyUnitBuildingAreaEnergy; + + @Schema(description = "月度人均建筑能耗") + private BigDecimal monthlyPerBuildingAreaEnergy; + + + + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDTO.java new file mode 100644 index 0000000..969b056 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgConsumptionAnalyseDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "月约束值") + private BigDecimal limitValue; + + public BigDecimal getValue() { + if (value!=null){ + return value.setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + + public BigDecimal getLimitValue() { + if (limitValue!=null){ + return limitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return limitValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDataDTO.java new file mode 100644 index 0000000..8713d37 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgConsumptionAnalyseDataDTO.java @@ -0,0 +1,29 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgConsumptionAnalyseDataDTO { + + + @Schema(description = "单位建筑面积能耗") + private List buildAreaConsumption; + + @Schema(description = "人均建筑能耗") + private List buildPersonConsumption; + + @Schema(description = "数据中心电能使用效率") + private List dataCenterConsumption; + + @Schema(description = "食堂人均能耗") + private List canteenUserConsumption; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataCenterConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataCenterConsumptionAnalyseDTO.java new file mode 100644 index 0000000..9be0b86 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataCenterConsumptionAnalyseDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgDataCenterConsumptionAnalyseDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "月约束值") + private BigDecimal limitValue; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + + public BigDecimal getLimitValue() { + if (limitValue!=null){ + return limitValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return limitValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataEnergyConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataEnergyConsumptionDTO.java new file mode 100644 index 0000000..03076d8 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDataEnergyConsumptionDTO.java @@ -0,0 +1,31 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgDataEnergyConsumptionDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDetailInfoDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDetailInfoDTO.java new file mode 100644 index 0000000..80f1188 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgDetailInfoDTO.java @@ -0,0 +1,81 @@ +package com.thing.publicorg.publicboard.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.publicorg.orgdetail.dto.PublicOrgDetailDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +/** +* 公共机构 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-01 +*/ +@Data +@Schema( name= "公共机构信息1") +public class PublicOrgDetailInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + @Schema(description = "id") + private Long id; + @Schema(description = "企业id") + private Long deptId; + @Schema(description = "地区id") + private Long regionId; + @Schema(description = "机构地址") + private String address; + @Schema(description = "租户Code") + private Long tenantCode; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @DateTimeFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + @Schema(description = "机构坐标") + private String coordinate; + @Schema(description = "备注") + private String description; + @Schema(description = "机构图片") + private String picture; + @Schema(description = "行业代码") + private String industryCode; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "机构类别归属") + private String orgType; + @Schema(description = "单位负责人") + private String orgLeader; + @Schema(description = "统计负责人") + private String statLeader; + @Schema(description = "统计负责人手机") + private String statLeaderPhone; + @Schema(description = "统计员") + private String statUser; + @Schema(description = "统计员手机") + private String statUserPhone; + @Schema(description = "固定电话") + private String telephone; + @Schema(description = "空调形式") + private String airConditioningType; + @Schema(description = "社会信用代码") + private String corporationCode; + @Schema(description = "地区名称") + private String regionName; + @Schema(description = "地区编号") + private String regionCode; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "明细数据") + private PublicOrgDetailDTO orgDetail; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgEnergyConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgEnergyConsumptionAnalyseDTO.java new file mode 100644 index 0000000..ddb7956 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgEnergyConsumptionAnalyseDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgEnergyConsumptionAnalyseDTO { + + @Schema(description = "日期") + private String date; + + @Schema(description = "值") + private BigDecimal value; + + @Schema(description = "同比") + private BigDecimal yoy; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + + public BigDecimal getYoy() { + if (yoy!=null){ + return yoy.setScale(2, RoundingMode.HALF_UP); + }else{ + return yoy; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgTotalConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgTotalConsumptionAnalyseDTO.java new file mode 100644 index 0000000..762ea82 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgTotalConsumptionAnalyseDTO.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgTotalConsumptionAnalyseDTO { + + @Schema(description = "今年能耗及同比数据") + private List currentYearDatas; + + @Schema(description = "去年能耗数据") + private List lastYearDatas; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountDTO.java new file mode 100644 index 0000000..4e899cb --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountDTO.java @@ -0,0 +1,36 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgWarningCountDTO { + + @Schema(description = "年超限数量") + private Integer yearUpLimitCount; + @Schema(description = "年超限数量企业id") + private List yearUpLimitDeptIds; + + @Schema(description = "年正常数量") + private Integer yearNormalCount; + @Schema(description = "年正常数量企业id") + private List yearNormalDeptIds; + + @Schema(description = "年超限数量") + private Integer monthrUpLimitCount; + @Schema(description = "年超限数量企业id") + private List monthUpLimitDeptIds; + + @Schema(description = "年正常数量") + private Integer monthNormalCount; + @Schema(description = "年正常数量企业id") + private List monthNormalDeptIds; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountRatioDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountRatioDTO.java new file mode 100644 index 0000000..da94b50 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgWarningCountRatioDTO.java @@ -0,0 +1,38 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgWarningCountRatioDTO { + + @Schema(description = "超限企业占比") + private BigDecimal upLimitCountRatio; + @Schema(description = "安全企业占比") + private BigDecimal normalCountRatio; + + public BigDecimal getUpLimitCountRatio() { + if (upLimitCountRatio!=null){ + return upLimitCountRatio.setScale(2, RoundingMode.HALF_UP); + }else{ + return upLimitCountRatio; + } + } + + public BigDecimal getNormalCountRatio() { + if (normalCountRatio!=null){ + return normalCountRatio.setScale(2, RoundingMode.HALF_UP); + }else{ + return normalCountRatio; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgYearConsumptionCompareDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgYearConsumptionCompareDTO.java new file mode 100644 index 0000000..58ff4ee --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicOrgYearConsumptionCompareDTO.java @@ -0,0 +1,54 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicOrgYearConsumptionCompareDTO { + + @Schema(description = "当前年份") + private String currentYear; + @Schema(description = "当前总能耗") + private BigDecimal currentValue; + @Schema(description = "去前年份") + private String lastYear; + @Schema(description = "去年总能耗") + private BigDecimal lastValue; + @Schema(description = "前年年份") + private String beforeLastYear; + @Schema(description = "前年总能耗") + private BigDecimal beforeLastValue; + + public BigDecimal getCurrentValue() { + if (currentValue!=null){ + return currentValue.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return currentValue; + } + } + + public BigDecimal getLastValue() { + if (lastValue!=null){ + return lastValue.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return lastValue; + } + } + + public BigDecimal getBeforeLastValue() { + if (beforeLastValue!=null){ + return beforeLastValue.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return beforeLastValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicPhotovoltaicGenerationDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicPhotovoltaicGenerationDTO.java new file mode 100644 index 0000000..7e1ba66 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicPhotovoltaicGenerationDTO.java @@ -0,0 +1,20 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + +@Data +public class PublicPhotovoltaicGenerationDTO { + + @Schema(description = "当年") + private List current; + + + @Schema(description = "去年") + private List last; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicQuotaTypeDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicQuotaTypeDTO.java new file mode 100644 index 0000000..a8eafe4 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicQuotaTypeDTO.java @@ -0,0 +1,35 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicQuotaTypeDTO { + + @Schema(description = "定额类型") + private String quotaType; + + @Schema(description = "定额类型名称") + private String quotaTypeName; + + @Schema(description = "数据") + private BigDecimal value; + + public BigDecimal getValue() { + if (value!=null){ + return value.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportDataDTO.java new file mode 100644 index 0000000..a702a7d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportDataDTO.java @@ -0,0 +1,321 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicReportDataDTO { + + @Schema(description = "机构id") + private Long id; + @Schema(description = "机构名称") + private String orgName; + @Schema(description = "定额类型") + private String quotaType; + @Schema(description = "定额类型名称") + private String quotaTypeName; + @Schema(description = "行业代码") + private String industryCode; + @Schema(description = "行业名称") + private String industryName; + + @Schema(description = "用地面积") + private BigDecimal landArea; + @Schema(description = "建筑面积") + private BigDecimal buildArea; + @Schema(description = "编制人数") + private Integer headCount; + @Schema(description = "日均用能人数") + private Integer dayUserCount; + @Schema(description = "食堂日均人数") + private Integer canteenUserCount; + @Schema(description = "车辆数量") + private Integer carCount; + @Schema(description = "汽油车数量") + private Integer gasCarCount; + @Schema(description = "新能源车数量") + private Integer greenCarCount; + @Schema(description = "柴油车数量") + private Integer dieselCarCount; + @Schema(description = "太阳能集热器面积") + private BigDecimal solarCollectorArea; + @Schema(description = "充电桩数量") + private Integer chargingStationCount; + @Schema(description = "地热能利用装机容量") + private BigDecimal geothermalEnergyCapacity; + @Schema(description = "太阳能光电装机容量") + private Integer solarCapacity; + + @Schema(description = "总能耗") + private BigDecimal totalConsumption; + @Schema(description = "总碳排") + private BigDecimal totalCarbon; + @Schema(description = "建筑能耗") + private BigDecimal totalBuildConsumption; + @Schema(description = "建筑碳排") + private BigDecimal totalBuildCarbon; + @Schema(description = "食堂能耗") + private BigDecimal canteenConsumption; + @Schema(description = "数据中心能耗") + private BigDecimal dataCenterConsumption; + + @Schema(description = "电用量") + private BigDecimal energyA; + + @Schema(description = "天然气用量") + private BigDecimal energyC; + + @Schema(description = "水用量") + private BigDecimal energyB; + + @Schema(description = "蒸汽用量") + private BigDecimal energyE; + + @Schema(description = "汽油用量") + private BigDecimal energyO; + + @Schema(description = "柴油用量") + private BigDecimal energyU; + + @Schema(description = "液化气用量") + private BigDecimal energyN; + + @Schema(description = "光伏发电量") + private BigDecimal energyPhotovoltaic; + + @Schema(description = "充电桩电量") + private BigDecimal energyChargingStation; + + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + + @Schema(description = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + + @Schema(description = "人均用电量") + private BigDecimal unitEnergyAPersonValue; + + @Schema(description = "人均用水量") + private BigDecimal unitEnergyBPersonValue; + + @Schema(description = "食堂人均能耗") + private BigDecimal unitCanteenPersonValue; + @Schema(description = "数据中心电能使用率") + private BigDecimal unitDataCenterValue; + + @Schema(description = "文件名") + private String fileName; + @Schema(description = "节能报告下载地址") + private String url; + + + public BigDecimal getTotalConsumption() { + if (totalConsumption!=null){ + return totalConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalConsumption; + } + } + + public BigDecimal getLandArea() { + if (landArea!=null){ + return landArea.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return landArea; + } + } + + public BigDecimal getBuildArea() { + if (buildArea!=null){ + return buildArea.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return buildArea; + } + + } + + public BigDecimal getSolarCollectorArea() { + if (solarCollectorArea!=null){ + return solarCollectorArea.setScale(2, RoundingMode.HALF_UP); + }else{ + return solarCollectorArea; + } + } + + public BigDecimal getGeothermalEnergyCapacity() { + if (geothermalEnergyCapacity!=null){ + return geothermalEnergyCapacity.setScale(2, RoundingMode.HALF_UP); + }else{ + return geothermalEnergyCapacity; + } + } + + public BigDecimal getTotalCarbon() { + if (totalCarbon!=null){ + return totalCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalCarbon; + } + } + + public BigDecimal getTotalBuildConsumption() { + if (totalBuildConsumption!=null){ + return totalBuildConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalBuildConsumption; + } + } + + public BigDecimal getTotalBuildCarbon() { + if (totalBuildCarbon!=null){ + return totalBuildCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalBuildCarbon; + } + } + + public BigDecimal getCanteenConsumption() { + if (canteenConsumption!=null){ + return canteenConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return canteenConsumption; + } + } + + public BigDecimal getDataCenterConsumption() { + if (dataCenterConsumption!=null){ + return dataCenterConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return dataCenterConsumption; + } + } + + public BigDecimal getEnergyA() { + if (energyA!=null){ + return energyA.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyA; + } + } + + public BigDecimal getEnergyC() { + if (energyC!=null){ + return energyC.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyC; + } + } + + public BigDecimal getEnergyB() { + if (energyB!=null){ + return energyB.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyB; + } + } + + public BigDecimal getEnergyE() { + if (energyE!=null){ + return energyE.setScale(2, RoundingMode.HALF_UP); + }else{ + return energyE; + } + } + + public BigDecimal getEnergyO() { + if (energyO!=null){ + return energyO.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyO; + } + } + + public BigDecimal getEnergyU() { + if (energyU!=null){ + return energyU.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyU; + } + } + + public BigDecimal getEnergyN() { + if (energyN!=null){ + return energyN.setScale(2, RoundingMode.HALF_UP); + }else{ + return energyN; + } + } + + public BigDecimal getEnergyPhotovoltaic() { + if (energyPhotovoltaic!=null){ + return energyPhotovoltaic.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyPhotovoltaic; + } + } + + public BigDecimal getEnergyChargingStation() { + if (energyChargingStation!=null){ + return energyChargingStation.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return energyChargingStation; + } + } + + public BigDecimal getUnitBuildAreaValue() { + if (unitBuildAreaValue!=null){ + return unitBuildAreaValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaValue; + } + } + + public BigDecimal getUnitBuildPersonValue() { + if (unitBuildPersonValue!=null){ + return unitBuildPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonValue; + } + } + + public BigDecimal getUnitEnergyAPersonValue() { + if (unitEnergyAPersonValue!=null){ + return unitEnergyAPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitEnergyAPersonValue; + } + } + + public BigDecimal getUnitEnergyBPersonValue() { + if (unitEnergyBPersonValue!=null){ + return unitEnergyBPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitEnergyBPersonValue; + } + } + + public BigDecimal getUnitCanteenPersonValue() { + if (unitCanteenPersonValue!=null){ + return unitCanteenPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitCanteenPersonValue; + } + } + + public BigDecimal getUnitDataCenterValue() { + if (unitDataCenterValue!=null){ + return unitDataCenterValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitDataCenterValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportExcel.java new file mode 100644 index 0000000..6707e95 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicReportExcel.java @@ -0,0 +1,114 @@ +package com.thing.publicorg.publicboard.dto; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 用能预警导出 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-01 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicReportExcel { + + @Excel(name = "机构名称") + private String orgName; + @Excel(name = "定额类型") + private String quotaTypeName; + @Excel(name = "行业代码") + private String industryName; + + @Excel(name = "用地面积") + private BigDecimal landArea; + @Excel(name = "建筑面积") + private BigDecimal buildArea; + @Excel(name = "编制人数") + private Integer headCount; + @Excel(name = "日均用能人数") + private Integer dayUserCount; + @Excel(name = "食堂日均人数") + private Integer canteenUserCount; + @Excel(name = "车辆数量") + private Integer carCount; + @Excel(name = "汽油车数量") + private Integer gasCarCount; + @Excel(name = "新能源车数量") + private Integer greenCarCount; + @Excel(name = "柴油车数量") + private Integer dieselCarCount; + @Excel(name = "太阳能集热器面积") + private BigDecimal solarCollectorArea; + @Excel(name = "充电桩数量") + private Integer chargingStationCount; + @Excel(name = "地热能利用装机容量") + private BigDecimal geothermalEnergyCapacity; + @Excel(name = "太阳能光电装机容量") + private Integer solarCapacity; + + @Excel(name = "总能耗") + private BigDecimal totalConsumption; + @Excel(name = "总碳排") + private BigDecimal totalCarbon; + @Excel(name = "建筑能耗") + private BigDecimal totalBuildConsumption; + @Excel(name = "建筑碳排") + private BigDecimal totalBuildCarbon; + @Excel(name = "食堂能耗") + private BigDecimal canteenConsumption; + @Excel(name = "数据中心能耗") + private BigDecimal dataCenterConsumption; + + @Excel(name = "电用量") + private BigDecimal energyA; + + @Excel(name = "天然气用量") + private BigDecimal energyC; + + @Excel(name = "水用量") + private BigDecimal energyB; + + @Excel(name = "蒸汽用量") + private BigDecimal energyE; + + @Excel(name = "汽油用量") + private BigDecimal energyO; + + @Excel(name = "汽油用量") + private BigDecimal energyU; + + @Excel(name = "液化气用量") + private BigDecimal energyN; + + @Excel(name = "光伏发电量") + private BigDecimal energyPhotovoltaic; + + @Excel(name = "充电桩电量") + private BigDecimal energyChargingStation; + + @Excel(name = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + + @Excel(name = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + + @Excel(name = "人均用电量") + private BigDecimal unitEnergyAPersonValue; + + @Excel(name = "人均用水量") + private BigDecimal unitEnergyBPersonValue; + + @Excel(name = "食堂人均能耗") + private BigDecimal unitCanteenPersonValue; + @Excel(name = "数据中心电能使用率") + private BigDecimal unitDataCenterValue; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicSubNodeRespDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicSubNodeRespDTO.java new file mode 100644 index 0000000..03d3ca3 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicSubNodeRespDTO.java @@ -0,0 +1,20 @@ +package com.thing.publicorg.publicboard.dto; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author xzw + */ +@Data +public class PublicSubNodeRespDTO implements Serializable { + + private static final long serialVersionUID = 889248616749990247L; + + private List nodes; + + private List links; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalCarbonDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalCarbonDTO.java new file mode 100644 index 0000000..e9aa49d --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalCarbonDTO.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicTotalCarbonDTO { + + @Schema(description = "今年数据") + private List currentYearDatas; + + @Schema(description = "去年数据") + private List lastYearDatas; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalConsumptionAnalyseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalConsumptionAnalyseDTO.java new file mode 100644 index 0000000..f7871a2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalConsumptionAnalyseDTO.java @@ -0,0 +1,23 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.util.List; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicTotalConsumptionAnalyseDTO { + + @Schema(description = "今年能耗及同比数据") + private List currentYearDatas; + + @Schema(description = "去年能耗数据") + private List lastYearDatas; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalDataDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalDataDTO.java new file mode 100644 index 0000000..faab538 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTotalDataDTO.java @@ -0,0 +1,152 @@ +package com.thing.publicorg.publicboard.dto; + + +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import io.swagger.v3.oas.annotations.media.Schema; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicTotalDataDTO { + + @Schema(description = "总能耗") + private BigDecimal totalConsumption; + + @Schema(description = "总碳排") + private BigDecimal totalCarbon; + + @Schema(description = "建筑总能耗") + private BigDecimal buildConsumption; + + @Schema(description = "建筑总碳排") + private BigDecimal buildCarbon; + + @Schema(description = "食堂能耗") + private BigDecimal canteenConsumption; + + @Schema(description = "数据中心能耗") + private BigDecimal dataCenterConsumption; + + @Schema(description = "充电桩电量") + private BigDecimal chargingStationEnergy; + + @Schema(description = "光伏电量") + private BigDecimal photovoltaicEnergy; + + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuildAreaConsumption; + + @Schema(description = "人均建筑能耗") + private BigDecimal unitBuildPersonConsumption; + + @Schema(description = "单位建筑面积碳排") + private BigDecimal unitBuildAreaCarbon; + + @Schema(description = "人均建筑碳排") + private BigDecimal unitBuildPersonCarbon; + + @Schema(description = "接入单位数量") + private Integer orgCount; + + public BigDecimal getTotalConsumption() { + if (totalConsumption!=null){ + return totalConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalConsumption; + } + } + + public BigDecimal getTotalCarbon() { + if (totalCarbon!=null){ + return totalCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return totalCarbon; + } + } + + public BigDecimal getBuildConsumption() { + if (buildConsumption!=null){ + return buildConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return buildConsumption; + } + } + + public BigDecimal getBuildCarbon() { + if (buildCarbon!=null){ + return buildCarbon.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return buildCarbon; + } + } + + public BigDecimal getCanteenConsumption() { + if (canteenConsumption!=null){ + return canteenConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return canteenConsumption; + } + } + + public BigDecimal getDataCenterConsumption() { + if (dataCenterConsumption!=null){ + return dataCenterConsumption.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return dataCenterConsumption; + } + } + + public BigDecimal getChargingStationEnergy() { + if (chargingStationEnergy!=null){ + return chargingStationEnergy.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return chargingStationEnergy; + } + } + + public BigDecimal getPhotovoltaicEnergy() { + if (photovoltaicEnergy!=null){ + return photovoltaicEnergy.divide(new BigDecimal(10000)).setScale(2, RoundingMode.HALF_UP); + }else{ + return photovoltaicEnergy; + } + } + + public BigDecimal getUnitBuildAreaConsumption() { + if (unitBuildAreaConsumption!=null){ + return unitBuildAreaConsumption.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaConsumption; + } + } + + public BigDecimal getUnitBuildPersonConsumption() { + if (unitBuildPersonConsumption!=null){ + return unitBuildPersonConsumption.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonConsumption; + } + } + + public BigDecimal getUnitBuildAreaCarbon() { + if (unitBuildAreaCarbon!=null){ + return unitBuildAreaCarbon.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaCarbon; + } + } + + public BigDecimal getUnitBuildPersonCarbon() { + if (unitBuildPersonCarbon!=null){ + return unitBuildPersonCarbon.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonCarbon; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeEnergyUseYearDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeEnergyUseYearDTO.java new file mode 100644 index 0000000..42df3ca --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeEnergyUseYearDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.util.List; + +/** + * 用能种类-年度 + */ +@Data +@Schema( name= "用能种类-年度") +public class PublicTypeEnergyUseYearDTO extends PublicTypeUseDTO { + + + + @Schema(description = "年度用能趋势") + private List AnnualEnergyTrend; + + @Schema(description ="年度碳排趋势") + private List AnnualCarbonEmission; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeUseDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeUseDTO.java new file mode 100644 index 0000000..3982d33 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicTypeUseDTO.java @@ -0,0 +1,17 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class PublicTypeUseDTO { + + + @Schema(description = "属性") + private String key; + + @Schema(description = "属性名称") + private String keyName; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitBuildConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitBuildConsumptionDTO.java new file mode 100644 index 0000000..a631c06 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitBuildConsumptionDTO.java @@ -0,0 +1,55 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicUnitBuildConsumptionDTO { + + @Schema(description = "机构id") + private Long orgId; + @Schema(description = "机构名称") + private String orgName; + + @Schema(description = "建筑能耗") + private BigDecimal buildValue; + + @Schema(description = "单位建筑面积能耗") + private BigDecimal unitBuildAreaValue; + + @Schema(description = "人均建筑能耗") + private BigDecimal unitBuildPersonValue; + + public BigDecimal getBuildValue() { + if (buildValue!=null){ + return buildValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return buildValue; + } + } + + public BigDecimal getUnitBuildAreaValue() { + if (unitBuildAreaValue!=null){ + return unitBuildAreaValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildAreaValue; + } + } + + public BigDecimal getUnitBuildPersonValue() { + if (unitBuildPersonValue!=null){ + return unitBuildPersonValue.setScale(2, RoundingMode.HALF_UP); + }else{ + return unitBuildPersonValue; + } + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitConsumptionDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitConsumptionDTO.java new file mode 100644 index 0000000..ca9c560 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitConsumptionDTO.java @@ -0,0 +1,32 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.math.RoundingMode; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class PublicUnitConsumptionDTO { + + @Schema(description = "机构名称") + private String orgName; + + @Schema(description = "数据") + private BigDecimal value; + + public BigDecimal getValue() { + if (value!=null){ + return value.setScale(2, RoundingMode.HALF_UP); + }else{ + return value; + } + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitNamePile.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitNamePile.java new file mode 100644 index 0000000..d266c84 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitNamePile.java @@ -0,0 +1,32 @@ +package com.thing.publicorg.publicboard.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class PublicUnitNamePile { + + + @Schema(description = "单位编号") + private Long deptId; + + @Schema(description = "单位名称") + private String unitName; + + @Schema(description = "单位图片") + private String picture; + + @Schema(description = "充电桩数量") + private BigDecimal chargingPileNumber; + + @Schema(description = "耗电量") + private BigDecimal electricityGeneration; + + @Schema(description = "机构坐标") + private String coordinate; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitOfTheYearDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitOfTheYearDTO.java new file mode 100644 index 0000000..a103c20 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitOfTheYearDTO.java @@ -0,0 +1,42 @@ +package com.thing.publicorg.publicboard.dto; + +import lombok.Data; + +@Data +public class PublicUnitOfTheYearDTO { + +// @Schema(description = "总能耗") +// private BigDecimal TotalEnergyConsumption; +// +// @Schema(description = "总碳排") +// private BigDecimal TotalCarbonEmission; +// +// @Schema(description = "建筑能耗") +// private BigDecimal buildingEnergyYear; +// +// @Schema(description = "建筑碳排") +// private BigDecimal carbonEmissionYear; + +// @Schema(description = "单位建筑面积能耗") +// private BigDecimal buildingAreaEnergy; +// +// @Schema(description = "人均建筑能耗") +// private BigDecimal capitaBuildingEnergy; + +// @Schema(description = "数据中心能耗") +// private BigDecimal centerEnergy; +// +// @Schema(description = "数据中心设备能耗") +// private BigDecimal EnergyConsumptionDataCenterDevices; +// +// @Schema(description = "食堂能耗") +// private BigDecimal canteenEnergy; +// +// @Schema(description = "光伏发电量") +// private BigDecimal electricityGeneration; +// +// @Schema(description = "充电桩耗电量") +// private BigDecimal Consumption; + +// private Map mapUnitString; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitsDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitsDTO.java new file mode 100644 index 0000000..a121242 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/dto/PublicUnitsDTO.java @@ -0,0 +1,26 @@ +package com.thing.publicorg.publicboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema( name= "") +public class PublicUnitsDTO { + + @Schema(description = "单位编号") + private Long deptId; + + @Schema(description = "单位名称") + private String unitsName; + + @Schema(description = "总能耗kgtce") + private BigDecimal totalEnergy; + + @Schema(description = "机构坐标") + private String coordinate; + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicAppletGovernmentService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicAppletGovernmentService.java new file mode 100644 index 0000000..e571a81 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicAppletGovernmentService.java @@ -0,0 +1,29 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.publicorg.publicboard.dto.*; + +import java.util.List; +import java.util.Map; + +/** + * 看板 + * + * @author wzf 56583086@qq.com + * @since 1.0.0 2020-09-27 + */ + +public interface PublicAppletGovernmentService { + + List getTotalConsumptionTrend(Map params); + + List getUnitBuildConsumption(Map params); + + PublicTotalDataDTO getTotalData(Map params); + + List getEnergyAnalyse(Map params); + + List getOrgUnitBuildConsumption(Map params); + + PublicOrgWarningCountRatioDTO getWarningCountRatio(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicBoardService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicBoardService.java new file mode 100644 index 0000000..1172c34 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicBoardService.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.publicorg.publicboard.dto.*; + +import java.util.List; +import java.util.Map; + +/** + * 看板 + * + * @author wzf 56583086@qq.com + * @since 1.0.0 2020-09-27 + */ + +public interface PublicBoardService { + + List getTotalConsumptionTrend(Map params); + + PublicTotalCarbonDTO getTotalCarbonTrend(Map params); + + List getQuotaTypeConsumption(Map params); + + PublicTotalConsumptionAnalyseDTO getTotalConsumptionAnalyse(Map params); + + PublicSubNodeRespDTO getEnergyFlow(Map params); + + List getUnitBuildConsumption(Map params); + + List getAreaEnergyData(Map params); + + PublicTotalDataDTO getTotalData(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicChargingPileService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicChargingPileService.java new file mode 100644 index 0000000..99f7cc3 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicChargingPileService.java @@ -0,0 +1,33 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.publicorg.publicboard.dto.PublicAnnualPowerDTO; +import com.thing.publicorg.publicboard.dto.PublicChargingPileDTO; +import com.thing.publicorg.publicboard.dto.PublicUnitNamePile; + +import java.util.List; +import java.util.Map; + + +public interface PublicChargingPileService { + /** + * 充电桩情况 + * @param params + * @return + */ + List getChargingPileCondition(Map params); + + /** + * 当前地区单位清单 + * @param params + * @return + */ + List getChargingPileListUnits(Map params); + + /** + * 单位-年度充电桩耗电趋势-年度充电桩耗电趋势 + * @param params + * @return + */ + PublicAnnualPowerDTO getChargingPiles(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEachUnitKanbanService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEachUnitKanbanService.java new file mode 100644 index 0000000..4474ba5 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEachUnitKanbanService.java @@ -0,0 +1,50 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.publicorg.publicboard.dto.PublicAnnualConsumptionRatioDTO; +import com.thing.publicorg.publicboard.dto.PublicHourElectricityUsageTrendsDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyConsumptionDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyIntegratedEnergyUseStructureDTO; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + + +public interface PublicEachUnitKanbanService { + + /** + * 当年单位-能耗 + * @param params + * @return + */ + Map getUnitOfTheYearEnergyConsumption(Map params); + + /** + * 月用电趋势 + * @param params + * @return + */ + List getMonthlyConsumptionTrend(Map params); + + /** + * 年用电对比 + * @param params + * @return + */ + List getAnnualConsumptionRatio(Map params); + + /** + * 月综合用能结构 + * @param params + * @return + */ + List getMonthlyIntegratedEnergyUseStructure(Map params); + + /** + * 24小时用电趋势 + * @param params + * @return + */ + PublicHourElectricityUsageTrendsDTO getHourElectricityUsageTrends(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEnergyWarningService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEnergyWarningService.java new file mode 100644 index 0000000..e329958 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicEnergyWarningService.java @@ -0,0 +1,32 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.publicorg.publicboard.dto.*; + +import java.util.List; +import java.util.Map; + + +/** + * @author xzw + */ + +public interface PublicEnergyWarningService { + + PageData getEnergyWarningPageList(PublicEnergyWarningRequestDTO dto); + + List getEnergyWarningList(PublicEnergyWarningRequestDTO dto); + + PublicOrgTotalConsumptionAnalyseDTO getOrgTotalConsumptionAnalyse(Map params); + + List getOrgUnitBuildAreaConsumption(Map params); + + List getOrgUnitBuildPersonConsumption(Map params); + + PublicOrgYearConsumptionCompareDTO getOrgYearDataCompare(Map params); + + PublicOrgWarningCountDTO getWarningCount(Map params); + + PublicOrgConsumptionAnalyseDataDTO getOrgConsumptionAnalyse(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicMonitoringService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicMonitoringService.java new file mode 100644 index 0000000..4540a47 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicMonitoringService.java @@ -0,0 +1,49 @@ +package com.thing.publicorg.publicboard.service; + + +import com.thing.publicorg.publicboard.dto.*; + +import java.util.List; +import java.util.Map; + + +public interface PublicMonitoringService { + + /** + * 行业代码-总能耗-建筑能耗 + * @param params + * @return + */ + List getDetailsEnergy(Map params); + + + /** + * 单位-总能耗 + * @param params + * @return + */ + List getDetailsUnits(Map params); + + /** + * 单位-建筑平均能耗 + * @param params + * @return + */ + PublicCentralizedOfficeAreaDTO getConsumption(Map params); + + /** + * 用能种类 + * @param params + * @return + */ + List TypeEnergyUse(Map params); + + + /** + * 用能种类-年度 + * @param params + * @return + */ + List TypeEnergyUseYear(Map params); + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicPhotovoltaicService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicPhotovoltaicService.java new file mode 100644 index 0000000..87cd4d2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicPhotovoltaicService.java @@ -0,0 +1,37 @@ +package com.thing.publicorg.publicboard.service; + + + + +import com.thing.publicorg.publicboard.dto.PublicElectricityUnitDTO; +import com.thing.publicorg.publicboard.dto.PublicInstalledCapacityDTO; +import com.thing.publicorg.publicboard.dto.PublicPhotovoltaicGenerationDTO; + +import java.util.List; +import java.util.Map; + + +public interface PublicPhotovoltaicService { + + /** + * 行业装机容量-发电量 + * @param params + * @return + */ + List getInstalledCapacityElectricityGeneration(Map params); + + /** + * 当前地区单位清单 + * @param params + * @return + */ + List getListUnits(Map params); + + + /** + * 单位-年度光伏发电趋势-光伏发电同比分析 + * @param params + * @return + */ + PublicPhotovoltaicGenerationDTO getAnnualPhotovoltaicGeneration(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicReportService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicReportService.java new file mode 100644 index 0000000..75ea983 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/PublicReportService.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.publicboard.service; + + + + +import com.thing.common.core.web.response.PageData; +import com.thing.publicorg.publicboard.dto.PublicReportDataDTO; + +import java.util.List; +import java.util.Map; + + +/** + * @author xzw + */ + +public interface PublicReportService { + + PageData getEnergyReportPageList(Map params); + + List getEnergyReportList(Map params); + + List getOrgEnergyReportList(Map params); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicAppletGovernmentServiceServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicAppletGovernmentServiceServiceImpl.java new file mode 100644 index 0000000..42c3e84 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicAppletGovernmentServiceServiceImpl.java @@ -0,0 +1,589 @@ +package com.thing.publicorg.publicboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.biz.service.SysRegionService; +import com.thing.publicorg.org.dto.PublicOrgInfoDTO; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicAppletGovernmentService; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 昆山公共机构看板 + * + * @author xiezw + * @since 2023-08-08 + */ +@Service +public class PublicAppletGovernmentServiceServiceImpl implements PublicAppletGovernmentService { + + @Autowired + private SysParamsService sysParamsService; + + @Autowired + private PublicOrgService publicOrgService; + + + + @Autowired + private SysDictDataService iotDictDataService; + + @Autowired + private SysRegionService sysRegionService; + + @Autowired + private PublicOrgLimitService publicOrgLimitService; + + @Override + public List getTotalConsumptionTrend(Map params) { + List result = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + List months = DateTimeUtils.getMonthList(year); + //获取属性 + String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); + Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); + //获取所有机构 +// List orgs = publicOrgService.getAllOrgInfo(); + String industryCode = null; + if (params.get("industryCode")!=null){ + industryCode = params.get("industryCode").toString(); + } + List deptIds; + if (StringUtils.isBlank(industryCode)){ + deptIds = publicOrgService.getAllDeptIds(); + }else{ + deptIds = publicOrgService.getAllDeptIdsByIndustryCode(industryCode); + } + + if (CollectionUtil.isEmpty(deptIds)){ + return result; + } +// //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// return result; +// } +// Map totalMap = new HashMap<>(); +// for (Map.Entry entry : configValueMap.entrySet()){ +// PublicConsumptionDTO publicConsumptionDTO = new PublicConsumptionDTO(); +// publicConsumptionDTO.setKey(entry.getKey()); +// publicConsumptionDTO.setKeyName(entry.getValue()); +// List datas = new ArrayList<>(); +// for (String month:months){ +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// energyConsumptionDTO.setDate(month); +// BigDecimal value = tsKvService.getValue1(entry.getKey(),entityIds,monthStartTime,monthEndTime); +//// if (value!=null){ +//// if (totalMap.get(month)!=null){ +//// totalMap.put(month,totalMap.get(month).add(value)); +//// }else{ +//// totalMap.put(month,value); +//// } +//// }else{ +//// if (totalMap.get(month)==null){ +//// totalMap.put(month,null); +//// } +//// } +// energyConsumptionDTO.setValue(value); +// datas.add(energyConsumptionDTO); +// } +// publicConsumptionDTO.setDatas(datas); +// result.add(publicConsumptionDTO); +// } +//// PublicConsumptionDTO publicConsumptionDTO = new PublicConsumptionDTO(); +//// publicConsumptionDTO.setKey("total"); +//// publicConsumptionDTO.setKeyName("总能耗"); +//// List datas = new ArrayList<>(); +//// for (Map.Entry entry : totalMap.entrySet()){ +//// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +//// energyConsumptionDTO.setDate(entry.getKey()); +//// energyConsumptionDTO.setValue(entry.getValue()); +//// datas.add(energyConsumptionDTO); +//// } +//// List newDatas = datas.stream().sorted(Comparator.comparing(PublicEnergyConsumptionDTO::getDate)).collect(Collectors.toList()); +//// publicConsumptionDTO.setDatas(newDatas); +//// result.add(publicConsumptionDTO); + return result; + } + + + @Override + public List getUnitBuildConsumption(Map params) { + List result = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + String industryCode = null; + if (params.get("industryCode")!=null){ + industryCode = params.get("industryCode").toString(); + } + List orgs; + if (StringUtils.isBlank(industryCode)){ + //获取所有机构 + orgs = publicOrgService.getAllOrgInfo(); + }else{ + orgs = publicOrgService.getOrgUnits(industryCode); + } + + if (CollectionUtil.isEmpty(orgs)){ + return result; + } +// for (PublicOrgInfoDTO publicOrgInfoDTO:orgs){ +// PublicUnitConsumptionDTO publicUnitConsumptionDTO = new PublicUnitConsumptionDTO(); +// publicUnitConsumptionDTO.setOrgName(publicOrgInfoDTO.getOrgInfo().getOrgName()); +// if (CollectionUtil.isEmpty(publicOrgInfoDTO.getOrgDetails())){ +// result.add(publicUnitConsumptionDTO); +// continue; +// } +// List deptIds = new ArrayList<>(); +// deptIds.add(publicOrgInfoDTO.getOrgInfo().getId()); +// +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,deptIds); +// +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// result.add(publicUnitConsumptionDTO); +// continue; +// } +// //总能耗能耗 +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,yearStartTime,yearEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// publicUnitConsumptionDTO.setValue(value); +// +// result.add(publicUnitConsumptionDTO); +// } + + Comparator comparator = Comparator.comparing(PublicUnitConsumptionDTO::getValue, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) +// .filter(item -> item.getValue() != null) + .limit(10) + .collect(Collectors.toList()); + return sortedList; + } + + + @Override + public PublicTotalDataDTO getTotalData(Map params) { + PublicTotalDataDTO result = new PublicTotalDataDTO(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + String industryCode = null; + if (params.get("industryCode")!=null){ + industryCode = params.get("industryCode").toString(); + } + + //获取所有机构 + List orgDetailDTOS; + if (StringUtils.isBlank(industryCode)){ + orgDetailDTOS = publicOrgService.getOrgDetails(year); + }else{ + orgDetailDTOS = publicOrgService.getOrgDetailsByIndustryCode(year,industryCode); + } + + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + result.setOrgCount(0); + return result; + } + result.setOrgCount(orgDetailDTOS.size()); + //计算总建筑面积 + BigDecimal areaValue = orgDetailDTOS.stream() + .filter(item -> item.getOrgDetail().getBuildArea() != null) + .map(item -> item.getOrgDetail().getBuildArea()) + .reduce(BigDecimal.ZERO, BigDecimal::add); + //计算总用能人数 + Integer sum = orgDetailDTOS.stream() + .filter(item -> item.getOrgDetail().getDayUserCount() != null) + .mapToInt(item -> item.getOrgDetail().getDayUserCount()) + .sum(); + BigDecimal sumUser = null; + if (sum !=null){ + sumUser = new BigDecimal(sum); + } + +// List deptIds = orgDetailDTOS.stream().map(PublicOrgDetailInfoDTO::getId).collect(Collectors.toList()); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,deptIds); +// +// //总能耗,总碳排 +// List totalBindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List totalEntityIds = totalBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(totalEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(totalEntityIds,yearStartTime,yearEndTime); +// if (data!=null){ +// BigDecimal totalConsumption = data.getConsumption(); +// BigDecimal totalCarbon = data.getCarbon(); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// totalConsumption = totalConsumption==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : totalConsumption.add(consumptionAndCarbon.getConsumption()); +// } +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// totalCarbon = totalCarbon==null? BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()) : totalCarbon.add(consumptionAndCarbon.getCarbon()); +// } +// result.setTotalConsumption(totalConsumption); +// result.setTotalCarbon(totalCarbon); +// } +// } +// //建筑能耗,建筑碳排 +// List buildBindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List buildEntityIds = buildBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(buildEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(buildEntityIds,yearStartTime,yearEndTime); +// BigDecimal buildConsumption =null; +// BigDecimal buildCarbon =null; +// if (data!=null){ +// buildConsumption = data.getConsumption(); +// buildCarbon = data.getCarbon(); +// result.setBuildConsumption(data.getConsumption()); +// result.setBuildCarbon(data.getCarbon()); +// } +// //单位建筑面积能耗,人均建筑能耗 +// if (buildConsumption!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildAreaConsumption(buildConsumption.divide(areaValue,2,BigDecimal.ROUND_HALF_UP)); +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildPersonConsumption(buildConsumption.divide(sumUser,2,BigDecimal.ROUND_HALF_UP)); +// } +// } +// +// //单位建筑面积碳排,人均建筑碳排 +// if (buildCarbon!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildAreaCarbon(buildCarbon.divide(areaValue,2,BigDecimal.ROUND_HALF_UP)); +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildPersonCarbon(buildCarbon.divide(sumUser,2,BigDecimal.ROUND_HALF_UP)); +// } +// } +// } +// //食堂能耗 +// List canteenBindCodeDTOS = publicBindCodeService.getListByDeptIds("canteen_energy",deptIds); +// List canteenEntityIds = canteenBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(canteenEntityIds)){ +// BigDecimal value = tsKvService.getTotalConsumption(canteenEntityIds,yearStartTime,yearEndTime); +// result.setCanteenConsumption(value); +// } +// +// //数据中心能耗 +// List dataCenterBindCodeDTOS = publicBindCodeService.getListByDeptIds("data_center_energy",deptIds); +// List dataCenterEntityIds = dataCenterBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(dataCenterEntityIds)){ +// BigDecimal value = tsKvService.getTotalConsumption(dataCenterEntityIds,yearStartTime,yearEndTime); +// result.setDataCenterConsumption(value); +// } +// +// //光伏电量 +// List photovoltaicBindCodeDTOS = publicBindCodeService.getListByDeptIds("photovoltaic_energy",deptIds); +// List photovoltaicEntityIds = photovoltaicBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(photovoltaicEntityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(photovoltaicEntityIds,yearStartTime,yearEndTime); +// result.setPhotovoltaicEnergy(value); +// } +// +// //充电桩电量 +// List chargingStationBindCodeDTOS = publicBindCodeService.getListByDeptIds("charging_station_energy",deptIds); +// List chargingStationEntityIds = chargingStationBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(chargingStationEntityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(chargingStationEntityIds,yearStartTime,yearEndTime); +// result.setChargingStationEnergy(value); +// } + + return result; + } + + @Override + public List getEnergyAnalyse(Map params) { + List result = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + List months = DateTimeUtils.getMonthList(year); + + String lastYear = String.valueOf(Integer.parseInt(year)-1); + //去年月份 + List lastMonths = DateTimeUtils.getMonthList(lastYear); + //获取属性 + String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); + Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); + String industryCode = null; + if (params.get("industryCode")!=null){ + industryCode = params.get("industryCode").toString(); + } + List deptIds; + if (StringUtils.isBlank(industryCode)){ + deptIds = publicOrgService.getAllDeptIds(); + }else{ + deptIds = publicOrgService.getAllDeptIdsByIndustryCode(industryCode); + } + + if (CollectionUtil.isEmpty(deptIds)){ + return result; + } + //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// return result; +// } +// +// for (Map.Entry entry : configValueMap.entrySet()){ +// PublicEnergyDTO publicEnergyDTO = new PublicEnergyDTO(); +// publicEnergyDTO.setKey(entry.getKey()); +// publicEnergyDTO.setKeyName(entry.getValue()); +// List datas = new ArrayList<>(); +// List datas1 = new ArrayList<>(); +// for (String month:months){ +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// energyConsumptionDTO.setDate(month); +// BigDecimal value = tsKvService.getValue1(entry.getKey(),entityIds,monthStartTime,monthEndTime); +// energyConsumptionDTO.setValue(value); +// datas.add(energyConsumptionDTO); +// } +// for (String month:lastMonths){ +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// energyConsumptionDTO.setDate(month); +// BigDecimal value = tsKvService.getValue1(entry.getKey(),entityIds,monthStartTime,monthEndTime); +// energyConsumptionDTO.setValue(value); +// datas1.add(energyConsumptionDTO); +// } +// publicEnergyDTO.setCurrentDatas(datas); +// publicEnergyDTO.setLastDatas(datas1); +// result.add(publicEnergyDTO); +// } + return result; + } + + @Override + public List getOrgUnitBuildConsumption(Map params) { + List result = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + + //获取所有机构 + List orgDetailDTOS = publicOrgService.getOrgDetailsByParams2(year,params); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + return result; + } +// for (PublicOrgDetailInfoDTO detailInfoDTO:orgDetailDTOS){ +// PublicUnitBuildConsumptionDTO buildConsumptionDTO = new PublicUnitBuildConsumptionDTO(); +// buildConsumptionDTO.setOrgId(detailInfoDTO.getId()); +// buildConsumptionDTO.setOrgName(detailInfoDTO.getOrgName()); +// //计算总建筑面积 +// BigDecimal areaValue = detailInfoDTO.getOrgDetail().getBuildArea(); +// //计算总用能人数 +// Integer sum = detailInfoDTO.getOrgDetail().getDayUserCount(); +// BigDecimal sumUser = null; +// if (sum !=null){ +// sumUser = new BigDecimal(sum); +// } +// List deptIds = new ArrayList<>(); +// deptIds.add(detailInfoDTO.getId()); +// //建筑能耗,建筑碳排 +// List buildBindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List buildEntityIds = buildBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(buildEntityIds)){ +// BigDecimal buildConsumption = tsKvService.getTotalConsumption(buildEntityIds,yearStartTime,yearEndTime); +// buildConsumptionDTO.setBuildValue(buildConsumption); +// //单位建筑面积能耗,人均建筑能耗 +// if (buildConsumption!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// buildConsumptionDTO.setUnitBuildAreaValue(buildConsumption.divide(areaValue,2,BigDecimal.ROUND_HALF_UP)); +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// buildConsumptionDTO.setUnitBuildPersonValue(buildConsumption.divide(sumUser,2,BigDecimal.ROUND_HALF_UP)); +// } +// } +// } +// result.add(buildConsumptionDTO); +// } + Comparator comparator = Comparator.comparing(PublicUnitBuildConsumptionDTO::getBuildValue, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) + .collect(Collectors.toList()); + return sortedList; + } + + @Override + public PublicOrgWarningCountRatioDTO getWarningCountRatio(Map params) { + PublicOrgWarningCountRatioDTO result = new PublicOrgWarningCountRatioDTO(); + String year = params.get("year").toString(); + List orgDetailDTOS = publicOrgService.getOrgDetailsByParams2(year,params); + if (CollectionUtil.isEmpty(orgDetailDTOS)){ + return result; + } + int totalCount = orgDetailDTOS.size(); + int yearUpLimitCount = 0; + + int yearNormalCount = orgDetailDTOS.size(); + + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + +// for (PublicOrgDetailInfoDTO orgDetailInfoDTO : orgDetailDTOS){ +// //获取机构限制阈值 +// Map limitMap = publicOrgLimitService.getOrgLimitMap(orgDetailInfoDTO.getId()); +// //年单位面积约束值 +// BigDecimal yearUnitBuildAreaLimitValue = limitMap.get(Constant.ENERGY_PERBULID).getYearValue(); +// //年单位人数约束值 +// BigDecimal yearUnitBuildPersonLimitValue = null; +// if (ObjectUtil.isNotEmpty(limitMap.get(Constant.ENERGY_PERPERSON))){ +// yearUnitBuildPersonLimitValue = limitMap.get(Constant.ENERGY_PERPERSON).getYearValue(); +// } +// +// //建筑面积 +// BigDecimal areaValue = orgDetailInfoDTO.getOrgDetail().getBuildArea(); +// //用能人数 +// Integer sum = orgDetailInfoDTO.getOrgDetail().getDayUserCount(); +// BigDecimal sumUser = null; +// if (sum !=null){ +// sumUser = new BigDecimal(sum); +// } +// +// List deptIds = new ArrayList<>(); +// deptIds.add(orgDetailInfoDTO.getId()); +// //建筑能耗 +// List buildBindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List buildEntityIds = buildBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(buildEntityIds)){ +// BigDecimal buildConsumption = tsKvService.getTotalConsumption(buildEntityIds,yearStartTime,yearEndTime); +// //单位建筑面积能耗,人均建筑能耗 +// if (buildConsumption!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// BigDecimal unitBuildAreaValue = buildConsumption.divide(areaValue,2,BigDecimal.ROUND_HALF_UP); +// //超限 +// if (unitBuildAreaValue!=null){ +// if (unitBuildAreaValue.compareTo(yearUnitBuildAreaLimitValue)==1){ +// yearUpLimitCount++; +// yearNormalCount--; +// continue; +// } +// } +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// BigDecimal unitBuildPersonValue = buildConsumption.divide(sumUser,2,BigDecimal.ROUND_HALF_UP); +// //超限 +// if (unitBuildPersonValue!=null && yearUnitBuildPersonLimitValue!=null){ +// if (unitBuildPersonValue.compareTo(yearUnitBuildPersonLimitValue)==1){ +// yearUpLimitCount++; +// yearNormalCount--; +// continue; +// } +// } +// } +// } +// } +// +// if (orgDetailInfoDTO.getQuotaType().equals(Constant.CENTRALIZED_OFFICE_AREA)){ +// //年数据中心约束值 +// BigDecimal yearUnitDataCenterLimitValue = limitMap.get(Constant.ENERGY_UTILIZATION_EFFICIENCY).getYearValue(); +// +// //年餐厅人数约束值 +// BigDecimal yearUnitCanteenPersonLimitValue = limitMap.get(Constant.ENERGY_PERCAFETERIA_PERSON).getYearValue(); +// +// //食堂能耗 +// List canteenBindCodeDTOS = publicBindCodeService.getListByDeptIds("canteen_energy",deptIds); +// List canteenEntityIds = canteenBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(canteenEntityIds)){ +// BigDecimal canteenValue = tsKvService.getTotalConsumption(canteenEntityIds,yearStartTime,yearEndTime); +// //超限 +// if (canteenValue!=null){ +// if (canteenValue.compareTo(yearUnitCanteenPersonLimitValue)==1){ +// yearUpLimitCount++; +// yearNormalCount--; +// continue; +// } +// } +// } +// +// //数据中心能耗 +// BigDecimal dataCenterValue =null; +// List dataCenterBindCodeDTOS = publicBindCodeService.getListByDeptIds("data_center_energy",deptIds); +// List dataCenterEntityIds = dataCenterBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(dataCenterEntityIds)){ +// dataCenterValue = tsKvService.getTotalConsumption(dataCenterEntityIds,yearStartTime,yearEndTime); +// } +// //数据中心设备能耗 +// BigDecimal dataCenterDeviceValue = null; +// List dataCenterDeviceBindCodeDTOS = publicBindCodeService.getListByDeptIds("data_center_device_energy",deptIds); +// List dataCenterDeviceEntityIds = dataCenterDeviceBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(dataCenterDeviceEntityIds)){ +// dataCenterDeviceValue = tsKvService.getTotalConsumption(dataCenterDeviceEntityIds,yearStartTime,yearEndTime); +// } +// //计算数据中心使用率 +// BigDecimal dataCenterUseValue = null; +// if (dataCenterDeviceValue!=null && dataCenterValue!=null && dataCenterValue.compareTo(BigDecimal.ZERO) !=0){ +// dataCenterUseValue = dataCenterDeviceValue.divide(dataCenterValue,2,BigDecimal.ROUND_HALF_UP); +// } +// if (dataCenterUseValue!=null){ +// //超限 +// if (dataCenterUseValue!=null){ +// if (dataCenterUseValue.compareTo(yearUnitDataCenterLimitValue)==1){ +// yearUpLimitCount++; +// yearNormalCount--; +// continue; +// } +// } +// } +// } +// } +// if (yearUpLimitCount==totalCount){ +// result.setUpLimitCountRatio(new BigDecimal(100)); +// result.setNormalCountRatio(new BigDecimal(0)); +// return result; +// } +// if (yearNormalCount==totalCount){ +// result.setNormalCountRatio(new BigDecimal(100)); +// result.setUpLimitCountRatio(new BigDecimal(0)); +// return result; +// } + result.setNormalCountRatio(new BigDecimal(yearNormalCount).divide(new BigDecimal(totalCount),2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100))); + result.setUpLimitCountRatio(new BigDecimal(yearUpLimitCount).divide(new BigDecimal(totalCount),2,BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal(100))); + return result; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicBoardServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicBoardServiceImpl.java new file mode 100644 index 0000000..7d97261 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicBoardServiceImpl.java @@ -0,0 +1,644 @@ +package com.thing.publicorg.publicboard.service.impl; + + +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicBoardService; +import com.thing.sys.biz.service.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 昆山公共机构看板 + * + * @author xiezw + * @since 2023-08-08 + */ +@Service +public class PublicBoardServiceImpl implements PublicBoardService { + + @Autowired + private SysParamsService sysParamsService; + + @Autowired + private PublicOrgService publicOrgService; + +// @Autowired +// private PublicBindCodeService publicBindCodeService; +// +// @Autowired +// private TsKvsService tsKvService; + + @Autowired + private SysDictDataService iotDictDataService; + + @Autowired + private SysRegionService sysRegionService; + +// @Autowired +// private PublicEnterValueService publicEnterValueService; + + @Autowired + private SysUserService sysUserService; + + @Autowired + private SysDeptService sysDeptService; + +// @Autowired +// private PowerAttributeService powerAttributeService; + + @Override + public List getTotalConsumptionTrend(Map params) { + List result = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// List months = TimeUtils.getMonthList(year); +// //获取属性 +// String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); +// Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); +// //获取所有机构 +//// List orgs = publicOrgService.getAllOrgInfo(); +// List deptIds = publicOrgService.getAllDeptIds(); +// if (CollectionUtil.isEmpty(deptIds)){ +// return result; +// } +// //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// return result; +// } +// Map totalMap = new HashMap<>(); +// for (Map.Entry entry : configValueMap.entrySet()){ +// PublicConsumptionDTO publicConsumptionDTO = new PublicConsumptionDTO(); +// publicConsumptionDTO.setKey(entry.getKey()); +// publicConsumptionDTO.setKeyName(entry.getValue()); +// List datas = new ArrayList<>(); +// for (String month:months){ +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// energyConsumptionDTO.setDate(month); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getConsumptionAndCarbon(month,deptIds,entry.getKey()); +// BigDecimal value = tsKvService.getValue(entry.getKey(),entityIds,monthStartTime,monthEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// if (value!=null){ +// if (totalMap.get(month)!=null){ +// totalMap.put(month,totalMap.get(month).add(value)); +// }else{ +// totalMap.put(month,value); +// } +// }else{ +// if (totalMap.get(month)==null){ +// totalMap.put(month,null); +// } +// } +// energyConsumptionDTO.setValue(value); +// datas.add(energyConsumptionDTO); +// } +// publicConsumptionDTO.setDatas(datas); +// result.add(publicConsumptionDTO); +// } +// PublicConsumptionDTO publicConsumptionDTO = new PublicConsumptionDTO(); +// publicConsumptionDTO.setKey("total"); +// publicConsumptionDTO.setKeyName("总能耗"); +// List datas = new ArrayList<>(); +// for (Map.Entry entry : totalMap.entrySet()){ +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// energyConsumptionDTO.setDate(entry.getKey()); +// energyConsumptionDTO.setValue(entry.getValue()); +// datas.add(energyConsumptionDTO); +// } +// List newDatas = datas.stream().sorted(Comparator.comparing(PublicEnergyConsumptionDTO::getDate)).collect(Collectors.toList()); +// publicConsumptionDTO.setDatas(newDatas); +// result.add(publicConsumptionDTO); + return result; + } + + @Override + public PublicTotalCarbonDTO getTotalCarbonTrend(Map params) { + PublicTotalCarbonDTO result = new PublicTotalCarbonDTO(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //今年月份 +// List currentMonths = TimeUtils.getMonthList(year); +// String lastYear = String.valueOf(Integer.parseInt(year)-1); +// //去年月份 +// List lastMonths = TimeUtils.getMonthList(lastYear); +// +// List deptIds = publicOrgService.getAllDeptIds(); +// if (CollectionUtil.isEmpty(deptIds)){ +// return result; +// } +// //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// return result; +// } +// List currentYearDatas = new ArrayList<>(); +// List lastYearDatas = new ArrayList<>(); +// for (String month : currentMonths){ +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// energyConsumptionDTO.setDate(month); +// +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetConsumptionAndCarbon(month,deptIds); +// BigDecimal value = tsKvService.getTotalCarbonValue(entityIds,monthStartTime,monthEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()) : value.add(consumptionAndCarbon.getCarbon()); +// } +// energyConsumptionDTO.setValue(value); +// currentYearDatas.add(energyConsumptionDTO); +// } +// for (String month : lastMonths){ +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// energyConsumptionDTO.setDate(month); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetConsumptionAndCarbon(month,deptIds); +// BigDecimal value = tsKvService.getTotalCarbonValue(entityIds,monthStartTime,monthEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()) : value.add(consumptionAndCarbon.getCarbon()); +// } +// energyConsumptionDTO.setValue(value); +// lastYearDatas.add(energyConsumptionDTO); +// } +// result.setCurrentYearDatas(currentYearDatas); +// result.setLastYearDatas(lastYearDatas); + return result; + } + + @Override + public List getQuotaTypeConsumption(Map params) { + List result = new ArrayList<>(); +// String type = "0"; +// if (params.get("type")!=null){ +// type = params.get("type").toString(); +// } +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); +// List quotaTypeDatas = iotDictDataService.getDictDataNameByDictTypeNew("quota_type",tenantCode); +// for (SysDictDataDTO dictDataDTO : quotaTypeDatas){ +// //获取所有机构 +// List orgs = publicOrgService.getByQuotaType(dictDataDTO.getDictValue()); +// //机构根据定额类型分组 +// PublicQuotaTypeDTO publicQuotaTypeDTO = new PublicQuotaTypeDTO(); +// publicQuotaTypeDTO.setQuotaType(dictDataDTO.getDictValue()); +// publicQuotaTypeDTO.setQuotaTypeName(dictDataDTO.getDictLabel()); +// if (CollectionUtil.isEmpty(orgs)){ +// result.add(publicQuotaTypeDTO); +// continue; +// } +// List deptIds = orgs.stream().map(PublicOrgDTO::getId).collect(Collectors.toList()); +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// BigDecimal value =null; +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,deptIds); +// if ("0".equals(type)){ +// if (CollectionUtil.isNotEmpty(entityIds)){ +// value = tsKvService.getTotalConsumption(entityIds,yearStartTime,yearEndTime); +// } +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value ==null?BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()):value.add(consumptionAndCarbon.getConsumption()); +// } +// }else{ +// if (CollectionUtil.isNotEmpty(entityIds)){ +// value = tsKvService.getTotalCarbonValue(entityIds,yearStartTime,yearEndTime); +// } +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// value = value ==null?BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()):value.add(consumptionAndCarbon.getCarbon()); +// } +// } +// publicQuotaTypeDTO.setValue(value); +// result.add(publicQuotaTypeDTO); +// } + + return result; + } + + @Override + public PublicTotalConsumptionAnalyseDTO getTotalConsumptionAnalyse(Map params) { + PublicTotalConsumptionAnalyseDTO result = new PublicTotalConsumptionAnalyseDTO(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //今年月份 +// List currentMonths = TimeUtils.getMonthList(year); +// String lastYear = String.valueOf(Integer.parseInt(year)-1); +// //去年月份 +// List lastMonths = TimeUtils.getMonthList(lastYear); +// +// List deptIds = publicOrgService.getAllDeptIds(); +// if (CollectionUtil.isEmpty(deptIds)){ +// return result; +// } +// //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// return result; +// } +// List currentYearDatas = new ArrayList<>(); +// List lastYearDatas = new ArrayList<>(); +// Map lastYearData = new HashMap<>(); +// for (String month : lastMonths){ +// PublicEnergyConsumptionDTO energyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// energyConsumptionDTO.setDate(month); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetConsumptionAndCarbon(month,deptIds); +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,monthStartTime,monthEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// energyConsumptionDTO.setValue(value); +// lastYearData.put(month.substring(5),value); +// lastYearDatas.add(energyConsumptionDTO); +// } +// for (String month : currentMonths){ +// PublicEnergyConsumptionAnalyseDTO energyConsumptionDTO = new PublicEnergyConsumptionAnalyseDTO(); +// Long monthStartTime = TimeUtils.getBeginTime(month+"-01 00:00:00","0"); +// Long monthEndTime = TimeUtils.getEndTime(month+"-01 00:00:00","0"); +// energyConsumptionDTO.setDate(month); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetConsumptionAndCarbon(month,deptIds); +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,monthStartTime,monthEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// energyConsumptionDTO.setValue(value); +// //计算同比 +// if (value!=null && lastYearData!=null && lastYearData.get(month.substring(5))!=null && lastYearData.get(month.substring(5)).compareTo(BigDecimal.ZERO)!=0){ +// BigDecimal lastValue = lastYearData.get(month.substring(5)); +// BigDecimal yearOnYearGrowthRate = (value.subtract(lastValue)).divide(lastValue, 4, BigDecimal.ROUND_HALF_UP).multiply(new BigDecimal("100")); +// energyConsumptionDTO.setYoy(yearOnYearGrowthRate); +// } +// currentYearDatas.add(energyConsumptionDTO); +// } +// +// result.setCurrentYearDatas(currentYearDatas); +// result.setLastYearDatas(lastYearDatas); + return result; + } + + @Override + public PublicSubNodeRespDTO getEnergyFlow(Map params) { + PublicSubNodeRespDTO result = new PublicSubNodeRespDTO(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// +// List nodes = new ArrayList<>(); +// +// List links = new ArrayList<>(); +// //获取属性 +// String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_CODE"); +// Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); +// for (Map.Entry entry : configValueMap.entrySet()){ +// PublicNodeDTO publicNodeDTO = new PublicNodeDTO(); +// publicNodeDTO.setId(entry.getKey().substring(0,1)); +// publicNodeDTO.setName(entry.getValue()); +// nodes.add(publicNodeDTO); +// } +// //获取所有机构 +// List orgs = publicOrgService.getAllOrg(); +// //机构根据定额类型分组 +// Map> orgMap = orgs.stream() +// .collect(Collectors.groupingBy(PublicOrgDTO::getQuotaType, Collectors.mapping(PublicOrgDTO::getId, Collectors.toList()))); +// Integer Id = 1; +// for (Map.Entry> entry : orgMap.entrySet()){ +// PublicNodeDTO publicNodeDTO = new PublicNodeDTO(); +// SysDictDataDTO quotaTypeData = iotDictDataService.getDictDataNameByDictType(entry.getKey()); +// publicNodeDTO.setId(Id.toString()); +// publicNodeDTO.setName(quotaTypeData.getDictLabel()); +// //获取所有机构对应的总能耗的物 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",entry.getValue()); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// nodes.add(publicNodeDTO); +// continue; +// } +// for (Map.Entry entry1 : configValueMap.entrySet()){ +// PublicLinkDTO publicLinkDTO = new PublicLinkDTO(); +// publicLinkDTO.setSource(entry1.getKey().substring(0,1)); +// publicLinkDTO.setTarget(Id.toString()); +// BigDecimal value = tsKvService.getValue1(entry1.getKey().substring(0,1),entityIds,yearStartTime,yearEndTime); +// publicLinkDTO.setValue(value); +// SysUserEntity sysUserEntity = sysUserService.getUserbySuperAdmin(); +// if (sysUserEntity!=null){ +// SysDeptDTO sysDeptDTO = sysDeptService.getDeptByPid(0L, sysUserEntity.getTenantCode()); +// if (sysDeptDTO!=null){ +//// List powerAttributes = powerAttributeService.getPowerAttributesByCode1(entry1.getKey(), sysDeptDTO.getId(), sysDeptDTO.getTenantCode()); +//// if (CollectionUtil.isNotEmpty(powerAttributes)){ +//// PowerAttributeDTO attributeDTO = powerAttributes.get(0); +//// SysDictDataDTO dictDataDTO = iotDictDataService.getByDictTypeAndKey("attribute_unit",attributeDTO.getUnit(),sysDeptDTO.getId(), sysDeptDTO.getTenantCode()); +//// if (dictDataDTO!=null){ +//// publicLinkDTO.setUnit("万"+dictDataDTO.getDictLabel()); +//// } +//// } +// } +// } +// links.add(publicLinkDTO); +// } +// nodes.add(publicNodeDTO); +// Id++; +// } +// result.setNodes(nodes); +// result.setLinks(links); + return result; + } + + @Override + public List getUnitBuildConsumption(Map params) { + List result = new ArrayList<>(); +// String type = "0"; +// if (params.get("type")!=null){ +// type = params.get("type").toString(); +// } +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// //获取所有机构 +// List orgs = publicOrgService.getAllOrgInfo(); +// if (CollectionUtil.isEmpty(orgs)){ +// return result; +// } +// for (PublicOrgInfoDTO publicOrgInfoDTO:orgs){ +// PublicUnitConsumptionDTO publicUnitConsumptionDTO = new PublicUnitConsumptionDTO(); +// publicUnitConsumptionDTO.setOrgName(publicOrgInfoDTO.getOrgInfo().getOrgName()); +// if (CollectionUtil.isEmpty(publicOrgInfoDTO.getOrgDetails())){ +// result.add(publicUnitConsumptionDTO); +// continue; +// } +// PublicOrgDetailDTO publicOrgDetailDTO = publicOrgInfoDTO.getOrgDetails().get(0); +// for (PublicOrgDetailDTO orgDetailDTO :publicOrgInfoDTO.getOrgDetails()){ +// if (orgDetailDTO.getYear().equals(year)){ +// publicOrgDetailDTO = orgDetailDTO; +// } +// } +// List deptIds = new ArrayList<>(); +// deptIds.add(publicOrgInfoDTO.getOrgInfo().getId()); +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// result.add(publicUnitConsumptionDTO); +// continue; +// } +// //建筑能耗 +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,yearStartTime,yearEndTime); +// BigDecimal unitValue = null; +// if (value !=null){ +// if ("0".equals(type)){ +// Integer userPerson = publicOrgDetailDTO.getDayUserCount(); +// if (userPerson !=null && userPerson!=0){ +// unitValue = value.divide(new BigDecimal(userPerson),2,BigDecimal.ROUND_HALF_UP); +// } +// }else{ +// BigDecimal buildArea = publicOrgDetailDTO.getBuildArea(); +// if (buildArea !=null && buildArea.compareTo(BigDecimal.ZERO) !=0){ +// unitValue = value.divide(buildArea,2,BigDecimal.ROUND_HALF_UP); +// } +// } +// } +// publicUnitConsumptionDTO.setValue(unitValue); +// +// result.add(publicUnitConsumptionDTO); +// } + + Comparator comparator = Comparator.comparing(PublicUnitConsumptionDTO::getValue, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) +// .filter(item -> item.getValue() != null) + .limit(10) + .collect(Collectors.toList()); + return sortedList; + } + + @Override + public List getAreaEnergyData(Map params) { + + List result = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// Map pidParams = new HashMap<>(); +// pidParams.put("pid","320583"); +// //获取区域 +// List list = sysRegionService.list(pidParams); +// for (SysRegionDTO sysRegionDTO: list){ +// PublicAreaConsumptionDTO areaConsumptionDTO = new PublicAreaConsumptionDTO(); +// areaConsumptionDTO.setRegionCode(sysRegionDTO.getId()); +// areaConsumptionDTO.setRegionName(sysRegionDTO.getName()); +// //获取区域内所有机构 +// List orgDetailDTOS = publicOrgService.getAreaOrgDetails(year,sysRegionDTO.getId().toString()); +// if (CollectionUtil.isEmpty(orgDetailDTOS)){ +// areaConsumptionDTO.setOrgCount(0); +// result.add(areaConsumptionDTO); +// continue; +// } +// areaConsumptionDTO.setOrgCount(orgDetailDTOS.size()); +// List deptIds = orgDetailDTOS.stream().map(PublicOrgDetailInfoDTO::getId).collect(Collectors.toList()); +// //总能耗,总碳排 +// List totalBindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List totalEntityIds = totalBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,deptIds); +// +// if (CollectionUtil.isNotEmpty(totalEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(totalEntityIds,yearStartTime,yearEndTime); +// if (data!=null){ +// BigDecimal totalConsumption = data.getConsumption(); +// BigDecimal totalCarbon = data.getCarbon(); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// totalConsumption = totalConsumption==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : totalConsumption.add(consumptionAndCarbon.getConsumption()); +// } +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// totalCarbon = totalCarbon==null? BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()) : totalCarbon.add(consumptionAndCarbon.getCarbon()); +// } +// areaConsumptionDTO.setTotalConsumption(totalConsumption); +// areaConsumptionDTO.setTotalCarbon(totalCarbon); +// } +// } +// //建筑能耗,建筑碳排 +// List buildBindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List buildEntityIds = buildBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(buildEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(buildEntityIds,yearStartTime,yearEndTime); +// if (data!=null){ +// areaConsumptionDTO.setTotalBuildConsumption(data.getConsumption()); +// areaConsumptionDTO.setTotalBuildCarbon(data.getCarbon()); +// } +// } +// result.add(areaConsumptionDTO); +// } + + return result; + } + + @Override + public PublicTotalDataDTO getTotalData(Map params) { + PublicTotalDataDTO result = new PublicTotalDataDTO(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// +// //获取所有机构 +// List orgDetailDTOS = publicOrgService.getOrgDetails(year); +// +// if (CollectionUtil.isEmpty(orgDetailDTOS)){ +// result.setOrgCount(0); +// return result; +// } +// result.setOrgCount(orgDetailDTOS.size()); +// //计算总建筑面积 +// BigDecimal areaValue = orgDetailDTOS.stream() +// .filter(item -> item.getOrgDetail().getBuildArea() != null) +// .map(item -> item.getOrgDetail().getBuildArea()) +// .reduce(BigDecimal.ZERO, BigDecimal::add); +// //计算总用能人数 +// Integer sum = orgDetailDTOS.stream() +// .filter(item -> item.getOrgDetail().getDayUserCount() != null) +// .mapToInt(item -> item.getOrgDetail().getDayUserCount()) +// .sum(); +// BigDecimal sumUser = null; +// if (sum !=null){ +// sumUser = new BigDecimal(sum); +// } +// +// List deptIds = orgDetailDTOS.stream().map(PublicOrgDetailInfoDTO::getId).collect(Collectors.toList()); +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,deptIds); +// +// //总能耗,总碳排 +// List totalBindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",deptIds); +// List totalEntityIds = totalBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(totalEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(totalEntityIds,yearStartTime,yearEndTime); +// if (data!=null){ +// BigDecimal totalConsumption = data.getConsumption(); +// BigDecimal totalCarbon = data.getCarbon(); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// totalConsumption = totalConsumption==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : totalConsumption.add(consumptionAndCarbon.getConsumption()); +// } +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getCarbon()!=null){ +// totalCarbon = totalCarbon==null? BigDecimal.ZERO.add(consumptionAndCarbon.getCarbon()) : totalCarbon.add(consumptionAndCarbon.getCarbon()); +// } +// result.setTotalConsumption(totalConsumption); +// result.setTotalCarbon(totalCarbon); +// } +// } +// //建筑能耗,建筑碳排 +// List buildBindCodeDTOS = publicBindCodeService.getListByDeptIds("bulid_energy",deptIds); +// List buildEntityIds = buildBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(buildEntityIds)){ +// PublicConsumptionAndCarbonDTO data = tsKvService.getTotalConsumptionAndCarbon(buildEntityIds,yearStartTime,yearEndTime); +// BigDecimal buildConsumption =null; +// BigDecimal buildCarbon =null; +// if (data!=null){ +// buildConsumption = data.getConsumption(); +// buildCarbon = data.getCarbon(); +// result.setBuildConsumption(data.getConsumption()); +// result.setBuildCarbon(data.getCarbon()); +// } +// //单位建筑面积能耗,人均建筑能耗 +// if (buildConsumption!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildAreaConsumption(buildConsumption.divide(areaValue,2,BigDecimal.ROUND_HALF_UP)); +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildPersonConsumption(buildConsumption.divide(sumUser,2,BigDecimal.ROUND_HALF_UP)); +// } +// } +// +// //单位建筑面积碳排,人均建筑碳排 +// if (buildCarbon!=null){ +// if (areaValue!=null && areaValue.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildAreaCarbon(buildCarbon.divide(areaValue,2,BigDecimal.ROUND_HALF_UP)); +// } +// if (sumUser!=null && sumUser.compareTo(BigDecimal.ZERO) !=0){ +// result.setUnitBuildPersonCarbon(buildCarbon.divide(sumUser,2,BigDecimal.ROUND_HALF_UP)); +// } +// } +// } +// //食堂能耗 +// List canteenBindCodeDTOS = publicBindCodeService.getListByDeptIds("canteen_energy",deptIds); +// List canteenEntityIds = canteenBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(canteenEntityIds)){ +// BigDecimal value = tsKvService.getTotalConsumption(canteenEntityIds,yearStartTime,yearEndTime); +// result.setCanteenConsumption(value); +// } +// +// //数据中心能耗 +// List dataCenterBindCodeDTOS = publicBindCodeService.getListByDeptIds("data_center_energy",deptIds); +// List dataCenterEntityIds = dataCenterBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(dataCenterEntityIds)){ +// BigDecimal value = tsKvService.getTotalConsumption(dataCenterEntityIds,yearStartTime,yearEndTime); +// result.setDataCenterConsumption(value); +// } +// +// //光伏电量 +// List photovoltaicBindCodeDTOS = publicBindCodeService.getListByDeptIds("photovoltaic_energy",deptIds); +// List photovoltaicEntityIds = photovoltaicBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(photovoltaicEntityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(photovoltaicEntityIds,yearStartTime,yearEndTime); +// result.setPhotovoltaicEnergy(value); +// } +// +// //充电桩电量 +// List chargingStationBindCodeDTOS = publicBindCodeService.getListByDeptIds("charging_station_energy",deptIds); +// List chargingStationEntityIds = chargingStationBindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(chargingStationEntityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(chargingStationEntityIds,yearStartTime,yearEndTime); +// result.setChargingStationEnergy(value); +// } + + return result; + } + + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicChargingPileServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicChargingPileServiceImpl.java new file mode 100644 index 0000000..218a619 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicChargingPileServiceImpl.java @@ -0,0 +1,280 @@ +package com.thing.publicorg.publicboard.service.impl; + + +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import com.thing.publicorg.publicboard.dto.PublicAnnualPowerDTO; +import com.thing.publicorg.publicboard.dto.PublicChargingPileDTO; +import com.thing.publicorg.publicboard.dto.PublicUnitNamePile; +import com.thing.publicorg.publicboard.service.PublicChargingPileService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class PublicChargingPileServiceImpl implements PublicChargingPileService { + + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private SysDictDataService iotDictDataService; + +// @Autowired +// private PublicBindCodeService publicBindCodeService; +// +// @Autowired +// private TsKvsService tsKvService; + + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + /** + * 充电桩情况 + * @param params + * @return + */ + @Override + public List getChargingPileCondition(Map params) { + List result = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //开始时间 +// Long yearStartTimes = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Timestamp yearStartTime = new Timestamp(yearStartTimes); +// //结束时间 +// Long yearEndTimes = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// Timestamp yearEndTime = new Timestamp(yearEndTimes); +// //获取所有机构 +// List orgs = publicOrgService.getAllOrg(); +// //机构根据业务代码分组-org +// Map> orgMap = orgs.stream() +// .collect(Collectors.groupingBy(PublicOrgDTO::getIndustryCode, Collectors.mapping(PublicOrgDTO::getId, Collectors.toList()))); +// +// //BigDecimal sum=null; +// for (Map.Entry> entry : orgMap.entrySet()){ +// PublicChargingPileDTO chargingPileDTO = new PublicChargingPileDTO(); +// //根据行业代码-字典值查询 +// SysDictDataDTO quotaTypeData = iotDictDataService.getDictDataNameByDictTypes(entry.getKey()); +// //获取行业代码-字典值 +// chargingPileDTO.setIndustryCode(entry.getKey()); +// //获取行业代码-名称 +// chargingPileDTO.setIndustryCodeName(quotaTypeData.getDictLabel()); +// //获取充电桩耗电量 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("charging_station_energy",entry.getValue()); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(entityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(entityIds,yearStartTimes,yearEndTimes); +// if(value!=null){ +// chargingPileDTO.setConsumption(value.setScale(2, RoundingMode.HALF_UP)); +// }else { +// chargingPileDTO.setConsumption(BigDecimal.ZERO); +// } +// } +// //获取指定时间内的充电桩数量 +// BigDecimal values = publicOrgDetailService.getChargingPileByid(entry.getValue(),yearStartTime,yearEndTime); +// if(values!=null){ +// //InstalledCapacity-装机容量 +// chargingPileDTO.setChargingPileNumber(values); +// }else { +// chargingPileDTO.setChargingPileNumber(BigDecimal.ZERO); +// } +// +// result.add(chargingPileDTO); +// } + List sortedList = result.stream() + .sorted(Comparator.comparing(PublicChargingPileDTO::getConsumption, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + /** + * 当前地区单位清单 + * @param params + * @return + */ + @Override + public List getChargingPileListUnits(Map params) { + List result = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //开始时间 +// Long yearStartTimes = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// Timestamp yearStartTime = new Timestamp(yearStartTimes); +// //结束时间 +// Long yearEndTimes = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// Timestamp yearEndTime = new Timestamp(yearEndTimes); +// String IndustryCode=null; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("IndustryCode"))){ +// IndustryCode = params.get("IndustryCode").toString(); +// } +// String unitName = null; +// //判断unitsName-单位名称不能为空 +// if (params!=null && ObjectUtil.isNotEmpty(params.get("unitName"))){ +// unitName = params.get("unitName").toString(); +// } +// Long deptId = null; +// //判断unitsName-单位名称不能为空 +// if (params!=null && ObjectUtil.isNotEmpty(params.get("deptId"))){ +// deptId = Long.parseLong(params.get("deptId").toString()); +// } +// List allOrgInfos = publicOrgService.getAllOrgInfos(deptId, IndustryCode, unitName); +// if(CollectionUtil.isEmpty(allOrgInfos)){ +// return result; +// } +// for (PublicOrgInfoDTO allOrgInfo : allOrgInfos) { +// PublicOrgDTO orgInfo = allOrgInfo.getOrgInfo(); +// List orgDetails = allOrgInfo.getOrgDetails(); +// if(ObjectUtil.isEmpty(orgInfo) && CollectionUtil.isEmpty(orgDetails)){ +// return result; +// } +// PublicUnitNamePile publicUnitNamePile = new PublicUnitNamePile(); +// //单位编号赋值 +// publicUnitNamePile.setDeptId(orgInfo.getDeptId()); +// //单位名称赋值 +// publicUnitNamePile.setUnitName(orgInfo.getOrgName()); +// //单位图片赋值 +// publicUnitNamePile.setPicture(orgInfo.getPicture()); +// //单位坐标 +// publicUnitNamePile.setCoordinate(orgInfo.getCoordinate()); +// //单位用电量 +// ArrayList deptIds = new ArrayList<>(); +// deptIds.add(orgInfo.getDeptId()); +// //获取充电桩耗电量 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("charging_station_energy",deptIds); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(entityIds)){ +// BigDecimal value = tsKvService.getEnergyValue(entityIds,yearStartTimes,yearEndTimes); +// //photovoltaic_energy-发电量 +// publicUnitNamePile.setElectricityGeneration(value.setScale(2, RoundingMode.HALF_UP)); +// } +// for (PublicOrgDetailDTO orgDetail : orgDetails) { +// ArrayList longs = new ArrayList<>(); +// longs.add(orgDetail.getDeptId()); +// //获取指定时间内的充电桩数量 +// BigDecimal values = publicOrgDetailService.getChargingPileByid(longs,yearStartTime,yearEndTime); +// //InstalledCapacity-装机容量 +// publicUnitNamePile.setChargingPileNumber(values); +// } +// result.add(publicUnitNamePile); +// } + List sortedList = result.stream() + .sorted(Comparator.comparing(PublicUnitNamePile::getElectricityGeneration, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + /** + * 单位-年度充电桩耗电趋势-年度充电桩耗电趋势 + * @param params + * @return + */ + @Override + public PublicAnnualPowerDTO getChargingPiles(Map params) { + PublicAnnualPowerDTO publicAnnualPowerDTO = new PublicAnnualPowerDTO(); +// ArrayList publicEnergyConsumptionDTOS = new ArrayList<>(); +// ArrayList publicEnergyConsumptionDTOS1 = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //当年月份-12 +// List months = TimeUtils.getMonthList(year); +// //去年月份-12 +// String lastYear = String.valueOf(Integer.parseInt(year)-1); +// //去年月份 +// List lastMonths = TimeUtils.getMonthList(lastYear); +// Long deptId; +// if(params!=null && ObjectUtil.isNotEmpty(params.get("deptId"))){ +// deptId = Long.parseLong(params.get("deptId").toString()); +// }else { +// deptId = 0L; +// } +// ArrayList longs1 = new ArrayList<>(); +// longs1.add(deptId); +// //获取光伏发电量 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("charging_station_energy",longs1); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// Map totalMap = new HashMap<>(); +// for (String month : months) { +// //今年-时间 +// Long monthStartTime = TimeUtils.getBeginTime(month + "-01 00:00:00", "0"); +// Long monthEndTime = TimeUtils.getEndTime(month + "-01 00:00:00", "0"); +// //今年 +// PublicDataValueDTO publicDataValueDTO = new PublicDataValueDTO(); +// publicDataValueDTO.setDate(month); +// if (CollectionUtil.isNotEmpty(entityIds)){ +// //获取今年内的光伏发电量 +// BigDecimal value = tsKvService.getEnergyValue(entityIds,monthStartTime,monthEndTime); +// if (value!=null){ +// if (totalMap.get(month)!=null){ +// totalMap.put(month,totalMap.get(month).add(value)); +// }else{ +// totalMap.put(month,value); +// } +// }else{ +// if (totalMap.get(month)==null){ +// totalMap.put(month,null); +// } +// } +// if(value == null){ +// publicDataValueDTO.setValue(value); +// }else { +// publicDataValueDTO.setValue(value); +// } +// } +// publicEnergyConsumptionDTOS.add(publicDataValueDTO); +// publicAnnualPowerDTO.setCurrent(publicEnergyConsumptionDTOS); +// } +// //去年 +// for (String lastMonth : lastMonths) { +// //去年-时间 +// Long monthStartTimes = TimeUtils.getBeginTime(lastMonth + "-01 00:00:00", "0"); +// Long monthEndTimes = TimeUtils.getEndTime(lastMonth + "-01 00:00:00", "0"); +// //去年 +// PublicDataValueDTO publicDataValueDTO = new PublicDataValueDTO(); +// publicDataValueDTO.setDate(lastMonth); +// if (CollectionUtil.isNotEmpty(entityIds)){ +// //获取今年内的光伏发电量 +// BigDecimal value = tsKvService.getEnergyValue(entityIds,monthStartTimes,monthEndTimes); +// if (value!=null){ +// if (totalMap.get(lastMonth)!=null){ +// totalMap.put(lastMonth,totalMap.get(lastMonth).add(value)); +// }else{ +// totalMap.put(lastMonth,value); +// } +// }else{ +// if (totalMap.get(lastMonth)==null){ +// totalMap.put(lastMonth,null); +// } +// } +// if(value == null){ +// publicDataValueDTO.setValue(value); +// }else { +// publicDataValueDTO.setValue(value.setScale(2, RoundingMode.HALF_UP)); +// } +// } +// publicEnergyConsumptionDTOS1.add(publicDataValueDTO); +// publicAnnualPowerDTO.setLast(publicEnergyConsumptionDTOS1); +// } + return publicAnnualPowerDTO; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEachUnitKanbanServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEachUnitKanbanServiceImpl.java new file mode 100644 index 0000000..10af913 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEachUnitKanbanServiceImpl.java @@ -0,0 +1,502 @@ +package com.thing.publicorg.publicboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; + +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import com.thing.publicorg.publicboard.dto.PublicAnnualConsumptionRatioDTO; +import com.thing.publicorg.publicboard.dto.PublicHourElectricityUsageTrendsDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyConsumptionDTO; +import com.thing.publicorg.publicboard.dto.PublicMonthlyIntegratedEnergyUseStructureDTO; +import com.thing.publicorg.publicboard.service.PublicEachUnitKanbanService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.thing.ext.service.CommonConfigService; +import jakarta.annotation.Resource; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; + +@Service +public class PublicEachUnitKanbanServiceImpl implements PublicEachUnitKanbanService { + + + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private SysDictDataService iotDictDataService; + + // @Autowired +// private PublicBindCodeService publicBindCodeService; +// + @Autowired + private TsKvService tsKvService; + + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + @Autowired + private SysParamsService sysParamsService; + + @Autowired + private CommonConfigService commonConfigService; + +// @Autowired +// private RestClientUtils restClientUtils; + +// @Autowired +// private PublicEnterValueService publicEnterValueService; +// +// @Autowired +// private IDeviceDataFacade iDeviceDataFacade; + + @Autowired + private SysTenantService sysTenantService; + + @Resource + private ThingManageContextService thingManageContextService; + + + /** + * 当年单位-能耗 + * @param params + * @return + */ + @Override + public Map getUnitOfTheYearEnergyConsumption(Map params) { + + Map stringBigDecimalMap = new HashMap<>(); + String startTime =(String) params.get("startTime"); + String endTime =(String) params.get("endTime"); + if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) { + throw new SysException("查询日期格式不正确"); + } + String type; + if(params!=null && ObjectUtil.isNotEmpty(params.get("type"))){ + type = params.get("type").toString(); + }else { + type = "0"; + } + //年份 + Long yearStartTime; + //开始时间 + Long yearEndTime; + if(type.equals("0")){ + yearStartTime = DateTimeUtils.dateToStamp(startTime+"-01 00:00:00"); + //结束时间 + yearEndTime = DateTimeUtils.dateToStamp(endTime+"-01 23:59:59"); + if (!startTime.substring(0,4).equals(endTime.substring(0,4))) { + throw new SysException("请求开始时间和结束时间必须在同一年"); + } + }else{ + yearStartTime = DateTimeUtils.dateToStamp(startTime+" 00:00:00"); + yearEndTime = DateTimeUtils.dateToStamp(endTime+" 00:00:00"); + if (!startTime.substring(0,7).equals(endTime.substring(0,7))) { + throw new SysException("请求开始时间和结束时间必须在同一月"); + } + } + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); +// Long tenantCode = 1731546878167683074L; + + SysTenantDTO tenantCode1 = sysTenantService.getTenantCode(tenantCode); + if(ObjectUtil.isNotEmpty(tenantCode1)){ + Optional optional = thingManageContextService.findViewByCode(tenantCode1.getThingCode(), UserContext.getRealTenantCode(), true); + if(optional.isPresent()) { + ArrayList keyList = new ArrayList<>(); + ArrayList keyList1 = new ArrayList<>(); + //月 + keyList.add("tce_runmm"); + keyList.add("CO2_runmm"); + keyList.add("tce_buildmm"); + keyList.add("CO2_buildmm"); + keyList.add("tce_canteenmm"); + keyList.add("tce_dataCentermm"); + keyList.add("photovoltaic_A29mm"); + keyList.add("tce_infoDevicemm"); + keyList.add("chargPower_A29mm"); + //年 + keyList1.add("tce_runyy"); + keyList1.add("CO2_runyy"); + keyList1.add("tce_buildyy"); + keyList1.add("CO2_buildyy"); + keyList1.add("tce_canteenyy"); + keyList1.add("tce_dataCenteryy"); + keyList.add("photovoltaic_A29yy"); + keyList.add("tce_infoDeviceyy"); + keyList.add("chargPower_A29yy"); + Map> thingAttrParam = new HashMap<>(); + if("1".equals(type)){ + thingAttrParam.put(optional.get().getEntityCode(),keyList); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, yearStartTime, yearEndTime, true); + if(CollectionUtil.isNotEmpty(timeseries)){ + for (TsKvDTO timesery : timeseries) { + if(timesery.getAttrKey().equals("tce_runmm")){ + //总能耗 + stringBigDecimalMap.put("totalEnergyConsumption",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("CO2_runmm")){ + //总碳排 + stringBigDecimalMap.put("totalCarbonEmission",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_buildmm")){ + //建筑能耗 + stringBigDecimalMap.put("buildingEnergyYear",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("CO2_buildmm")){ + //建筑碳排 + stringBigDecimalMap.put("carbonEmissionYear",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_canteenmm")){ + //食堂能耗 + stringBigDecimalMap.put("canteenEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_infoDevicemm")){ + //数据中心信息设备能耗 + stringBigDecimalMap.put("canteenEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_dataCentermm")){ + //数据中心能耗 + stringBigDecimalMap.put("centerEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("chargPower_A29mm")){ + //充电桩耗电量 + stringBigDecimalMap.put("consumption",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("photovoltaic_A29mm")){ + //光伏发电量 + stringBigDecimalMap.put("electricityGeneration",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + } + } + }else if("0".equals(type)){ + thingAttrParam.put(optional.get().getEntityCode(),keyList1); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, yearStartTime-1000, yearEndTime+1000, true); + if(CollectionUtil.isNotEmpty(timeseries)){ + for (TsKvDTO timesery : timeseries) { + if(timesery.getAttrKey().equals("tce_runyy")){ + //总能耗 + stringBigDecimalMap.put("totalEnergyConsumption",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("CO2_runyy")){ + //总碳排 + stringBigDecimalMap.put("totalCarbonEmission",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_buildyy")){ + //建筑能耗 + stringBigDecimalMap.put("buildingEnergyYear",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("CO2_buildyy")){ + //建筑能耗 + stringBigDecimalMap.put("carbonEmissionYear",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_canteenyy")){ + //食堂能耗 + stringBigDecimalMap.put("canteenEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_infoDeviceyy")){ + //数据中心信息设备能耗 + stringBigDecimalMap.put("canteenEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("tce_dataCenteryy")){ + //数据中心能耗 + stringBigDecimalMap.put("centerEnergy",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP).divide(new BigDecimal(10000),2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("chargPower_A29yy")){ + //充电桩耗电量 + stringBigDecimalMap.put("consumption",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + }else if(timesery.getAttrKey().equals("photovoltaic_A29yy")){ + //光伏发电量 + stringBigDecimalMap.put("electricityGeneration",new BigDecimal(timesery.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + } + } + } + } + } + return stringBigDecimalMap; + } + + /** + * 月用电趋势 + * @param params + * @return + */ + @Override + public List getMonthlyConsumptionTrend(Map params) { + ArrayList publicMonthlyConsumptionDTOS = new ArrayList<>(); +// Long deptId = null; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("deptId"))){ +// deptId = Long.parseLong(params.get("deptId").toString()); +// //获取属性 +// String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); +// Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); +// //根据编号来查询 +// List bindCodeDTOS = publicBindCodeService.getByDeptIds(deptId); +// //获取到设备id +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// //获取当前月份的天数 +// List dayListOfDateMonth = TimeUtils.getDayListOfDateMonth(new Date()); +// String key; +// if(params!=null && ObjectUtil.isNotEmpty(params.get("key"))){ +// key = params.get("key").toString(); +// }else { +// key = "A"; +// } +// for (Map.Entry entry : configValueMap.entrySet()) { +// if(entry.getKey().equals(key)){ +// ArrayList publicEnergyConsumptionDTOS = new ArrayList<>(); +// PublicMonthlyConsumptionDTO publicMonthlyConsumptionDTO = new PublicMonthlyConsumptionDTO(); +// publicMonthlyConsumptionDTO.setKey(entry.getKey()); +// publicMonthlyConsumptionDTO.setKeyName(entry.getValue()); +// Map totalMap = new HashMap<>(); +// for (Long aLong : dayListOfDateMonth) { +// PublicEnergyConsumptionDTO publicEnergyConsumptionDTO = new PublicEnergyConsumptionDTO(); +// // 转换为 LocalDate 对象 +// LocalDate localDate = Instant.ofEpochMilli(aLong) +// .atZone(ZoneId.systemDefault()) +// .toLocalDate(); +// // 获取当天的开始时间和结束时间 +// LocalDateTime startOfDay = localDate.atTime(LocalTime.MIN); +// LocalDateTime endOfDay = localDate.atTime(LocalTime.MAX); +// // 获取开始时间和结束时间的时间戳 +// long monthStartTime = startOfDay.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); +// long monthEndTime = endOfDay.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); +// // 转换为 Instant 对象 +// Instant instant = Instant.ofEpochMilli(aLong); +// // 转换为 LocalDate 对象 +// LocalDate localDates = instant.atZone(ZoneId.systemDefault()).toLocalDate(); +// // 格式化为字符串,只显示月份和日期 +// String formattedDate = localDates.format(DateTimeFormatter.ofPattern("MM-dd")); +// //日期赋值 +// publicEnergyConsumptionDTO.setDate(formattedDate); +// BigDecimal value = tsKvService.getValue(entry.getKey(),entityIds,monthStartTime,monthEndTime); +// if (value!=null){ +// if (totalMap.get(formattedDate)!=null){ +// totalMap.put(formattedDate,totalMap.get(formattedDate).add(value)); +// }else{ +// totalMap.put(formattedDate,value); +// } +// }else{ +// if (totalMap.get(formattedDate)==null){ +// totalMap.put(formattedDate,null); +// } +// } +// //值赋值 +// publicEnergyConsumptionDTO.setValue(value); +// publicEnergyConsumptionDTOS.add(publicEnergyConsumptionDTO); +// publicMonthlyConsumptionDTO.setDailyElectricityConsumption(publicEnergyConsumptionDTOS); +// } +// publicMonthlyConsumptionDTOS.add(publicMonthlyConsumptionDTO); +// } +// } +// } + return publicMonthlyConsumptionDTOS; + } + + /** + * 年用电对比 + * @param params + * @return + */ + @Override + public List getAnnualConsumptionRatio(Map params) { + ArrayList publicAnnualConsumptionRatioDTOS = new ArrayList<>(); +// String year; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// }else{ +// year = TimeUtils.getCurrentYear(); +// } +// //去年月份-12 +// String lastYear = String.valueOf(Integer.parseInt(year)-1); +// //开始时间 +// Long yearStartTime = TimeUtils.dateToStamp(year +"-01-01 00:00:00"); +// //结束时间 +// Long yearEndTime = TimeUtils.dateToStamp(year +"-12-31 23:59:59"); +// //开始时间 +// Long yearStartTimes = TimeUtils.dateToStamp(lastYear +"-01-01 00:00:00"); +// //结束时间 +// Long yearEndTimes = TimeUtils.dateToStamp(lastYear +"-12-31 23:59:59"); +// //属性类型 +// String key; +// if(params!=null && ObjectUtil.isNotEmpty(params.get("key"))){ +// key = params.get("key").toString(); +// }else { +// key = "A"; +// } +// //获取属性 +// String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); +// Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); +// Long deptId; +// if(params!=null && ObjectUtil.isNotEmpty(params.get("deptId"))){ +// deptId = Long.parseLong(params.get("deptId").toString()); +// ArrayList longs1 = new ArrayList<>(); +// longs1.add(deptId); +// List bindCodeDTOS = publicBindCodeService.getByDeptIds(deptId); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// for (Map.Entry entry : configValueMap.entrySet()) { +// if(entry.getKey().equals(key)){ +// PublicEnergyConsumptionDTO publicEnergyConsumptionDTOS = new PublicEnergyConsumptionDTO(); +// PublicEnergyConsumptionDTO publicEnergyConsumptionDTOS1 = new PublicEnergyConsumptionDTO(); +// PublicAnnualConsumptionRatioDTO publicAnnualConsumptionRatioDTO = new PublicAnnualConsumptionRatioDTO(); +// publicAnnualConsumptionRatioDTO.setKey(entry.getKey()); +// publicAnnualConsumptionRatioDTO.setKeyName(entry.getValue()); +// //计算今年用量 +// BigDecimal value = tsKvService.getValue(entry.getKey(),entityIds,yearStartTime,yearEndTime); +// //今年-赋值 +// publicEnergyConsumptionDTOS.setDate(year); +// //今年用量-赋值 +// publicEnergyConsumptionDTOS.setValue(value); +// //计算去年用量 +// BigDecimal values = tsKvService.getValue(entry.getKey(),entityIds,yearStartTimes,yearEndTimes); +// //去年-赋值 +// publicEnergyConsumptionDTOS1.setDate(lastYear); +// //去年用量-赋值 +// publicEnergyConsumptionDTOS1.setValue(values); +// publicAnnualConsumptionRatioDTO.setCurrent(publicEnergyConsumptionDTOS); +// publicAnnualConsumptionRatioDTO.setLast(publicEnergyConsumptionDTOS1); +// publicAnnualConsumptionRatioDTOS.add(publicAnnualConsumptionRatioDTO); +// } +// } +// } + return publicAnnualConsumptionRatioDTOS; + } + + /** + * 月综合用能结构 + * @param params + * @return + */ + @Override + public List getMonthlyIntegratedEnergyUseStructure(Map params) { + ArrayList publicMonthlyIntegratedEnergyUseStructureDTOS = new ArrayList<>(); +// String year; +// Long monthStartTime; +// Long monthEndTime; +// if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ +// year = params.get("year").toString(); +// String years = TimeUtils.getCurrentYear(); +// if(years.equals(year)){ +// int todayMonth = TimeUtils.getTodayMonth(); +// monthStartTime = TimeUtils.dateToStamp(year + "-"+todayMonth+"-01 00:00:00"); +// monthEndTime = TimeUtils.dateToStamp(year + "-"+todayMonth+"-31 00:00:00"); +// }else { +// monthStartTime = TimeUtils.dateToStamp(year + "-"+12+"-01 00:00:00"); +// monthEndTime = TimeUtils.dateToStamp(year + "-"+12+"-31 00:00:00"); +// } +// }else{ +// year = TimeUtils.getCurrentYear(); +// //当前月份 +// int todayMonth = TimeUtils.getTodayMonth(); +// monthStartTime = TimeUtils.dateToStamp(year + "-"+todayMonth+"-01 00:00:00"); +// monthEndTime = TimeUtils.dateToStamp(year + "-"+todayMonth+"-31 00:00:00"); +// } +// //获取属性 +// String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); +// Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); +// Long deptId; +// if(params!=null && ObjectUtil.isNotEmpty(params.get("deptId"))){ +// deptId = Long.parseLong(params.get("deptId").toString()); +// ArrayList longs1 = new ArrayList<>(); +// longs1.add(deptId); +// List bindCodeDTOS = publicBindCodeService.getByDeptIds(deptId); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// //计算总和 +// BigDecimal total = BigDecimal.ZERO; +// for (Map.Entry entry : configValueMap.entrySet()) { +// PublicMonthlyIntegratedEnergyUseStructureDTO publicMonthlyIntegratedEnergyUseStructureDTO = new PublicMonthlyIntegratedEnergyUseStructureDTO(); +// publicMonthlyIntegratedEnergyUseStructureDTO.setKey(entry.getKey()); +// publicMonthlyIntegratedEnergyUseStructureDTO.setKeyName(entry.getValue()); +// // 计算今年用电量 +// BigDecimal value = tsKvService.getValue(entry.getKey(), entityIds, monthStartTime, monthEndTime); +// // 判断是否为null,使用Optional处理 +// BigDecimal nullSafeValue = Optional.ofNullable(value).orElse(BigDecimal.ZERO); +// // 今年用电量赋值 +// publicMonthlyIntegratedEnergyUseStructureDTO.setValues(nullSafeValue); +// // 计算总和 +// total = total.add(nullSafeValue); +// // 计算占比 +// if (total.compareTo(BigDecimal.ZERO) != 0) { +// BigDecimal percentage = nullSafeValue.divide(total, 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)); +// publicMonthlyIntegratedEnergyUseStructureDTO.setProportionSituation(percentage); +// } else { +// publicMonthlyIntegratedEnergyUseStructureDTO.setProportionSituation(BigDecimal.ZERO); +// } +// publicMonthlyIntegratedEnergyUseStructureDTOS.add(publicMonthlyIntegratedEnergyUseStructureDTO); +// } +// } + return publicMonthlyIntegratedEnergyUseStructureDTOS; + } + + /** + * 24小时用电趋势 + * @param params + * @return + */ + @Override + public PublicHourElectricityUsageTrendsDTO getHourElectricityUsageTrends(Map params) { + PublicHourElectricityUsageTrendsDTO publicHourElectricityUsageTrendsDTO = new PublicHourElectricityUsageTrendsDTO(); +// ArrayList publicEnergyConsumptionDTOS = new ArrayList<>(); +// String keyStr = params.get("key")+"am"; +// String startTime = (String) params.get("startTime"); +// String endTime = (String) params.get("endTime"); +// Long deptId = Long.parseLong((String) params.get("deptId")); +// if (StringUtils.isBlank(startTime) || StringUtils.isBlank(endTime)) { +// throw new RenException("查询日期格式不正确"); +// } +// if (StringUtils.isBlank(keyStr)) { +// throw new RenException("设备Key不能为空"); +// } +// if (StringUtils.isBlank(deptId.toString())) { +// throw new RenException("机构id不能为空"); +// } +// Long startDate = TimeUtils.dateToStamp(startTime); +// Long endDate = TimeUtils.dateToStamp(endTime); +// if (!startTime.substring(0,10).equals(endTime.substring(0,10))) { +// throw new RenException("请求开始时间和结束时间必须在同一天"); +// } +// +// ArrayList longs = new ArrayList<>(); +// longs.add(deptId); +// ArrayList keys = new ArrayList<>(); +// keys.add(keyStr); +// //当前总能耗 +// List chargingStationEnergy = publicBindCodeService.getListByDeptIds("total_energy", longs); +// List collect3 = chargingStationEnergy.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(collect3)){ +// return publicHourElectricityUsageTrendsDTO; +// } +// EntityId entityid = new DeviceId(UUID.fromString(collect3.get(0))); +// +//// List> timeseriesByTimeSlot = restClientUtils.getTimeseriesByTimeSlot(entityid, keys, +//// startDate - 1000, endDate, SortOrder.Direction.ASC); +//// for (Map map : timeseriesByTimeSlot) { +//// for (Map.Entry stringObjectEntry : map.entrySet()) { +//// if(stringObjectEntry.getKey().equals("list")){ +//// //list对应的value值 +//// Object value = stringObjectEntry.getValue(); +//// String s = JSONObject.toJSONString(value); +//// //序列化成list集合 +//// JSONArray jsonArray = JSONObject.parseArray(s); +//// for (Object o : jsonArray) { +//// String s1 = JSONObject.toJSONString(o); +//// JSONObject jsonObject = JSONObject.parseObject(s1); +//// PublicEnergyConsumptionDTO publicEnergyConsumptionDTO = new PublicEnergyConsumptionDTO(); +//// for (Map.Entry objectEntry : jsonObject.entrySet()) { +//// if(objectEntry.getKey().equals("time")){ +//// publicEnergyConsumptionDTO.setDate(objectEntry.getValue().toString()); +//// } +//// if(objectEntry.getKey().equals("value")){ +//// publicEnergyConsumptionDTO.setValue(new BigDecimal(objectEntry.getValue().toString())); +//// } +//// } +//// publicEnergyConsumptionDTOS.add(publicEnergyConsumptionDTO); +//// } +//// } +//// } +//// } +// publicHourElectricityUsageTrendsDTO.setHourElectricity(publicEnergyConsumptionDTOS); + return publicHourElectricityUsageTrendsDTO; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEnergyWarningServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEnergyWarningServiceImpl.java new file mode 100644 index 0000000..8a0ac4b --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicEnergyWarningServiceImpl.java @@ -0,0 +1,322 @@ +package com.thing.publicorg.publicboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; + +import com.google.common.collect.Lists; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicEnergyWarningService; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; + +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.annotation.Resource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 昆山用能预警 + * + * @author xiezw + * @since 2023-08-08 + */ +@Service +public class PublicEnergyWarningServiceImpl implements PublicEnergyWarningService { + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private PublicOrgLimitService publicOrgLimitService; + + @Autowired + private SysDeptService sysDeptService; + + @Autowired + private SysTenantService sysTenantService; + + @Autowired + private TsKvService tsKvService; + + @Resource + private ThingManageContextService thingManageContextService; + + @Override + public PageData getEnergyWarningPageList(PublicEnergyWarningRequestDTO dto) { + List taotalList = getTotalDataList(dto); + List resultList = publicOrgService.startPage(taotalList, Integer.valueOf(dto.getPage()), Integer.valueOf(dto.getLimit())); + + if (CollectionUtils.isEmpty(resultList)) { + return new PageData<>(Lists.newArrayList(), 0); + } + + PageData resultPageData = new PageData<>(resultList, taotalList.size()); + + return resultPageData; + } + + @Override + public List getEnergyWarningList(PublicEnergyWarningRequestDTO dto) { + List taotalList = getTotalDataList(dto); + return taotalList; + } + + @Override + public PublicOrgTotalConsumptionAnalyseDTO getOrgTotalConsumptionAnalyse(Map params) { + PublicOrgTotalConsumptionAnalyseDTO result = new PublicOrgTotalConsumptionAnalyseDTO(); + + return result; + } + + @Override + public List getOrgUnitBuildAreaConsumption(Map params) { + List result = new ArrayList<>(); + return result; + } + + @Override + public List getOrgUnitBuildPersonConsumption(Map params) { + List result = new ArrayList<>(); + + return result; + } + + @Override + public PublicOrgYearConsumptionCompareDTO getOrgYearDataCompare(Map params) { + PublicOrgYearConsumptionCompareDTO result = new PublicOrgYearConsumptionCompareDTO(); + + return result; + } + + @Override + public PublicOrgWarningCountDTO getWarningCount(Map params) { + PublicOrgWarningCountDTO result = new PublicOrgWarningCountDTO(); + return result; + } + + @Override + public PublicOrgConsumptionAnalyseDataDTO getOrgConsumptionAnalyse(Map params) { + PublicOrgConsumptionAnalyseDataDTO result = new PublicOrgConsumptionAnalyseDataDTO(); + List buildAreaConsumption = new ArrayList<>(); + + List buildPersonConsumption = new ArrayList<>(); + + List dataCenterConsumption = new ArrayList<>(); + + List canteenUserConsumption = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + + SysDeptEntity sysDeptEntity = sysDeptService.getDeptVaildPermission(); + List currentMonths; + String dateType = params.get("dateType").toString(); + if ("0".equals(dateType)){ + currentMonths = DateTimeUtils.generateYearList(year, 5); + }else{ + currentMonths = DateTimeUtils.getMonthList(year); + } + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); +// Long tenantCode = 1731546878167683074L; + SysTenantDTO tenantCode1 = sysTenantService.getTenantCode(tenantCode); + ArrayList deptIds = new ArrayList<>(); + deptIds.add(tenantCode1.getTenantCode()); + //获取机构限制阈值 + Map limitMap = publicOrgLimitService.getOrgLimitMap(tenantCode1.getTenantCode()); + BigDecimal yearUnitBuildAreaLimitValue = null; + BigDecimal monthUnitBuildAreaLimitValue = null; + BigDecimal yearUnitBuildPersonLimitValue = null; + BigDecimal monthUnitBuildPersonLimitValue =null; + if (ObjectUtil.isNotEmpty(limitMap)){ + //年单位面积约束值 + yearUnitBuildAreaLimitValue = limitMap.get(Constant.ENERGY_PERBULID).getYearValue(); + //月单位面积约束值 + monthUnitBuildAreaLimitValue = limitMap.get(Constant.ENERGY_PERBULID).getMonthValue(); + + if (ObjectUtil.isNotEmpty(limitMap.get(Constant.ENERGY_PERPERSON))){ + //年单位人数约束值 + yearUnitBuildPersonLimitValue = limitMap.get(Constant.ENERGY_PERPERSON).getYearValue(); + } + if (ObjectUtil.isNotEmpty(limitMap.get(Constant.ENERGY_PERPERSON))){ + //月单位人数约束值 + monthUnitBuildPersonLimitValue = limitMap.get(Constant.ENERGY_PERPERSON).getMonthValue(); + } + } + + PublicOrgDetailInfoDTO orgDetailInfoDTO = publicOrgService.getByDeptId(tenantCode1.getTenantCode(),year); + if (orgDetailInfoDTO==null || orgDetailInfoDTO.getId()==null){ + return null; + } + +// //建筑面积 +// BigDecimal areaValue=null; +// BigDecimal sumUser = null; +// +// if (orgDetailInfoDTO!=null){ +// areaValue = orgDetailInfoDTO.getOrgDetail().getBuildArea(); +// //用能人数 +// Integer sum = orgDetailInfoDTO.getOrgDetail().getDayUserCount(); +// +// if (sum !=null){ +// sumUser = new BigDecimal(sum); +// } +// } + for (String month : currentMonths){ + Long startTime; + Long endTime; + PublicOrgConsumptionAnalyseDTO orgConsumptionAnalyseDTO = new PublicOrgConsumptionAnalyseDTO(); + orgConsumptionAnalyseDTO.setDate(month); + PublicOrgConsumptionAnalyseDTO orgConsumptionAnalyseDTO1 = new PublicOrgConsumptionAnalyseDTO(); + orgConsumptionAnalyseDTO1.setDate(month); + PublicOrgDataCenterConsumptionAnalyseDTO orgConsumptionAnalyseDTO2 = new PublicOrgDataCenterConsumptionAnalyseDTO(); + orgConsumptionAnalyseDTO2.setDate(month); + PublicOrgConsumptionAnalyseDTO orgConsumptionAnalyseDTO3 = new PublicOrgConsumptionAnalyseDTO(); + orgConsumptionAnalyseDTO3.setDate(month); + if (orgDetailInfoDTO.getQuotaType().equals(Constant.CENTRALIZED_OFFICE_AREA)){ + //年数据中心约束值 + BigDecimal yearUnitDataCenterLimitValue = limitMap.get(Constant.ENERGY_UTILIZATION_EFFICIENCY).getYearValue(); + //月数据中心约束值 + BigDecimal monthUnitDataCenterLimitValue = limitMap.get(Constant.ENERGY_UTILIZATION_EFFICIENCY).getMonthValue(); + + //年餐厅人数约束值 + BigDecimal yearUnitCanteenPersonLimitValue = limitMap.get(Constant.ENERGY_PERCAFETERIA_PERSON).getYearValue(); + //月餐厅人数约束值 + BigDecimal monthUnitCanteenPersonLimitValue = limitMap.get(Constant.ENERGY_PERCAFETERIA_PERSON).getMonthValue(); + if ("0".equals(dateType)){ + orgConsumptionAnalyseDTO2.setLimitValue(yearUnitDataCenterLimitValue); + orgConsumptionAnalyseDTO3.setLimitValue(yearUnitCanteenPersonLimitValue); + }else{ + orgConsumptionAnalyseDTO2.setLimitValue(monthUnitDataCenterLimitValue); + orgConsumptionAnalyseDTO3.setLimitValue(monthUnitCanteenPersonLimitValue); + } + } + if ("0".equals(dateType)){ + startTime = DateTimeUtils.dateToStamp(month +"-01-01 00:00:00"); + endTime = DateTimeUtils.dateToStamp(month +"-12-31 23:59:59"); + orgConsumptionAnalyseDTO.setLimitValue(yearUnitBuildAreaLimitValue); + orgConsumptionAnalyseDTO1.setLimitValue(yearUnitBuildPersonLimitValue); + }else{ + startTime = DateTimeUtils.getBeginTime(month+"-01 00:00:00","0"); + endTime = DateTimeUtils.getEndTime(month+"-01 00:00:00","0"); + orgConsumptionAnalyseDTO.setLimitValue(monthUnitBuildAreaLimitValue); + orgConsumptionAnalyseDTO1.setLimitValue(monthUnitBuildPersonLimitValue); + } + + if(ObjectUtil.isNotEmpty(tenantCode1)){ + + Optional optional = thingManageContextService.findViewByCode(tenantCode1.getThingCode(), UserContext.getRealTenantCode(), true); + + if(optional.isPresent()) { + Collection keyList = new ArrayList<>(); + Collection keyList1 = new ArrayList<>(); + //月 + keyList.add("tce_perBuildAreamm"); + keyList.add("tce_perCapitaBuidmm"); + keyList.add("tce_dataCentermm"); + keyList.add("tce_perCanteenmm"); + //年 + keyList1.add("tce_perBuildAreayy"); + keyList1.add("tce_perCapitaBuidyy"); + keyList1.add("tce_dataCenteryy"); + keyList1.add("tce_perCanteenyy"); + Map> thingAttrParam = new HashMap<>(); + if("1".equals(dateType)){ + thingAttrParam.put(optional.get().getEntityCode(),keyList); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, startTime - 1000, endTime, true); + Map> groupedData = timeseries.stream() + .collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if(CollectionUtil.isNotEmpty(timeseries)){ + if(groupedData.get("tce_perBuildAreamm")!=null){ + //单位建筑面积能耗 + TsKvDTO perBuildAreamm = groupedData.get("tce_perBuildAreamm").get(0); + orgConsumptionAnalyseDTO.setValue(new BigDecimal(perBuildAreamm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_perCapitaBuidmm")!=null){ + //人均建筑能耗 + TsKvDTO perCapitaBuidmm = groupedData.get("tce_perCapitaBuidmm").get(0); + orgConsumptionAnalyseDTO1.setValue(new BigDecimal(perCapitaBuidmm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_dataCentermm")!=null){ + //数据中心能耗 + TsKvDTO dataCentermm = groupedData.get("tce_dataCentermm").get(0); + orgConsumptionAnalyseDTO2.setValue(new BigDecimal(dataCentermm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_perCanteenmm")!=null){ + //食堂人均能耗 + TsKvDTO perCanteenmm = groupedData.get("tce_perCanteenmm").get(0); + orgConsumptionAnalyseDTO3.setValue(new BigDecimal(perCanteenmm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + } + }else if("0".equals(dateType)) { + thingAttrParam.put(optional.get().getEntityCode(),keyList1); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, startTime - 1000, endTime, true); + Map> groupedData = timeseries.stream() + .collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if(CollectionUtil.isNotEmpty(timeseries)){ + if(groupedData.get("tce_perBuildAreayy")!=null){ + //单位建筑面积能耗 + TsKvDTO perBuildAreamm = groupedData.get("tce_perBuildAreayy").get(0); + orgConsumptionAnalyseDTO.setValue(new BigDecimal(perBuildAreamm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_perCapitaBuidyy")!=null){ + //人均建筑能耗 + TsKvDTO perCapitaBuidmm = groupedData.get("tce_perCapitaBuidyy").get(0); + orgConsumptionAnalyseDTO1.setValue(new BigDecimal(perCapitaBuidmm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_dataCenteryy")!=null){ + //数据中心能耗 + TsKvDTO dataCentermm = groupedData.get("tce_dataCenteryy").get(0); + orgConsumptionAnalyseDTO2.setValue(new BigDecimal(dataCentermm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + if(groupedData.get("tce_perCanteenyy")!=null){ + //食堂人均能耗 + TsKvDTO perCanteenmm = groupedData.get("tce_perCanteenyy").get(0); + orgConsumptionAnalyseDTO3.setValue(new BigDecimal(perCanteenmm.getVal()).setScale(2,BigDecimal.ROUND_HALF_UP)); + } + } + } + } + } + buildAreaConsumption.add(orgConsumptionAnalyseDTO); + buildPersonConsumption.add(orgConsumptionAnalyseDTO1); + dataCenterConsumption.add(orgConsumptionAnalyseDTO2); + canteenUserConsumption.add(orgConsumptionAnalyseDTO3); + } + result.setBuildAreaConsumption(buildAreaConsumption); + result.setBuildPersonConsumption(buildPersonConsumption); + result.setDataCenterConsumption(dataCenterConsumption); + result.setCanteenUserConsumption(canteenUserConsumption); + return result; + } + + + private List getTotalDataList(PublicEnergyWarningRequestDTO dto) { + List result = new ArrayList<>(); + Comparator comparator = Comparator.comparing(PublicEnergyWarningDataDTO::getTotalConsumption, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) + .collect(Collectors.toList()); + return sortedList; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicMonitoringServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicMonitoringServiceImpl.java new file mode 100644 index 0000000..db85d57 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicMonitoringServiceImpl.java @@ -0,0 +1,304 @@ +package com.thing.publicorg.publicboard.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.publicorg.org.dto.PublicOrgDTO; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import com.thing.publicorg.publicboard.dto.*; +import com.thing.publicorg.publicboard.service.PublicMonitoringService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.sql.Timestamp; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class PublicMonitoringServiceImpl implements PublicMonitoringService { + + @Autowired + private SysDictTypeService iotDictTypeService; + + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private SysDictDataService iotDictDataService; + + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + @Autowired + private SysParamsService sysParamsService; + + + + /** + * 行业代码-总能耗-建筑能耗 + * @param params + * @return + */ + @Override + public List getDetailsEnergy(Map params) { + List result = new ArrayList<>(); + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + //开始时间 + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Timestamp yearStartTimes = new Timestamp(yearStartTime); + //结束时间 + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + Timestamp yearEndTimes = new Timestamp(yearEndTime); + //获取所有机构 + List orgs = publicOrgService.getAllOrg(); + //机构根据业务代码分组 + Map> orgMap = orgs.stream() + .collect(Collectors.groupingBy(PublicOrgDTO::getIndustryCode, Collectors.mapping(PublicOrgDTO::getId, Collectors.toList()))); + for (Map.Entry> entry : orgMap.entrySet()){ + PublicMonitorDTO publicMonitorDTO = new PublicMonitorDTO(); + //根据行业代码-字典值查询 + SysDictDataDTO quotaTypeData = iotDictDataService.getDictDataNameByDictTypes(entry.getKey()); + //获取行业代码-字典值 + publicMonitorDTO.setIndustryCode(entry.getKey()); + //获取行业代码-名称 + publicMonitorDTO.setIndustryCodeName(quotaTypeData.getDictLabel()); + //获取总能耗 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",entry.getValue()); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(entityIds)){ +// //手入能耗 +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,entry.getValue()); +// //获取指定时间内的总能耗 +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,yearStartTime,yearEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// if(value != null){ +// publicMonitorDTO.setTotalEnergy(value.divide(new BigDecimal(10000),2,RoundingMode.HALF_UP)); +// }else { +// // 处理value为null的情况 +// publicMonitorDTO.setTotalEnergy(BigDecimal.ZERO); +// } +// } +// +// //获取建筑能耗 +// List bindCodeDTOList = publicBindCodeService.getListByDeptIds("bulid_energy",entry.getValue()); +// List collect = bindCodeDTOList.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isNotEmpty(collect)){ +// //获取指定时间内的建筑能耗 +// BigDecimal values = tsKvService.getTotalConsumption(collect,yearStartTime,yearEndTime); +// if(values != null){ +// //BuildingEnergy-建筑能耗 +// publicMonitorDTO.setBuildingEnergy(values.divide(new BigDecimal(10000),2,RoundingMode.HALF_UP)); +// }else { +// // 处理values为null的情况 +// publicMonitorDTO.setBuildingEnergy(BigDecimal.ZERO); +// } +// } + result.add(publicMonitorDTO); + } + List sortedList = result.stream() + .sorted(Comparator.comparing(PublicMonitorDTO::getTotalEnergy, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + /** + * 单位-总能耗 + * @param params + * @return + */ + @Override + public List getDetailsUnits(Map params) { + ArrayList publicUnitsDTOS = new ArrayList<>(); + //根据时间查询-如果有按照自己写的时间查询-如果没有按照最新一年进行查询 + String year; + if (params!=null && ObjectUtil.isNotEmpty(params.get("year"))){ + year = params.get("year").toString(); + }else{ + year = DateTimeUtils.getCurrentYear(); + } + //开始时间 + Long yearStartTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + Timestamp yearStartTimes = new Timestamp(yearStartTime); + //结束时间 + Long yearEndTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + Timestamp yearEndTimes = new Timestamp(yearEndTime); + String unitsName = null; + //判断unitsName-单位名称不能为空 + if (params!=null && ObjectUtil.isNotEmpty(params.get("unitsName"))){ + unitsName = params.get("unitsName").toString(); + } + String IndustryCode = null; + //判断unitsName-单位名称不能为空 + if (params!=null && ObjectUtil.isNotEmpty(params.get("IndustryCode"))){ + IndustryCode = params.get("IndustryCode").toString(); + } + List publicOrgDTOS = publicOrgService.getUnitsIndustryCode(unitsName,IndustryCode,yearStartTimes,yearEndTimes); + for (PublicOrgDTO publicOrgDTO : publicOrgDTOS) { + PublicUnitsDTO publicUnitsDTO = new PublicUnitsDTO(); + //单位名称-赋值 + publicUnitsDTO.setUnitsName(publicOrgDTO.getOrgName()); + //单位编号-赋值 + publicUnitsDTO.setDeptId(publicOrgDTO.getDeptId()); + ArrayList strings = new ArrayList<>(); + strings.add(publicOrgDTO.getDeptId()); +// //获取总能耗 +// List bindCodeDTOS = publicBindCodeService.getListByDeptIds("total_energy",strings); +// List entityIds = bindCodeDTOS.stream().map(PublicBindCodeDTO::getEntityId).collect(Collectors.toList()); +// if (CollectionUtil.isEmpty(entityIds)){ +// publicUnitsDTOS.add(publicUnitsDTO); +// continue; +// } +// //手入能耗 +// PublicConsumptionAndCarbonDTO consumptionAndCarbon = publicEnterValueService.getDepetYearConsumptionAndCarbon(year,strings); +// //获取指定时间内的总能耗 +// BigDecimal value = tsKvService.getTotalConsumption(entityIds,yearStartTime,yearEndTime); +// if (consumptionAndCarbon!=null && consumptionAndCarbon.getConsumption()!=null){ +// value = value==null? BigDecimal.ZERO.add(consumptionAndCarbon.getConsumption()) : value.add(consumptionAndCarbon.getConsumption()); +// } +// if(value!=null){ +// //TotalEnergy-总能耗 +// publicUnitsDTO.setTotalEnergy(value.divide(new BigDecimal(10000),2,RoundingMode.HALF_UP)); +// }else { +// publicUnitsDTO.setTotalEnergy(BigDecimal.ZERO); +// } +// //机构坐标-赋值 +// publicUnitsDTO.setCoordinate(publicOrgDTO.getCoordinate()); + publicUnitsDTOS.add(publicUnitsDTO); + } + List sortedList = publicUnitsDTOS.stream() + .sorted(Comparator.comparing(PublicUnitsDTO::getTotalEnergy, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + /** + * 单位-建筑平均能耗 + * @param params + * @return + */ + @Override + public PublicCentralizedOfficeAreaDTO getConsumption(Map params) { + String year; + if (params != null && ObjectUtil.isNotEmpty(params.get("year"))) { + year = params.get("year").toString(); + } else { + year = DateTimeUtils.getCurrentYear(); + } + Long yearStartTime = DateTimeUtils.dateToStamp(year + "-01-01 00:00:00"); + Timestamp yearStartTimes = new Timestamp(yearStartTime); + Long yearEndTime = DateTimeUtils.dateToStamp(year + "-12-31 23:59:59"); + Timestamp yearEndTimes = new Timestamp(yearEndTime); + Long monthStartTime; + Long monthEndTime; + int todayMonth = 0; + if (params != null && ObjectUtil.isNotEmpty(params.get("year"))) { + year = params.get("year").toString(); + String years = DateTimeUtils.getCurrentYear(); + if (years.equals(year)) { + todayMonth = DateTimeUtils.getTodayMonth(); + monthStartTime = DateTimeUtils.dateToStamp(year + "-" + todayMonth + "-01 00:00:00"); + monthEndTime = DateTimeUtils.dateToStamp(year + "-" + todayMonth + "-31 00:00:00"); + } else { + monthStartTime = DateTimeUtils.dateToStamp(year + "-" + 12 + "-01 00:00:00"); + monthEndTime = DateTimeUtils.dateToStamp(year + "-" + 12 + "-31 00:00:00"); + } + } else { + year = DateTimeUtils.getCurrentYear(); + //当前月份 + todayMonth = DateTimeUtils.getTodayMonth(); + monthStartTime = DateTimeUtils.dateToStamp(year + "-" + todayMonth + "-01 00:00:00"); + monthEndTime = DateTimeUtils.dateToStamp(year + "-" + todayMonth + "-31 00:00:00"); + } + //获取所有机构 + List orgs = publicOrgService.getCurrentAllOrg(yearStartTimes, yearEndTimes); + String unitsName = null; + // 判断unitsName-单位名称不能为空 + if (params != null && ObjectUtil.isNotEmpty(params.get("unitsName"))) { + unitsName = params.get("unitsName").toString(); + } + List filteredOrgs = orgs; + if (unitsName != null) { + //如果unitsName-单位名称存在,包含指定unitsName的PublicOrgDTO对象 + filteredOrgs = orgs.stream().filter(name -> name.getOrgName().contains((CharSequence) params.get("unitsName"))).collect(Collectors.toList()); + } + PublicCentralizedOfficeAreaDTO publicCentralizedOfficeArea = new PublicCentralizedOfficeAreaDTO(); + return publicCentralizedOfficeArea; + } + + + /** + * 用电种类 + * @param params + * @return + */ + @Override + public List TypeEnergyUse(Map params) { + List result = new ArrayList<>(); + //获取属性 + String configValue = sysParamsService.getValue("ATTRIBUTE_KEY_NAME"); + Map configValueMap = JSONObject.parseObject(configValue, HashMap.class); + //总能耗-赋值 + PublicTypeUseDTO publicTypeUseDTOs = new PublicTypeUseDTO(); + publicTypeUseDTOs.setKey("total"); + publicTypeUseDTOs.setKeyName("总能耗"); + result.add(publicTypeUseDTOs); + for (Map.Entry entry : configValueMap.entrySet()){ + if(!entry.getKey().equals("C")){ + //用能种类-赋值 + PublicTypeUseDTO publicTypeUseDTO = new PublicTypeUseDTO(); + publicTypeUseDTO.setKey(entry.getKey()); + publicTypeUseDTO.setKeyName(entry.getValue()); + result.add(publicTypeUseDTO); + } + } + //水-赋值 + PublicTypeUseDTO publicTypeUseDTO = new PublicTypeUseDTO(); + publicTypeUseDTO.setKey("B"); + publicTypeUseDTO.setKeyName("水"); + result.add(publicTypeUseDTO); + //水-赋值 + PublicTypeUseDTO publicTypeUsesDTO = new PublicTypeUseDTO(); + publicTypeUsesDTO.setKey("C"); + publicTypeUsesDTO.setKeyName("天然气"); + result.add(publicTypeUsesDTO); + //photovoltaic_energy-光伏 + PublicTypeUseDTO publicTypeUseDTOS = new PublicTypeUseDTO(); + publicTypeUseDTOS.setKey("photovoltaic_energy"); + publicTypeUseDTOS.setKeyName("光伏"); + result.add(publicTypeUseDTOS); + return result; + } + + + + + /** + * 用能种类-年度 + * @param params + * @return + */ + @Override + public List TypeEnergyUseYear(Map params) { + List result = new ArrayList<>(); + return result; + } + + + + +} + diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicPhotovoltaicServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicPhotovoltaicServiceImpl.java new file mode 100644 index 0000000..6e72fc2 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicPhotovoltaicServiceImpl.java @@ -0,0 +1,79 @@ +package com.thing.publicorg.publicboard.service.impl; + + +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.orgdetail.service.PublicOrgDetailService; +import com.thing.publicorg.publicboard.dto.PublicElectricityUnitDTO; +import com.thing.publicorg.publicboard.dto.PublicInstalledCapacityDTO; +import com.thing.publicorg.publicboard.dto.PublicPhotovoltaicGenerationDTO; +import com.thing.publicorg.publicboard.service.PublicPhotovoltaicService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +@Service +public class PublicPhotovoltaicServiceImpl implements PublicPhotovoltaicService { + + @Autowired + private PublicOrgService publicOrgService; + + @Autowired + private SysDictDataService iotDictDataService; + +// @Autowired +// private PublicBindCodeService publicBindCodeService; +// +// @Autowired +// private TsKvsService tsKvService; + + @Autowired + private PublicOrgDetailService publicOrgDetailService; + + /** + * 行业装机容量-发电量 + * @param params + * @return + */ + @Override + public List getInstalledCapacityElectricityGeneration(Map params) { + List result = new ArrayList<>(); + List sortedList = result.stream() + .sorted(Comparator.comparing(PublicInstalledCapacityDTO::getElectricityGeneration, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + /** + * 当前地区单位清单 + * @param params + * @return + */ + @Override + public List getListUnits(Map params) { + List result = new ArrayList<>(); + List sortedList = result.stream() + .sorted(Comparator.comparing(PublicElectricityUnitDTO::getElectricityGeneration, + Comparator.nullsLast(Comparator.reverseOrder()))) + .collect(Collectors.toList()); + return sortedList; + } + + + /** + * 单位-年度光伏发电趋势-光伏发电同比分析 + * @param params + * @return + */ + @Override + public PublicPhotovoltaicGenerationDTO getAnnualPhotovoltaicGeneration(Map params) { + PublicPhotovoltaicGenerationDTO capacityGenerationDTO = new PublicPhotovoltaicGenerationDTO(); + return capacityGenerationDTO; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicReportServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicReportServiceImpl.java new file mode 100644 index 0000000..e88117f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicboard/service/impl/PublicReportServiceImpl.java @@ -0,0 +1,333 @@ +package com.thing.publicorg.publicboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.publicorg.energysavingreport.dto.PublicEnergySavingReportDTO; +import com.thing.publicorg.energysavingreport.service.PublicEnergySavingReportService; +import com.thing.publicorg.org.service.PublicOrgService; +import com.thing.publicorg.publicboard.dto.PublicOrgDetailInfoDTO; +import com.thing.publicorg.publicboard.dto.PublicReportDataDTO; +import com.thing.publicorg.publicboard.service.PublicReportService; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.annotation.Resource; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 昆山用能预警 + * + * @author xiezw + * @since 2023-08-08 + */ +@Service +public class PublicReportServiceImpl implements PublicReportService { + @Resource + private PublicOrgService publicOrgService; + @Resource + private SysDictDataService iotDictDataService; + @Resource + private PublicEnergySavingReportService publicEnergySavingReportService; + @Resource + private SysTenantService sysTenantService; + @Resource + private ThingManageContextService thingManageContextService; + @Resource + private TsKvService tsKvService; + + + + @Override + public PageData getEnergyReportPageList(Map params) { + List totalList = getTotalReportDataList(params); + List resultList = publicOrgService.startPage(totalList, Integer.valueOf(params.get("page").toString()), Integer.valueOf(params.get("limit").toString())); + if (CollectionUtils.isEmpty(resultList)) { + return new PageData<>(Lists.newArrayList(), 0); + } + return new PageData<>(resultList, totalList.size()); + } + + @Override + public List getEnergyReportList(Map params) { + return getTotalReportDataList(params); + } + + @Override + public List getOrgEnergyReportList(Map params) { + return getTotalReportDataListNew(params); + } + + private List getTotalReportDataListNew(Map params) { + List result = new ArrayList<>(); + String dateType = params.get("dateType").toString(); + String date = params.get("date").toString(); + Long startTime = null; + Long endTime = null; + Long beginTime = null; + Long endTimes = null; + String year; + if ("0".equals(dateType)){ + year = date; + startTime = DateTimeUtils.dateToStamp(year +"-01-01 00:00:00"); + endTime = DateTimeUtils.dateToStamp(year +"-12-31 23:59:59"); + Integer integer = Integer.valueOf(year.substring(0, 4)); + Date yearFirst = DateTimeUtils.getYearFirst(integer); + SimpleDateFormat simpleDateFormat = new SimpleDateFormat(DateTimeUtils.DATE_TIME_PATTERN_STR); + String format = simpleDateFormat.format(yearFirst); + beginTime = DateTimeUtils.dateToStamp(format) - 1000; + + Date yearLast = DateTimeUtils.getYearLast(Integer.valueOf(integer)); + SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat(DateTimeUtils.DATE_TIME_PATTERN_STR); + String format1 = simpleDateFormat1.format(yearLast); + endTimes = DateTimeUtils.dateToStamp(format1) + 5*1000; + }else{ + year = date.substring(0,4); + startTime = DateTimeUtils.getBeginTime(date+"-01 00:00:00","0"); + endTime = DateTimeUtils.getEndTime(date+"-01 00:00:00","0"); + + beginTime = DateTimeUtils.getBeginTime(date+"-01 00:00:00", DateTimeUtils.DATE_TIME_PATTERN_STR) - 1000; + endTimes = DateTimeUtils.getEndTime(date+"-01 00:00:00",DateTimeUtils.DATE_TIME_PATTERN_STR) + 5*1000; + + } + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); +// Long tenantCode = 1731546878167683074L; + //获取所有机构 + PublicOrgDetailInfoDTO orgDetailInfoDTO = publicOrgService.getByTenantCode(year,tenantCode); + if (orgDetailInfoDTO == null || orgDetailInfoDTO.getId()==null){ + return result; + } + PublicReportDataDTO publicReportDataDTO = new PublicReportDataDTO(); + publicReportDataDTO.setId(orgDetailInfoDTO.getId()); + publicReportDataDTO.setOrgName(orgDetailInfoDTO.getOrgName()); + publicReportDataDTO.setQuotaType(orgDetailInfoDTO.getQuotaType()); + SysDictDataDTO quotaTypeData = iotDictDataService.getDictDataNameByDictType(orgDetailInfoDTO.getQuotaType()); + if (quotaTypeData != null) { + publicReportDataDTO.setQuotaTypeName(quotaTypeData.getDictLabel()); + } + publicReportDataDTO.setIndustryCode(orgDetailInfoDTO.getIndustryCode()); + SysDictDataDTO industryData = iotDictDataService.getIndustryNameByDictType(orgDetailInfoDTO.getIndustryCode()); + if (industryData != null) { + publicReportDataDTO.setIndustryName(industryData.getDictLabel()); + } + + SysTenantDTO tenantCode1 = sysTenantService.getTenantCode(tenantCode); + if (ObjectUtil.isNotEmpty(tenantCode1)) { + Optional optional = thingManageContextService.findViewByCode(tenantCode1.getThingCode(), UserContext.getRealTenantCode(), true); + if(optional.isPresent()) { + + Collection keyList = new ArrayList<>(); + Collection keyList1 = new ArrayList<>(); + //月 + keyList.add("tce_runmm"); + keyList.add("CO2_runmm"); + keyList.add("tce_buildmm"); + keyList.add("CO2_buildmm"); + keyList.add("tce_canteenmm"); + keyList.add("tce_dataCentermm"); + keyList.add("A29mm"); + keyList.add("C6mm"); + keyList.add("B2mm"); + keyList.add("E3mm"); + keyList.add("photovoltaic_A29mm"); + keyList.add("chargPower_A29mm"); + keyList.add("tce_perBuildAreamm"); + keyList.add("tce_perCapitaBuidmm"); + keyList.add("perCap_A29mm"); + keyList.add("perCap_B2mm"); + keyList.add("tce_perCanteenmm"); + keyList.add("EEUEmm"); + //年 + keyList1.add("tce_runyy"); + keyList1.add("CO2_runyy"); + keyList1.add("tce_buildyy"); + keyList1.add("CO2_buildyy"); + keyList1.add("tce_canteenyy"); + keyList1.add("tce_dataCenteryy"); + keyList1.add("A29yy"); + keyList1.add("C6yy"); + keyList.add("B2yy"); + keyList.add("E3yy"); + keyList1.add("photovoltaic_A29yy"); + keyList1.add("chargPower_A29yy"); + keyList1.add("tce_perBuildAreayy"); + keyList1.add("tce_perCapitaBuidyy"); + keyList1.add("perCap_A29yy"); + keyList1.add("perCap_B2yy"); + keyList1.add("tce_perCanteenyy"); + keyList1.add("EEUEyy"); + + Map> thingAttrParam = new HashMap<>(); + if ("1".equals(dateType)) { + thingAttrParam.put(optional.get().getEntityCode(), keyList); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, beginTime, endTimes, true); + if (CollectionUtil.isNotEmpty(timeseries)) { + for (TsKvDTO timesery : timeseries) { + if (timesery.getAttrKey().equals("tce_runmm")) { + //总能耗 + publicReportDataDTO.setTotalConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("CO2_runmm")) { + //总碳排 + publicReportDataDTO.setTotalCarbon(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_buildmm")) { + //建筑能耗 + publicReportDataDTO.setTotalBuildConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("CO2_buildmm")) { + //建筑能耗 + publicReportDataDTO.setTotalBuildConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_canteenmm")) { + //食堂能耗 + publicReportDataDTO.setCanteenConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_dataCentermm")) { + //数据中心能耗 + publicReportDataDTO.setDataCenterConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("A29mm")) { + //电用量 + publicReportDataDTO.setEnergyA(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("C6mm")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("B2mm")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("E3mm")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("photovoltaic_A29mm")) { + //光伏发电量 + publicReportDataDTO.setEnergyPhotovoltaic(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("chargPower_A29mm")) { + //充电桩电量 + publicReportDataDTO.setEnergyChargingStation(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perBuildAreamm")) { + //单位建筑面积能耗 + publicReportDataDTO.setUnitBuildAreaValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perCapitaBuidmm")) { + //人均建筑能耗 + publicReportDataDTO.setUnitBuildPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("perCap_A29mm")) { + //人均用电量 + publicReportDataDTO.setUnitEnergyAPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("perCap_B2mm")) { + //人均用水量 + publicReportDataDTO.setUnitEnergyBPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perCanteenmm")) { + //食堂人均能耗 + publicReportDataDTO.setUnitCanteenPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("EEUEmm")) { + //数据中心电能使用率 + publicReportDataDTO.setUnitDataCenterValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } + } + } + } else if ("0".equals(dateType)) { + thingAttrParam.put(optional.get().getEntityCode(), keyList1); + List timeseries = tsKvService.findTsKvByMultiMap(thingAttrParam, beginTime, endTimes, true); + if (CollectionUtil.isNotEmpty(timeseries)) { + for (TsKvDTO timesery : timeseries) { + if (timesery.getAttrKey().equals("tce_runyy")) { + //总能耗 + publicReportDataDTO.setTotalConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("CO2_runyy")) { + //总碳排 + publicReportDataDTO.setTotalCarbon(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_buildyy")) { + //建筑能耗 + publicReportDataDTO.setTotalBuildConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_canteenyy")) { + //食堂能耗 + publicReportDataDTO.setCanteenConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("CO2_buildyy")) { + //食堂能耗 + publicReportDataDTO.setCanteenConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_dataCenteryy")) { + //数据中心能耗 + publicReportDataDTO.setDataCenterConsumption(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("A29yy")) { + //电用量 + publicReportDataDTO.setEnergyA(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("C6yy")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("B2yy")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("E3yy")) { + //天然气用量 + publicReportDataDTO.setEnergyC(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("photovoltaic_A29yy")) { + //光伏发电量 + publicReportDataDTO.setEnergyPhotovoltaic(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("chargPower_A29yy")) { + //充电桩电量 + publicReportDataDTO.setEnergyChargingStation(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perBuildAreayy")) { + //单位建筑面积能耗 + publicReportDataDTO.setUnitBuildAreaValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perCapitaBuidyy")) { + //人均建筑能耗 + publicReportDataDTO.setUnitBuildPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("perCap_A29yy")) { + //人均用电量 + publicReportDataDTO.setUnitEnergyAPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("perCap_B2yy")) { + //人均用水量 + publicReportDataDTO.setUnitEnergyBPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("tce_perCanteenyy")) { + //食堂人均能耗 + publicReportDataDTO.setUnitCanteenPersonValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } else if (timesery.getAttrKey().equals("EEUEyy")) { + //数据中心电能使用率 + publicReportDataDTO.setUnitDataCenterValue(new BigDecimal(timesery.getVal()).setScale(2, BigDecimal.ROUND_HALF_UP)); + } + } + } + } + } + } + + if (ObjectUtil.isEmpty(orgDetailInfoDTO.getOrgDetail())){ + result.add(publicReportDataDTO); + return result; + }else { + BeanUtils.copyProperties(orgDetailInfoDTO.getOrgDetail(), publicReportDataDTO); + result.add(publicReportDataDTO); + } + //获取最新一条节能报告 + PublicEnergySavingReportDTO savingReportDTO = publicEnergySavingReportService.getByDeptId(orgDetailInfoDTO.getId()); + if (savingReportDTO != null) { + publicReportDataDTO.setUrl(savingReportDTO.getFilePath()); + } + Comparator comparator = Comparator.comparing(PublicReportDataDTO::getTotalConsumption, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) + .collect(Collectors.toList()); + return sortedList; + } + + private List getTotalReportDataList(Map params) { + List result = new ArrayList<>(); + Comparator comparator = Comparator.comparing(PublicReportDataDTO::getTotalConsumption, Comparator.nullsLast(Comparator.reverseOrder())); + List sortedList = result.stream() + .sorted(comparator) + .collect(Collectors.toList()); + return sortedList; + } +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/controller/PublicOrgLimitController.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/controller/PublicOrgLimitController.java new file mode 100644 index 0000000..57bebea --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/controller/PublicOrgLimitController.java @@ -0,0 +1,118 @@ +package com.thing.publicorg.publicorglimit.controller; + + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.excel.PublicOrgLimitExcel; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; + + +/** +* 机构指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@RestController +@RequestMapping("publicorglimit") +@Tag(name="机构指标阈值") +public class PublicOrgLimitController { + @Autowired + private PublicOrgLimitService publicOrgLimitService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) +// @RequiresPermissions("publicorglimit:publicorglimit:page") + public Result> page( @RequestParam Map params){ + PageData page = publicOrgLimitService.getPageData(params,PublicOrgLimitDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") +// @RequiresPermissions("publicorglimit:publicorglimit:info") + public Result get(@PathVariable("id") Long id){ + PublicOrgLimitDTO data = publicOrgLimitService.getByIdAs(id,PublicOrgLimitDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") +// @RequiresPermissions("publicorglimit:publicorglimit:save") + public Result save(@RequestBody PublicOrgLimitDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + publicOrgLimitService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") +// @RequiresPermissions("publicorglimit:publicorglimit:update") + public Result update(@RequestBody PublicOrgLimitDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + publicOrgLimitService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") +// @RequiresPermissions("publicorglimit:publicorglimit:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + publicOrgLimitService.batchDelete(ids); + + return new Result(); + } + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @RequiresPermissions("publicorglimit:publicorglimit:export") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = publicOrgLimitService.listAs(params,PublicOrgLimitDTO.class); + + ExcelUtils.exportExcel(list,null, "机构指标阈值", PublicOrgLimitExcel.class,null,response); + } + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/dto/PublicOrgLimitDTO.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/dto/PublicOrgLimitDTO.java new file mode 100644 index 0000000..1b1d5cf --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/dto/PublicOrgLimitDTO.java @@ -0,0 +1,45 @@ +package com.thing.publicorg.publicorglimit.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 机构指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@Data +@Schema( name= "机构指标阈值") +public class PublicOrgLimitDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "租户") + private Long tenantCode; + @Schema(description = "创建人") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "修改人") + private Long updater; + @Schema(description = "修改时间") + private Date updateDate; + @Schema(description = "指标名称") + private String indexName; + @Schema(description = "年约束值") + private BigDecimal yearValue; + @Schema(description = "月约束值") + private BigDecimal monthValue; + @Schema(description = "单位") + private String unit; + @Schema(description = "企业id") + private Long deptId; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/entity/PublicOrgLimitEntity.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/entity/PublicOrgLimitEntity.java new file mode 100644 index 0000000..902c1c5 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/entity/PublicOrgLimitEntity.java @@ -0,0 +1,43 @@ +package com.thing.publicorg.publicorglimit.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; + +/** + * 机构指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("public_org_limit") +public class PublicOrgLimitEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + + + /** + * 指标名称 + */ + private String indexName; + /** + * 年约束值 + */ + private BigDecimal yearValue; + /** + * 月约束值 + */ + private BigDecimal monthValue; + /** + * 单位 + */ + private String unit; + +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/excel/PublicOrgLimitExcel.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/excel/PublicOrgLimitExcel.java new file mode 100644 index 0000000..b64f559 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/excel/PublicOrgLimitExcel.java @@ -0,0 +1,45 @@ +package com.thing.publicorg.publicorglimit.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 机构指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class PublicOrgLimitExcel { + @ExcelProperty(value = "id", index = 0) + private Long id; + @ExcelProperty(value = "租户", index = 1) + private Long tenantCode; + @ExcelProperty(value = "创建人", index = 2) + private Long creator; + @ExcelProperty(value = "创建时间", index = 3) + private Date createDate; + @ExcelProperty(value = "修改人", index = 4) + private Long updater; + @ExcelProperty(value = "修改时间", index = 5) + private Date updateDate; + @ExcelProperty(value = "指标名称", index = 6) + private String indexName; + @ExcelProperty(value = "年约束值", index = 7) + private BigDecimal yearValue; + @ExcelProperty(value = "月约束值", index = 8) + private BigDecimal monthValue; + @ExcelProperty(value = "单位", index = 9) + private String unit; + @ExcelProperty(value = "企业id", index = 10) + private Long deptId; +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/mapper/PublicOrgLimitMapper.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/mapper/PublicOrgLimitMapper.java new file mode 100644 index 0000000..d212e09 --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/mapper/PublicOrgLimitMapper.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.publicorglimit.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.entity.PublicOrgLimitEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 机构指标阈值 +* +* @author xiezw 806671840@qq.com +* @since 3.0 2023-08-14 +*/ +@Mapper +public interface PublicOrgLimitMapper extends PowerBaseMapper { + + void deleteByOrgId(@Param("deptId") Long deptId); + + List getByDeptId(@Param("deptId")Long deptId); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/PublicOrgLimitService.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/PublicOrgLimitService.java new file mode 100644 index 0000000..2ea34dd --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/PublicOrgLimitService.java @@ -0,0 +1,24 @@ +package com.thing.publicorg.publicorglimit.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.entity.PublicOrgLimitEntity; + +import java.util.List; +import java.util.Map; + +/** + * 机构指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +public interface PublicOrgLimitService extends IBaseService { + + void deleteByOrgId(Long id); + + List getByDeptId(Long id); + + Map getOrgLimitMap(Long id); +} diff --git a/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/impl/PublicOrgLimitServiceImpl.java b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/impl/PublicOrgLimitServiceImpl.java new file mode 100644 index 0000000..a010e8f --- /dev/null +++ b/modules/publicorg/src/main/java/com/thing/publicorg/publicorglimit/service/impl/PublicOrgLimitServiceImpl.java @@ -0,0 +1,55 @@ +package com.thing.publicorg.publicorglimit.service.impl; + + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.entity.PublicOrgLimitEntity; +import com.thing.publicorg.publicorglimit.mapper.PublicOrgLimitMapper; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 机构指标阈值 + * + * @author xiezw 806671840@qq.com + * @since 3.0 2023-08-14 + */ +@Service +public class PublicOrgLimitServiceImpl extends BaseServiceImpl implements PublicOrgLimitService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + return wrapper; + } + + + @Override + public void deleteByOrgId(Long deptId) { + mapper.deleteByOrgId(deptId); + } + + @Override + public List getByDeptId(Long deptId) { + return mapper.getByDeptId(deptId); + } + + @Override + public Map getOrgLimitMap(Long deptId) { + Map limitMap = new HashMap<>(); + List dtos = mapper.getByDeptId(deptId); + for (PublicOrgLimitDTO limitDTO:dtos){ + limitMap.put(limitDTO.getIndexName(),limitDTO); + } + return limitMap; + } +} diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/benchmarkingproject/PublicBenchmarkingProjectMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/benchmarkingproject/PublicBenchmarkingProjectMapper.xml new file mode 100644 index 0000000..2719dd4 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/benchmarkingproject/PublicBenchmarkingProjectMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreport/PublicEnergySavingReportMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreport/PublicEnergySavingReportMapper.xml new file mode 100644 index 0000000..cdfda20 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreport/PublicEnergySavingReportMapper.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreportdetail/PublicEnergySavingReportDetailMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreportdetail/PublicEnergySavingReportDetailMapper.xml new file mode 100644 index 0000000..f8b13fb --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/energysavingreportdetail/PublicEnergySavingReportDetailMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + delete from public_energy_saving_report_detail WHERE report_id=#{reportId} + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/financial/PublicFinancialMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/financial/PublicFinancialMapper.xml new file mode 100644 index 0000000..aae00c6 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/financial/PublicFinancialMapper.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/indexstandard/PublicIndexStandardMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/indexstandard/PublicIndexStandardMapper.xml new file mode 100644 index 0000000..4bb2262 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/indexstandard/PublicIndexStandardMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/notice/PublicNoticeMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/notice/PublicNoticeMapper.xml new file mode 100644 index 0000000..7369287 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/notice/PublicNoticeMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/noticeviewrecord/PublicNoticeViewRecordMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/noticeviewrecord/PublicNoticeViewRecordMapper.xml new file mode 100644 index 0000000..4a139a7 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/noticeviewrecord/PublicNoticeViewRecordMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/org/PublicOrgMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/org/PublicOrgMapper.xml new file mode 100644 index 0000000..98ec925 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/org/PublicOrgMapper.xml @@ -0,0 +1,188 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/orgdetail/PublicOrgDetailMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/orgdetail/PublicOrgDetailMapper.xml new file mode 100644 index 0000000..381a9c3 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/orgdetail/PublicOrgDetailMapper.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + delete from public_org_detail WHERE 1 = 1 + and id not in + + #{id} + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/policyadvocacy/PublicPolicyAdvocacyMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/policyadvocacy/PublicPolicyAdvocacyMapper.xml new file mode 100644 index 0000000..6c20133 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/policyadvocacy/PublicPolicyAdvocacyMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/publicDiagnosisEnergySaving/PublicDiagnosisEnergySavingMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/publicDiagnosisEnergySaving/PublicDiagnosisEnergySavingMapper.xml new file mode 100644 index 0000000..adeeb4e --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/publicDiagnosisEnergySaving/PublicDiagnosisEnergySavingMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/publicalertnotice/PublicAlertNoticeMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/publicalertnotice/PublicAlertNoticeMapper.xml new file mode 100644 index 0000000..f228b75 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/publicalertnotice/PublicAlertNoticeMapper.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/publicorg/src/main/resources/mapper/publicorg/publicorglimit/PublicOrgLimitMapper.xml b/modules/publicorg/src/main/resources/mapper/publicorg/publicorglimit/PublicOrgLimitMapper.xml new file mode 100644 index 0000000..a495c74 --- /dev/null +++ b/modules/publicorg/src/main/resources/mapper/publicorg/publicorglimit/PublicOrgLimitMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + delete from public_org_limit where dept_id=#{deptId} + + + + diff --git a/modules/quartz/pom.xml b/modules/quartz/pom.xml new file mode 100644 index 0000000..bdb1639 --- /dev/null +++ b/modules/quartz/pom.xml @@ -0,0 +1,113 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + quartz + jar + ThingBI Server Modules quartz + + UTF-8 + 2.3.2 + + + + + com.thing.common + core + + + + com.thing.modules + thing + + + com.thing.common + core + + + com.thing.common + script + + + + + com.thing.modules + calculation + + + com.thing.common + core + + + + + + com.thing.modules + report-analysis + + + com.thing.common + core + + + com.thing.common + script + + + + + + com.thing.common + script + + + + com.thing.modules + msg + + + com.thing.common + core + + + + + + jakarta.validation + jakarta.validation-api + + + + org.quartz-scheduler + quartz + ${quartz.version} + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/calc/task/CalcTask.java b/modules/quartz/src/main/java/com/thing/quartz/calc/task/CalcTask.java new file mode 100644 index 0000000..e608344 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/calc/task/CalcTask.java @@ -0,0 +1,36 @@ +package com.thing.quartz.calc.task; + +import com.alibaba.fastjson.JSONObject; +import com.thing.calculation.dto.ExecuteCalcRequest; +import com.thing.calculation.handler.CalcExecuteHandler; +import com.thing.quartz.timetask.task.ITask; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +/** + * @author siyang + * @date 2024-03-04 + * @description 物计算定时任务 + */ +@Slf4j +@RequiredArgsConstructor +@Component("CalcTask") +public class CalcTask implements ITask { + + private final CalcExecuteHandler calcExecuteHandler; + + @Override + public void run(String params) { + ExecuteCalcRequest request; + if (StringUtils.isBlank(params)) { + request = new ExecuteCalcRequest(); + } else { + request = JSONObject.parseObject(params, ExecuteCalcRequest.class); + } + calcExecuteHandler.handleCalculate(request); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/listener/PeakEventListener.java b/modules/quartz/src/main/java/com/thing/quartz/listener/PeakEventListener.java new file mode 100644 index 0000000..bede152 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/listener/PeakEventListener.java @@ -0,0 +1,132 @@ +package com.thing.quartz.listener; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import com.thing.carbon.config.dto.CarbonPeakConfigDTO; +import com.thing.carbon.config.service.CarbonPeakConfigService; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.event.PeakEvent; +import com.thing.common.tskv.event.TsKvEvent; +import com.thing.quartz.task.dto.MonthHourMinObject; +import com.thing.queue.util.Topics; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class PeakEventListener{ + @Resource + private SysTenantService sysTenantService; + @Resource + private ThingManageContextService thingManageContextService; + @Resource + private CarbonPeakConfigService carbonPeakConfigService; + + private final ApplicationEventPublisher publisher; + + @EventListener(PeakEvent.class) + public void handleEvent(PeakEvent event) { + List tenantList = sysTenantService.listDTO(); + List tskvList = event.getProtos(); + + List list = TsKvDTO.toTsKvDTOs(tskvList); + for (SysTenantDTO tenant : tenantList) { + //根据企业code 获取企业所有物实体 + Optional> optionalList = thingManageContextService.findEntityAllByCodeAndTenantCode(null, tenant.getTenantCode(), true); + Map param =new HashMap<>(); + param.put("tenantCode",tenant.getTenantCode()); + //尖峰谷平配置信息 + List carbonPeakConfigDTOS = carbonPeakConfigService.listCarbonPeakConfigDTO(param); + //过滤 event 中为当前企业物的,配置了峰平谷尖信息的数据 + List thingCodes = optionalList.orElseGet(Collections::emptyList).stream().map(IotThingEntityDTO::getCode).toList(); + List attrKeys = carbonPeakConfigDTOS.stream().map(CarbonPeakConfigDTO::getBaseAttrCode).toList(); + List filterTsKvDTOList = filterTsKvDTOList(list,thingCodes,attrKeys); + peakValleyCalculate(filterTsKvDTOList,carbonPeakConfigDTOS); + } + } + + /** + * 按尖峰谷平配置,替换属性编码后回传至本地队列 + * @param filterTsKvDTOList + * @param carbonPeakConfigDTOS + */ + private void peakValleyCalculate(List filterTsKvDTOList,List carbonPeakConfigDTOS){ + if(!filterTsKvDTOList.isEmpty()){ + Map monthCacheMap = new HashMap<>(); + Map> priceMonthCacheMap = new HashMap<>(); + List kvDTOList = new ArrayList<>(); + filterTsKvDTOList.forEach(tskv->{ + // 处理tb时间戳数据 变成年 月,分钟 + String year=""; + String month = ""; + int min = 0; + MonthHourMinObject monthHourMinObject = null; + if (monthCacheMap.containsKey(tskv.getTs())&& Objects.isNull(monthCacheMap.get(tskv.getTs()))){ + monthHourMinObject = monthCacheMap.get(tskv.getTs()); + year = monthHourMinObject.getYear(); + month = monthHourMinObject.getMonth(); + min = monthHourMinObject.getMin(); + } else { + Date date = new Date(tskv.getTs()); + year = String.valueOf(DateUtil.year(date) ); + month = String.valueOf(DateUtil.month(date) + 1); + min = DateUtil.hour(date, true) * 60 + DateUtil.minute(date); + + monthHourMinObject = new MonthHourMinObject(); + monthHourMinObject.setYear(year); + monthHourMinObject.setMonth(month); + monthHourMinObject.setMin(min); + monthCacheMap.put(tskv.getTs(), monthHourMinObject); + } + int finalMin = min; + String finalMonth = month; + String finalYear = year; + // 价格列表匹配时间戳的数据是否满足区间 满足则计算对应的尖峰谷平 + for (CarbonPeakConfigDTO configDTO : carbonPeakConfigDTOS) { + String yearParam = configDTO.getYear(); + List monthList = null; + if (priceMonthCacheMap.containsKey(configDTO.getId())) { + monthList = priceMonthCacheMap.get(configDTO.getId()); + } else { + monthList = Arrays.asList(configDTO.getMonths().split(",")); + priceMonthCacheMap.put(configDTO.getId(), monthList); + } + if (finalYear.equals(yearParam) &&CollectionUtil.isNotEmpty(monthList) && monthList.contains(finalMonth) + && finalMin >= configDTO.getStartTime() && finalMin < configDTO.getEndTime()){ + //copy tskv + tskv.setAttrKey(configDTO.getBuildAttrCode()); + kvDTOList.add(tskv); + } + } + }); + publisher.publishEvent(new TsKvEvent(Topics.V1_TSKV_HISTORY.getValue(), TsKvDTO.toDataProtoList(kvDTOList))); + } + } + + /** + * 过滤 List list 其thingCode 字段 在 List thingCodes中存在,并且 attrKey 字段 在ListattrKeys中存在 + * @param list tskv数据 + * @param thingCodes 企业所有物实体 + * @param attrKeys 配置了尖峰谷平的原始物属性 + * @return + */ + public static List filterTsKvDTOList(List list, List thingCodes, List attrKeys) { + return list.stream() + .filter(dto -> thingCodes.contains(dto.getThingCode()) && attrKeys.contains(dto.getAttrKey())) + .collect(Collectors.toList()); + } + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/controller/IotTaskController.java b/modules/quartz/src/main/java/com/thing/quartz/task/controller/IotTaskController.java new file mode 100644 index 0000000..886444f --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/controller/IotTaskController.java @@ -0,0 +1,109 @@ +package com.thing.quartz.task.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.quartz.task.dto.IotTaskDTO; +import com.thing.quartz.task.service.IotTaskService; +import com.thing.quartz.task.task.TaskBeanManager; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.Map; + + +/** + * 定时任务 + * + * @author zhh + * @since 3.0 2023-04-11 + */ +@RestController +@RequestMapping("iot/task") +@Tag(name = "定时任务") +public class IotTaskController { + @Autowired + private IotTaskService iotTaskService; + @Autowired + private TaskBeanManager taskBeanManager; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="任务名称"), + @Parameter(name = "type",description ="任务类型"), + @Parameter(name = "timeType",description ="定时类型") + }) + public Result> page( @RequestParam Map params) { + PageData page = iotTaskService.getPageData(params,IotTaskDTO.class); + return new Result>().ok(page); + } + + @GetMapping("dict") + @Operation(summary="字典") + public Result> type() { + Map data = iotTaskService.dict(); + data.put("bean", taskBeanManager.getDict()); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + IotTaskDTO data = iotTaskService.getByIdAs(id,IotTaskDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotTaskDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + iotTaskService.saveDto(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotTaskDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + iotTaskService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotTaskService.removeByIds(Arrays.asList(ids)); + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/dto/IotTaskDTO.java b/modules/quartz/src/main/java/com/thing/quartz/task/dto/IotTaskDTO.java new file mode 100644 index 0000000..0b45aba --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/dto/IotTaskDTO.java @@ -0,0 +1,80 @@ +package com.thing.quartz.task.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 定时任务 +* +* @author zhh +* @since 3.0 2023-04-11 +*/ +@Data +@Schema( name= "定时任务") +public class IotTaskDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "任务名称") + @NotBlank(message="任务名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema(description = "任务类型") + @NotBlank(message="任务类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "定时类型") + @NotBlank(message="定时类型不能为空", groups = DefaultGroup.class) + private String timeType; + @Schema(description = "开始时间") + @NotNull(message="开始时间不能为空", groups = DefaultGroup.class) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date startDate; + @Schema(description = "结束时间") + @NotNull(message="结束时间不能为空", groups = DefaultGroup.class) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date endDate; + @Schema(description = "扩展字段,存放具体定时参数") + private String extendData; + @Schema(description = "cron表达式") + private String cron; + @Schema(description = "beanName或控制主键") + @NotBlank(message="关联不能为空", groups = DefaultGroup.class) + private String beanId; + @Schema(description = "参数方法") + private String func; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建人") + private String createName; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/dto/MonthHourMinObject.java b/modules/quartz/src/main/java/com/thing/quartz/task/dto/MonthHourMinObject.java new file mode 100644 index 0000000..a8a672f --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/dto/MonthHourMinObject.java @@ -0,0 +1,14 @@ +package com.thing.quartz.task.dto; + +import lombok.Data; + + +@Data +public class MonthHourMinObject { + + private String year; + + private String month; + + private Integer min; +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/dto/TaskDictDTO.java b/modules/quartz/src/main/java/com/thing/quartz/task/dto/TaskDictDTO.java new file mode 100644 index 0000000..ff02768 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/dto/TaskDictDTO.java @@ -0,0 +1,26 @@ +package com.thing.quartz.task.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * @author zhenghh. 2023-04-12 + */ +@Data +@AllArgsConstructor +public class TaskDictDTO { + + private Long dictId; + private String dictCode; + private String dictName; + + public TaskDictDTO(Long dictId, String dictName) { + this.dictId = dictId; + this.dictName = dictName; + } + + public TaskDictDTO(String dictCode, String dictName) { + this.dictCode = dictCode; + this.dictName = dictName; + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/entity/IotTaskEntity.java b/modules/quartz/src/main/java/com/thing/quartz/task/entity/IotTaskEntity.java new file mode 100644 index 0000000..945d7e7 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/entity/IotTaskEntity.java @@ -0,0 +1,68 @@ +package com.thing.quartz.task.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 定时任务 + * + * @author zhh + * @since 3.0 2023-04-11 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("iot_task") +public class IotTaskEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务名称 + */ + private String name; + /** + * 任务类型 + */ + private String type; + /** + * 定时类型 + */ + private String timeType; + /** + * 开始时间 + */ + private Date startDate; + /** + * 结束时间 + */ + private Date endDate; + /** + * 扩展字段,存放具体定时参数 + */ + private String extendData; + /** + * cron表达式 + */ + private String cron; + /** + * beanName或控制主键 + */ + private String beanId; + /** + * 参数方法 + */ + private String func; + /** + * 备注说明 + */ + private String remark; + + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/mapper/IotTaskMapper.java b/modules/quartz/src/main/java/com/thing/quartz/task/mapper/IotTaskMapper.java new file mode 100644 index 0000000..0c6b05f --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/mapper/IotTaskMapper.java @@ -0,0 +1,16 @@ +package com.thing.quartz.task.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.quartz.task.entity.IotTaskEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 定时任务 +* +* @author zhh +* @since 3.0 2023-04-11 +*/ +@Mapper +public interface IotTaskMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/service/IotTaskService.java b/modules/quartz/src/main/java/com/thing/quartz/task/service/IotTaskService.java new file mode 100644 index 0000000..01098cc --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/service/IotTaskService.java @@ -0,0 +1,44 @@ +package com.thing.quartz.task.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.quartz.task.dto.IotTaskDTO; +import com.thing.quartz.task.entity.IotTaskEntity; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 定时任务 + * + * @author zhh + * @since 3.0 2023-04-11 + */ +public interface IotTaskService extends IBaseService { + + PageData page(Map params); + + /** + * 字典 + * + * @return map + */ + Map dict(); + + @Transactional(rollbackFor = Exception.class) + void save(IotTaskDTO dto); + + @Transactional(rollbackFor = Exception.class) + void update(IotTaskDTO dto); + + void delete(Long[] ids); + + /** + * 获取所有列表 初始化用 + * + * @return list + */ + List getScheduleJobList(); +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/service/impl/IotTaskServiceImpl.java b/modules/quartz/src/main/java/com/thing/quartz/task/service/impl/IotTaskServiceImpl.java new file mode 100644 index 0000000..ef4b954 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/service/impl/IotTaskServiceImpl.java @@ -0,0 +1,230 @@ +package com.thing.quartz.task.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.quartz.task.dto.IotTaskDTO; +import com.thing.quartz.task.dto.TaskDictDTO; +import com.thing.quartz.task.entity.IotTaskEntity; +import com.thing.quartz.task.mapper.IotTaskMapper; +import com.thing.quartz.task.service.IotTaskService; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import com.thing.quartz.timetask.utils.ScheduleUtils; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.quartz.CronExpression; +import org.quartz.Scheduler; +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.LocalTime; +import java.time.format.DateTimeFormatter; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 定时任务 + * + * @author zhh + * @since 3.0 2023-04-11 + */ +@Slf4j +@Service +public class IotTaskServiceImpl extends BaseServiceImpl implements IotTaskService { + + @Autowired + private Scheduler scheduler; + @Autowired + private SysUserMapper userDao; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + String type = (String) params.get("type"); + String timeType = (String) params.get("timeType"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper + .like(IotTaskEntity::getName, name,StringUtils.isNotBlank(name)) + .eq(IotTaskEntity::getType, type,StringUtils.isNotBlank(type)) + .eq( IotTaskEntity::getTimeType, timeType,StringUtils.isNotBlank(timeType)); + return wrapper; + } + + @Override + public PageData page(Map params) { + PageData page = super.getPageData(params,IotTaskDTO.class); + if (page.getTotal() > 0) { + List userIds = page.getList().stream().map(IotTaskDTO::getCreator) + .filter(Objects::nonNull).distinct().collect(Collectors.toList()); + List userList = userDao.selectListByIds(userIds); + page.getList().forEach(item -> { + String name = userList.parallelStream().filter(user -> Objects.equals(item.getCreator(), user.getId())) + .findFirst().map(SysUserEntity::getRealName).orElse("--"); + item.setCreateName(name); + }); + } + return page; + } + + @Override + public Map dict() { + Map result = Maps.newHashMap(); + result.put("type", Arrays.stream(TaskType.values()).map(item -> new TaskDictDTO(item.name(), item.getName())).collect(Collectors.toList())); + result.put("timeType", Arrays.stream(TaskTimeType.values()).map(item -> new TaskDictDTO(item.name(), item.getName())).collect(Collectors.toList())); + result.put("week", Arrays.stream(TaskWeek.values()).map(item -> new TaskDictDTO(item.getWeek(), item.getName())).collect(Collectors.toList())); + result.put("hms", Arrays.stream(TaskTime.values()).map(item -> new TaskDictDTO(item.name(), item.getName())).collect(Collectors.toList())); + return result; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(IotTaskDTO dto) { + dto.setCron(convertCron(dto)); + super.saveDto(dto); + ScheduleJobEntity entity = convertScheduleJob(dto); + ScheduleUtils.createScheduleJob(scheduler, entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(IotTaskDTO dto) { + dto.setCron(convertCron(dto)); + super.updateDto(dto); + ScheduleJobEntity entity = convertScheduleJob(dto); + //移除后重新增加 + ScheduleUtils.deleteScheduleJob(scheduler, dto.getId()); + ScheduleUtils.createScheduleJob(scheduler, entity); + } + + @Override + public void delete(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.deleteScheduleJob(scheduler, id); + } + super.removeByIds(Arrays.asList(ids)); + } + + @Override + public List getScheduleJobList() { + List list = list(QueryWrapper.create()); + return CollectionUtil.isEmpty(list) ? Lists.newArrayList() : list.stream().map(this::convertScheduleJob1).collect(Collectors.toList()); + } + + /** + * 条件转换为cron表达式 + * @param dto dto + * @return cron + */ + private static String convertCron(IotTaskDTO dto) { + TaskTimeType taskTimeType = TaskTimeType.valueOf(dto.getTimeType()); + LocalDateTime dateTime = LocalDateTimeUtil.of(dto.getStartDate()); + if (TaskTimeType.TIME.equals(taskTimeType)) { + if(dateTime.isBefore(LocalDateTime.now())) { + throw new SysException("开始时间要大于当前时间"); + } + return String.format("%s %s %s %s %s ? %s", dateTime.getSecond(), dateTime.getMinute(), dateTime.getHour(), + dateTime.getDayOfMonth(), dateTime.getMonthValue(), dateTime.getYear()); + } + + JsonNode extendData = JacksonUtil.toJsonNode(dto.getExtendData()); + JsonNode extend = extendData.get("extend"); + if (extend == null || StringUtils.isBlank(extend.asText())) { + throw new SysException("扩展属性不能为空"); + } + switch (taskTimeType) { + case DAY: + LocalTime localTime = LocalTime.parse(extend.asText(), DateTimeFormatter.ofPattern("HH:mm:ss")); + return String.format("%s %s %s * * ?", localTime.getSecond(), localTime.getMinute(), localTime.getHour()); + case WEEK: + return String.format("%s %s %s ? * %s", dateTime.getSecond(), dateTime.getMinute(), dateTime.getHour(), extend.asText()); + case POLLING: + JsonNode unit = extendData.get("unit"); + if (unit == null || StringUtils.isBlank(unit.asText())) { + throw new SysException("请选择时间单位"); + } + TaskTime taskTime = TaskTime.valueOf(unit.asText()); + int time = extend.asInt(); + switch (taskTime) { + case hh: + if (time < 1 || time > 23) { + throw new SysException("单位为小数,时间范围在1~23之间"); + } + return String.format("0 %s %s/%s * * ?", dateTime.getMinute(), dateTime.getHour(), time); + case mm: + if (time < 1 || time > 59) { + throw new SysException("单位为分钟,时间范围在1~59之间"); + } + return String.format("0 %s/%s * * * ?", dateTime.getMinute(), time); + case ss: + if (time < 1 || time > 59) { + throw new SysException("单位为秒,时间范围在1~59之间"); + } + return String.format("0/%s * * * * ?", time); + default: + throw new SysException("时间单位不支持"); + } + case CRON: + if (!CronExpression.isValidExpression(extend.asText())) { + throw new SysException("CRON表达式格式有误"); + } + return extend.asText(); + default: + throw new SysException("定时类型不支持"); + } + } + + private ScheduleJobEntity convertScheduleJob(IotTaskDTO dto) { + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.put("startTime", DateUtil.formatDateTime(dto.getStartDate())); + jsonNodes.put("endTime", DateUtil.formatDateTime(dto.getEndDate())); + jsonNodes.put("type", dto.getType()); + jsonNodes.put("beanId", dto.getBeanId()); + jsonNodes.put("func", dto.getFunc()); + jsonNodes.put("name", dto.getName()); + jsonNodes.put("id", dto.getId()); + + ScheduleJobEntity entity = new ScheduleJobEntity(); + entity.setStatus(ScheduleStatus.NORMAL.getValue()); + entity.setId(dto.getId()); + entity.setBeanName("CustomTask"); + entity.setCronExpression(dto.getCron()); + entity.setParams(JacksonUtil.toString(jsonNodes)); + return entity; + } + + private ScheduleJobEntity convertScheduleJob1(IotTaskEntity dto) { + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.put("startTime", DateUtil.formatDateTime(dto.getStartDate())); + jsonNodes.put("endTime", DateUtil.formatDateTime(dto.getEndDate())); + jsonNodes.put("type", dto.getType()); + jsonNodes.put("beanId", dto.getBeanId()); + jsonNodes.put("func", dto.getFunc()); + jsonNodes.put("name", dto.getName()); + jsonNodes.put("id", dto.getId()); + + ScheduleJobEntity entity = new ScheduleJobEntity(); + entity.setStatus(ScheduleStatus.NORMAL.getValue()); + entity.setId(dto.getId()); + entity.setBeanName("CustomTask"); + entity.setCronExpression(dto.getCron()); + entity.setParams(JacksonUtil.toString(jsonNodes)); + return entity; + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/task/CustomTask.java b/modules/quartz/src/main/java/com/thing/quartz/task/task/CustomTask.java new file mode 100644 index 0000000..2f3cd07 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/task/CustomTask.java @@ -0,0 +1,112 @@ +package com.thing.quartz.task.task; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.common.core.enumeration.TaskType; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.control.dto.ControlParam; +import com.thing.control.service.IotDeviceControlService; +import com.thing.quartz.timetask.task.ITask; +import com.thing.queue.QueueThreadFactory; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.annotation.PostConstruct; +import java.time.LocalDateTime; +import java.util.Optional; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * @author zhenghh. 2023-04-12 + */ +@Slf4j +@Component("CustomTask") +public class CustomTask implements ITask { + + @Autowired + private TaskBeanManager taskBeanManager; + @Autowired + private IotDeviceControlService deviceControlService; + @Autowired + private ScriptCreateService scriptCreateService; + + private ExecutorService executor; + + @PostConstruct + public void init() { + this.executor = Executors.newCachedThreadPool(QueueThreadFactory.forName("custom-task")); + } + + /** + * @param params {name:'任务名称',startTime:'开始时间', endTime:'结束时间', type:'任务类型', beanId:'beanName或控制主键',func:'方法'} + */ + @Override + public void run(String params) { + executor.submit(() -> { + LocalDateTime now = LocalDateTime.now(); + JsonNode jsonNode = JacksonUtil.toJsonNode(params); + LocalDateTime startTime = LocalDateTimeUtil.parse(jsonNode.get("startTime").asText(), DatePattern.NORM_DATETIME_FORMATTER); + LocalDateTime endTime = LocalDateTimeUtil.parse(jsonNode.get("endTime").asText(), DatePattern.NORM_DATETIME_FORMATTER); + String name = jsonNode.get("name").asText(); + //当前时间在范围内才执行 + if (now.isBefore(startTime) || now.isAfter(endTime)) { + log.warn("Task {} 不在执行时间范围内,跳过", name); + return; + } + log.debug("Task {} 开始执行", name); + //解析 + String func = jsonNode.get("func").asText(); + JsonNode msg = StringUtils.isBlank(func) ? JacksonUtil.newObjectNode() : convertFunc(func); + + //控制 + if (TaskType.CTL.name().equals(jsonNode.get("type").asText())) { + control(jsonNode.get("beanId").asLong(), msg); + } + //bean + if (TaskType.BEAN.name().equals(jsonNode.get("type").asText())) { + bean(jsonNode.get("beanId").asText(), msg); + } + }); + } + + private void bean(String beanName, JsonNode msg) { + try { + Optional taskBean = taskBeanManager.getTaskBean(beanName); + if (!taskBean.isPresent()) { + log.error("bean {} 未找到", beanName); + return; + } + taskBean.get().run(msg); + } catch (Exception e) { + log.error("调用bean {} 失败: {}", beanName, e.getMessage()); + } + } + + private void control(Long beanId, JsonNode msg) { + try { + ControlParam param = new ControlParam(); + param.setControlId(beanId); + param.setCondition(JacksonUtil.toString(msg)); + deviceControlService.control(param); + } catch (Exception e) { + log.error("{} 控制失败: {}", beanId, e.getMessage()); + } + } + + private JsonNode convertFunc(String func) { + try { + return scriptCreateService.executeJson(new SnowFlakeIDKeyGenerator().nextId(), ScriptLanguage.NASHORN, func, false, + "", JacksonUtil.newObjectNode(), JacksonUtil.newObjectNode(), true); + } catch (Exception e) { + log.error("{} 解析失败: {}", func, e.getMessage()); + return JacksonUtil.newObjectNode(); + } + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/task/DefaultTaskBeanManager.java b/modules/quartz/src/main/java/com/thing/quartz/task/task/DefaultTaskBeanManager.java new file mode 100644 index 0000000..409e7a5 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/task/DefaultTaskBeanManager.java @@ -0,0 +1,101 @@ +package com.thing.quartz.task.task; + +import com.google.common.collect.Lists; +import com.thing.quartz.task.dto.TaskDictDTO; +import com.thing.quartz.task.service.IotTaskService; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import com.thing.quartz.timetask.utils.ScheduleUtils; +import lombok.extern.slf4j.Slf4j; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author zhenghh. 2023-04-11 + */ +@Slf4j +@Component +public class DefaultTaskBeanManager implements TaskBeanManager, BeanPostProcessor, CommandLineRunner { + /** + * 扩展缓存 + */ + private final Map store = new ConcurrentHashMap<>(); + + @Autowired + private IotTaskService iotTaskService; + + @Autowired + private Scheduler scheduler; + + /** + * 初始化TaskBean + * + * @param bean bean + * @param beanName beanName + * @return bean + * @throws BeansException e + */ + @Override + public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName) throws BeansException { + if (bean instanceof TaskBean) { + register(beanName, (TaskBean) bean); + } + return bean; + } + + /** + * 启动加载task + * + * @param args 参数 + * @throws Exception e + */ + @Override + public void run(String... args) throws Exception { + List taskList = iotTaskService.getScheduleJobList(); + for (ScheduleJobEntity scheduleJob : taskList) { + try { + CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId()); + //如果不存在,则创建 + if (cronTrigger == null) { + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + } else { + ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); + } + } catch (Exception e) { + log.info("{}启动失败: {}", scheduleJob.getId(), e.getMessage()); + } + } + } + + @Override + public List getAll() { + return new ArrayList<>(store.values()); + } + + @Override + public List getDict() { + List list = Lists.newArrayList(); + store.forEach((beanName, bean) -> list.add(new TaskDictDTO(beanName, bean.getName()))); + return list; + } + + @Override + public Optional getTaskBean(String beanName) { + return Optional.ofNullable(store.get(beanName)); + } + + private void register(String beanName, TaskBean provider) { + this.store.put(beanName, provider); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBean.java b/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBean.java new file mode 100644 index 0000000..c701313 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBean.java @@ -0,0 +1,26 @@ +package com.thing.quartz.task.task; + +import com.fasterxml.jackson.databind.JsonNode; + +import javax.annotation.Nonnull; + +/** + * @author zhenghh. 2023-04-11 + */ +public interface TaskBean { + + /** + * 中文名 + * + * @return 中文名 + */ + @Nonnull + String getName(); + + /** + * 执行 + * + * @param params 入参 + */ + void run(JsonNode params); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBeanManager.java b/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBeanManager.java new file mode 100644 index 0000000..0114340 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/task/TaskBeanManager.java @@ -0,0 +1,34 @@ +package com.thing.quartz.task.task; + + +import com.thing.quartz.task.dto.TaskDictDTO; + +import java.util.List; +import java.util.Optional; + +/** + * @author zhenghh. 2023-04-11 + */ +public interface TaskBeanManager { + + /** + * 获取所有bean + * + * @return list + */ + List getAll(); + + /** + * 字典 + * @return list + */ + List getDict(); + + /** + * 获取单个bean + * @param beanName beanName + * @return TaskBean + */ + Optional getTaskBean(String beanName); + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/task/task/TestTaskBean.java b/modules/quartz/src/main/java/com/thing/quartz/task/task/TestTaskBean.java new file mode 100644 index 0000000..7ccf9e5 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/task/task/TestTaskBean.java @@ -0,0 +1,23 @@ +package com.thing.quartz.task.task; + +import com.fasterxml.jackson.databind.JsonNode; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2023-04-11 + */ +@Component("TestTaskBean") +public class TestTaskBean implements TaskBean { + + @NotNull + @Override + public String getName() { + return "示例,上线请删除"; + } + + @Override + public void run(JsonNode params) { + System.out.println(params); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/config/ScheduleConfig.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/config/ScheduleConfig.java new file mode 100644 index 0000000..77c71ae --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/config/ScheduleConfig.java @@ -0,0 +1,60 @@ +package com.thing.quartz.timetask.config; + + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +import javax.sql.DataSource; +import java.util.Properties; + + +/** + * 定时任务配置 + * + * @author Mark sunlightcs@gmail.com + */ +@Configuration +public class ScheduleConfig { + + @Bean + public SchedulerFactoryBean schedulerFactoryBean(DataSource dataSource) { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setDataSource(dataSource); + + // quartz参数 + Properties prop = new Properties(); + prop.put("org.quartz.scheduler.instanceName", "ThingScheduler"); + prop.put("org.quartz.scheduler.instanceId", "AUTO"); + // 线程池配置 + prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool"); + prop.put("org.quartz.threadPool.threadCount", "80"); + prop.put("org.quartz.threadPool.threadPriority", "3"); + //JobStore配置 + prop.put("org.quartz.jobStore.class", "org.springframework.scheduling.quartz.LocalDataSourceJobStore"); + //集群配置 + prop.put("org.quartz.jobStore.isClustered", "true"); + prop.put("org.quartz.jobStore.clusterCheckinInterval", "15000"); + prop.put("org.quartz.jobStore.maxMisfiresToHandleAtATime", "1"); + + prop.put("org.quartz.jobStore.misfireThreshold", "12000"); + prop.put("org.quartz.jobStore.tablePrefix", "QRTZ_"); + prop.put("org.quartz.jobStore.selectWithLockSQL", "SELECT * FROM {0}LOCKS UPDLOCK WHERE LOCK_NAME = ?"); + + //PostgreSQL数据库,需要打开此注释 + prop.put("org.quartz.jobStore.driverDelegateClass", "org.quartz.impl.jdbcjobstore.PostgreSQLDelegate"); + + factory.setQuartzProperties(prop); + + factory.setSchedulerName("ThingScheduler"); + //延时启动 + factory.setStartupDelay(30); + factory.setApplicationContextSchedulerContextKey("applicationContextKey"); + //可选,QuartzScheduler 启动时更新己存在的Job,这样就不用每次修改targetObject后删除qrtz_job_details表对应记录了 + factory.setOverwriteExistingJobs(true); + //设置自动启动,默认为true + factory.setAutoStartup(true); + + return factory; + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobController.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobController.java new file mode 100644 index 0000000..7b6b141 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobController.java @@ -0,0 +1,111 @@ + + +package com.thing.quartz.timetask.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.quartz.timetask.dto.ScheduleJobDTO; +import com.thing.quartz.timetask.service.ScheduleJobService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/schedule") +@Tag(name="定时任务") +public class ScheduleJobController { + @Autowired + private ScheduleJobService scheduleJobService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "beanName",description ="beanName") + }) + public Result> page( @RequestParam Map params){ + PageData page = scheduleJobService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result info(@PathVariable("id") Long id){ + ScheduleJobDTO schedule = scheduleJobService.get(id); + + return new Result().ok(schedule); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody ScheduleJobDTO dto){ + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + scheduleJobService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody ScheduleJobDTO dto){ + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + scheduleJobService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + scheduleJobService.deleteBatch(ids); + + return new Result(); + } + + @PutMapping("/run") + @Operation(summary="立即执行") + public Result run(@RequestBody Long[] ids){ + scheduleJobService.run(ids); + + return new Result(); + } + + @PutMapping("/pause") + @Operation(summary="暂停") + public Result pause(@RequestBody Long[] ids){ + scheduleJobService.pause(ids); + + return new Result(); + } + + @PutMapping("/resume") + @Operation(summary="恢复") + public Result resume(@RequestBody Long[] ids){ + scheduleJobService.resume(ids); + + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobLogController.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobLogController.java new file mode 100644 index 0000000..1d9f054 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/controller/ScheduleJobLogController.java @@ -0,0 +1,52 @@ +package com.thing.quartz.timetask.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.quartz.timetask.dto.ScheduleJobLogDTO; +import com.thing.quartz.timetask.service.ScheduleJobLogService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + +/** + * 定时任务日志 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/scheduleLog") +@Tag(name="定时任务日志") +public class ScheduleJobLogController { + @Autowired + private ScheduleJobLogService scheduleJobLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "jobId",description ="jobId") + }) + public Result> page( @RequestParam Map params){ + PageData page = scheduleJobLogService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result info(@PathVariable("id") Long id){ + ScheduleJobLogDTO log = scheduleJobLogService.get(id); + + return new Result().ok(log); + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobDTO.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobDTO.java new file mode 100644 index 0000000..626eb13 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobDTO.java @@ -0,0 +1,61 @@ + + +package com.thing.quartz.timetask.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "定时任务") +public class ScheduleJobDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "spring bean名称") + @NotBlank(message = "{schedule.bean.require}", groups = DefaultGroup.class) + private String beanName; + + @Schema(description = "参数") + private String params; + + @Schema(description = "cron表达式") + @NotBlank(message = "{schedule.cron.require}", groups = DefaultGroup.class) + private String cronExpression; + + @Schema(description = "任务状态 0:暂停 1:正常") + @Range(min=0, max=1, message = "{schedule.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobLogDTO.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobLogDTO.java new file mode 100644 index 0000000..d3efdbc --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/dto/ScheduleJobLogDTO.java @@ -0,0 +1,50 @@ + + +package com.thing.quartz.timetask.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "定时任务日志") +public class ScheduleJobLogDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "任务id") + private Long jobId; + + @Schema(description = "spring bean名称") + private String beanName; + + @Schema(description = "参数") + private String params; + + @Schema(description = "任务状态 0:失败 1:成功") + private Integer status; + + @Schema(description = "失败信息") + private String error; + + @Schema(description = "耗时(单位:毫秒)") + private Integer times; + + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobEntity.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobEntity.java new file mode 100644 index 0000000..f1900e1 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobEntity.java @@ -0,0 +1,50 @@ + + +package com.thing.quartz.timetask.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("schedule_job") +public class ScheduleJobEntity extends BaseDateEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * cron表达式 + */ + private String cronExpression; + /** + * 任务状态 0:暂停 1:正常 + */ + private Integer status; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobLogEntity.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobLogEntity.java new file mode 100644 index 0000000..97c10ad --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/entity/ScheduleJobLogEntity.java @@ -0,0 +1,63 @@ + + +package com.thing.quartz.timetask.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 定时任务日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Table("schedule_job_log") +public class ScheduleJobLogEntity implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 任务id + */ + private Long jobId; + /** + * spring bean名称 + */ + private String beanName; + /** + * 参数 + */ + private String params; + /** + * 任务状态 0:失败 1:成功 + */ + private Integer status; + /** + * 失败信息 + */ + private String error; + /** + * 耗时(单位:毫秒) + */ + private Integer times; + /** + * 创建时间 + */ + private Date createDate; + /** + * IP地址 + */ + private String ip; + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/init/JobCommandLineRunner.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/init/JobCommandLineRunner.java new file mode 100644 index 0000000..be7d90e --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/init/JobCommandLineRunner.java @@ -0,0 +1,46 @@ +package com.thing.quartz.timetask.init; + +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import com.thing.quartz.timetask.mapper.ScheduleJobMapper; +import com.thing.quartz.timetask.utils.ScheduleUtils; +import lombok.extern.slf4j.Slf4j; +import org.quartz.CronTrigger; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.List; + + +/** + * 初始化定时任务数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +@Slf4j +public class JobCommandLineRunner implements CommandLineRunner { + @Autowired + private Scheduler scheduler; + @Autowired + private ScheduleJobMapper scheduleJobDao; + + @Override + public void run(String... args) { + List scheduleJobList = scheduleJobDao.selectAll(); + for(ScheduleJobEntity scheduleJob : scheduleJobList){ + try { + CronTrigger cronTrigger = ScheduleUtils.getCronTrigger(scheduler, scheduleJob.getId()); + //如果不存在,则创建 + if(cronTrigger == null) { + ScheduleUtils.createScheduleJob(scheduler, scheduleJob); + }else { + ScheduleUtils.updateScheduleJob(scheduler, scheduleJob); + } + } catch (Exception e) { + log.error("任务id:{}初始化失败!请检查配置信息",scheduleJob.getId()); + } + } + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobLogMapper.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobLogMapper.java new file mode 100644 index 0000000..318cf63 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobLogMapper.java @@ -0,0 +1,17 @@ + + +package com.thing.quartz.timetask.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.quartz.timetask.entity.ScheduleJobLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 定时任务日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface ScheduleJobLogMapper extends PowerBaseMapper { + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobMapper.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobMapper.java new file mode 100644 index 0000000..3da9d1f --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/mapper/ScheduleJobMapper.java @@ -0,0 +1,24 @@ + + +package com.thing.quartz.timetask.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Map; + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface ScheduleJobMapper extends PowerBaseMapper { + + /** + * 批量更新状态 + */ + int updateBatch(Map map); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/AlarmJobService.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/AlarmJobService.java new file mode 100644 index 0000000..bb35205 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/AlarmJobService.java @@ -0,0 +1,12 @@ +package com.thing.quartz.timetask.service; + +/** + * @author zhenghh. 2022-08-05 + **/ +public interface AlarmJobService { + + /** + * 告警 + */ + void alarm(); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/CalculationJobService.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/CalculationJobService.java new file mode 100644 index 0000000..0e07f12 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/CalculationJobService.java @@ -0,0 +1,45 @@ +package com.thing.quartz.timetask.service; + + + +import com.thing.calculation.calculation.dto.CalculationJson; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; + +import java.util.List; + +/** + * @author zhenghh. 2022-05-26 + **/ +public interface CalculationJobService { + + + /** + * 物计算job + * + */ + void calculation(); + + /** + * 异常物计算 + * + * @param calculationNum 最大执行次数 + */ + void calculation(Integer calculationNum); + + /** + * 推送tb + * + * @param calculationJsonList 计算结果 + * @param logEntityList 已存在日志 + * @return list 日志列表 + */ + List sendTb(List calculationJsonList, List logEntityList); + + /** + * 计算日志 + * @param logEntityList 历史计算日志列表 + * @param calculationJson 当前计算 + * @return CalculationFormulaLogEntity + */ + CalculationFormulaLogEntity convertLogEntity(List logEntityList, CalculationJson calculationJson); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobLogService.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobLogService.java new file mode 100644 index 0000000..8e1bc37 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobLogService.java @@ -0,0 +1,24 @@ + + +package com.thing.quartz.timetask.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.quartz.timetask.dto.ScheduleJobLogDTO; +import com.thing.quartz.timetask.entity.ScheduleJobLogEntity; + +import java.util.Map; + +/** + * 定时任务日志 + * + * @author Mark sunlightcs@gmail.com + */ +public interface ScheduleJobLogService extends IBaseService { + + PageData page(Map params); + + ScheduleJobLogDTO get(Long id); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobService.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobService.java new file mode 100644 index 0000000..19b6ec6 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/ScheduleJobService.java @@ -0,0 +1,59 @@ + + +package com.thing.quartz.timetask.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.quartz.timetask.dto.ScheduleJobDTO; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; + +import java.util.Map; + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + */ +public interface ScheduleJobService extends IBaseService { + + PageData page(Map params); + + ScheduleJobDTO get(Long id); + + /** + * 保存定时任务 + */ + void save(ScheduleJobDTO dto); + + /** + * 更新定时任务 + */ + void update(ScheduleJobDTO dto); + + /** + * 批量删除定时任务 + */ + void deleteBatch(Long[] ids); + + /** + * 批量更新定时任务状态 + */ + int updateBatch(Long[] ids, int status); + + /** + * 立即执行 + */ + void run(Long[] ids); + + /** + * 暂停运行 + */ + void pause(Long[] ids); + + /** + * 恢复运行 + */ + void resume(Long[] ids); +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/AlarmJobServiceImpl.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/AlarmJobServiceImpl.java new file mode 100644 index 0000000..19b2f22 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/AlarmJobServiceImpl.java @@ -0,0 +1,226 @@ +package com.thing.quartz.timetask.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.BooleanUtil; +import com.thing.alarm.alarm.dto.AlarmJson; +import com.thing.alarm.alarm.dto.AlarmRuleActionCache; +import com.thing.alarm.alarm.dto.AlarmRuleActionDTO; +import com.thing.alarm.alarm.service.AlarmRuleActionService; +import com.thing.alarm.alarm.service.AlarmRuleService; +import com.thing.alarm.alarm.service.AlarmService; +import com.thing.common.core.enumeration.AlarmStatus; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.quartz.timetask.service.AlarmJobService; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-08-05 + **/ +@Service +@RequiredArgsConstructor +public class AlarmJobServiceImpl implements AlarmJobService { + + private final TskvService tsKvService; + private final AlarmRuleActionService alarmRuleActionService; + private final AlarmRuleService alarmRuleService; + private final AlarmService alarmService; + + @Override + public void alarm() { + alarmJobVersion2023(); + } + + /** + * 新版定时告警 since 2023.10 + * 1. 实时性要求不高 + * 2. 每条规则下的物属性值的时间应该是一致的,否则不告警 + */ + private void alarmJobVersion2023(){ + List validRuleList = getValidRuleList(); + if (CollectionUtil.isEmpty(validRuleList)) { + return; + } + + for (AlarmJson rule : validRuleList) { + // 获取规则中的所有物属性值 + Map> thingAttrMap = groupRuleForThing(rule); + + // 查询物属性最新值 + List lastTsKvList = tsKvService.getLastTimeSeries(thingAttrMap); + // 排序取最小时间的 tsKv 及其时间 + TsKvDTO minTsKv = lastTsKvList.stream().min(Comparator.comparing(TsKvDTO::getTs)).orElse(null); + if (Objects.isNull(minTsKv)) { + continue; + } + Long minTs = minTsKv.getTs(); + + // 过滤出其他物属性 + List noMinTsKvSet = lastTsKvList.stream().filter(e -> !Objects.equals(minTs, e.getTs())).distinct().collect(Collectors.toList()); + + // 查询其他物属性在 minTs 这个时刻的值 + List allTsKvList = findTsKvInTime(noMinTsKvSet, minTs); + allTsKvList.add(minTsKv); + + // 预备和执行告警 + handleAlarm(validRuleList, allTsKvList); + } + } + + /** 原告警逻辑,作为参考 */ + @SuppressWarnings("all") + private void alarmJobVersion2022() { + List validRuleList = getValidRuleList(); + if (CollectionUtil.isEmpty(validRuleList)) { + return; + } + + //获取数据 + List tsKvList = getTsKvList(validRuleList); + if (CollectionUtil.isEmpty(tsKvList)) { + return; + } + + handleAlarm(validRuleList, tsKvList); + } + + /** + * 按物属性对告警规则进行分组 + */ + private Map> groupRuleForThing(AlarmJson rule) { + return rule.getList().stream() + .collect( + Collectors.groupingBy( + item -> item.getThingId() + "," + item.getThingCode(), + Collectors.mapping( + AlarmJson.AlarmEntity::getThingAttr, Collectors.toList()))); + } + + /** + * 查询其他物属性在ts时刻的属性值 + * @param payloadTsKv 携带着"需要查询的物属性"的载体 + * @param ts 指定的时刻 + * @return 在指定时刻的物属性的tsKv + */ + private List findTsKvInTime(List payloadTsKv, Long ts) { + List allTsKvList = new ArrayList<>(); + Map> thingAttrGroup = + payloadTsKv.stream().collect(Collectors.groupingBy(TsKvDTO::getThingId)); + thingAttrGroup.forEach( + (thingId, tsKvList) -> { + if (CollectionUtils.isEmpty(tsKvList)) { + return; + } + TsKvDTO tsKvDTO = tsKvList.get(0); + List attrCodes = + tsKvList.stream() + .map(TsKvDTO::getThingAttr) + .collect(Collectors.toList()); + List tsKvs = + tsKvService.getTimeSeries( + new ThingAttrDTO(thingId, tsKvDTO.getThingCode(), attrCodes), + ts - 1, + ts + 1); + if (!CollectionUtils.isEmpty(tsKvs)) { + allTsKvList.addAll(tsKvs); + } + }); + return allTsKvList; + } + + /** 查询有效的告警规则 + * 所谓有效:(满足任意条件即可) + * - 1. 是重复告警: 当前规则可以一直生效,最优先判断 + * - 2. 告警规则状态为空:没有状态的告警规则就是一直有效 + * - 3. 告警规则状态为已处理 + * - 3.1 究极反人类的设计,纯粹为了技术实现方便而在告警规则中增加的status字段 + * - 3.2 如果判断到了这个条件就说明本条告警规则不支持重复告警,那么就需要看之前的告警记录是否被处理过了 + * - 3.2.1 如果之前的告警记录还未被处理:那么本条规则就不应该生效,直到那条告警记录被处理完 + * - 3.2.2 如果之前的告警记录已处理完了:那么本条规则就可以继续去跑 + */ + private List getValidRuleList() { + List validRuleList = alarmRuleService.getEnableList(TriggerTypeEnum.JOB); + // 重复告警 或 状态为空 或 状态为已处理 + return validRuleList.stream() + .filter( + item -> + item.getRepeat() + || StringUtils.isBlank(item.getStatus()) + || StringUtils.equals( + AlarmStatus.PROCESSED.getStatus(), + item.getStatus())) + .collect(Collectors.toList()); + } + + private List fill(List validRuleList, List tsKvList){ + return validRuleList.stream() + .peek(item -> item.setList(item.getList().stream() + .peek(entity -> tsKvList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), entity.getThingCode()) && StringUtils.equals(tsKv.getThingAttr(), entity.getThingAttr())) + .findFirst().ifPresent(tsKv -> { + entity.setTime(tsKv.getTs()); + entity.setValue(new BigDecimal(tsKv.getVal())); + })).collect(Collectors.toList())) + ).peek(item -> { + // 暂时判断为 所有值已存在 且 时间一致 + if (CollectionUtil.isNotEmpty(item.getList()) && + item.getList().parallelStream().allMatch(entity -> entity.getValue() != null) && + item.getList().parallelStream().map(AlarmJson.AlarmEntity::getTime).filter(Objects::nonNull).distinct().count() == 1) { + item.setEvaluate(alarmService.evaluate(item)); + } + //第一次或处理完成后第一次告警 修改状态为待处理 + if(BooleanUtil.isTrue(item.getEvaluate()) && + (StringUtils.isBlank(item.getStatus()) || StringUtils.equals(item.getStatus(), AlarmStatus.PROCESSED.getStatus()))) { + item.setStatus(AlarmStatus.PENDING.getStatus()); + } + }).collect(Collectors.toList()); + } + + private List generateCaches(List validRuleList){ + List ruleIds = validRuleList.stream().filter(item -> BooleanUtil.isTrue(item.getEvaluate())) + .map(item -> Long.parseLong(item.getRuleId())).collect(Collectors.toList()); + List actionList = alarmRuleActionService.getListByRuleIds(ruleIds); + List caches = new ArrayList<>(); + actionList.forEach(item -> { + if(caches.stream().noneMatch(cache -> Objects.equals(cache.getActionId(), item.getActionId()))) { + caches.add(ConvertUtils.sourceToTarget(item, AlarmRuleActionCache.class)); + } + }); + return caches; + } + + /** 告警前准备和执行告警 */ + private void handleAlarm(List validRuleList, List tsKvList) { + // 数据组装 + List alarmJsonList = fill(validRuleList, tsKvList); + + // 生成告警行为缓存对象 + List caches = generateCaches(validRuleList); + + // 执行告警 + alarmService.alarm(alarmJsonList, caches); + } + + /** + * 查询元素数据 + * + * @param enableList 元素列表 + * @return list + */ + private List getTsKvList(List enableList) { + Map> collect = enableList.parallelStream().flatMap(item -> item.getList().stream()) + .collect(Collectors.groupingBy(item -> item.getThingId() + "," + item.getThingCode(), + Collectors.mapping(AlarmJson.AlarmEntity::getThingAttr, Collectors.toList()))); + return tsKvService.getLastTimeSeries(collect); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/CalculationJobServiceImpl.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/CalculationJobServiceImpl.java new file mode 100644 index 0000000..d8011bd --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/CalculationJobServiceImpl.java @@ -0,0 +1,309 @@ +package com.thing.quartz.timetask.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.calculation.calculation.dto.CalculationForm; +import com.thing.calculation.calculation.dto.CalculationFormulaAttributeDTO; +import com.thing.calculation.calculation.dto.CalculationFormulaSettingDTO; +import com.thing.calculation.calculation.dto.CalculationJson; +import com.thing.calculation.calculation.entity.CalculationFormulaLogEntity; +import com.thing.calculation.calculation.entity.CalculationFormulaSettingEntity; +import com.thing.calculation.calculation.mapper.CalculationFormulaSettingMapper; +import com.thing.calculation.calculation.service.CalculationFormulaAttributeService; +import com.thing.calculation.calculation.service.CalculationFormulaLogService; +import com.thing.calculation.calculation.service.CalculationFormulaSettingService; +import com.thing.calculation.calculation.service.CalculationService; +import com.thing.common.core.enumeration.CalculationStatusEnum; +import com.thing.common.core.enumeration.TriggerTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.data.dto.TsKvReqParam; +import com.thing.quartz.timetask.service.CalculationJobService; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +import static com.thing.calculation.calculation.entity.table.CalculationFormulaSettingEntityTableDef.CALCULATION_FORMULA_SETTING_ENTITY; + +/** + * @author zhenghh. 2022-05-26 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class CalculationJobServiceImpl implements CalculationJobService { + + private final CalculationFormulaAttributeService attributeService; + private final CalculationFormulaSettingService settingService; + private final CalculationFormulaLogService logService; + private final TskvService tsKvService; + private final CalculationFormulaSettingMapper settingDao; + private final CalculationService calculationService; + + /** + * 物计算job + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void calculation() { + //当前时间 + long nowTime = System.currentTimeMillis(); + //计算设置 + List formulaSettingList = getFormulaSettingList(nowTime); + //数据值 + List dataList = getFormulaTsKvList(formulaSettingList, nowTime); + //修改nextTs + settingService.updateBatchById(formulaSettingList.parallelStream().map(item -> { + CalculationFormulaSettingEntity entity = new CalculationFormulaSettingEntity(); + entity.setId(item.getId()); + entity.setNextTs(item.getNextTs()); + return entity; + }).collect(Collectors.toList())); + calculationService.calculation(formulaSettingList, dataList, TriggerTypeEnum.JOB); + } + + + /** + * 异常物计算 + * + * @param calculationNum 最大执行次数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void calculation(Integer calculationNum) { + List logList = logService.getCalculationList(calculationNum); + if (CollectionUtil.isEmpty(logList)) { + return; + } + //修改计算执行次数 + logService.saveBatch(logList.parallelStream() + .map(item -> new CalculationFormulaLogEntity(item.getId(), item.getCalculationNum() + 1)) + .collect(Collectors.toList())); + //计算设置 + List formulaSettingList = getFormulaSettingList(logList); + //数据值 + List dataList = getFormulaTsKvList(formulaSettingList); + if (CollectionUtil.isEmpty(dataList)) { + return; + } + calculationService.calculation(formulaSettingList, dataList, TriggerTypeEnum.ERROR); + } + + /** + * 查询元素数据 + * + * @param list 计算设置列表 + * @return list + */ + private List getFormulaTsKvList(List list, Long nowTime) { + List result = Lists.newArrayList(); + for (CalculationFormulaSettingDTO dto : list) { + List tsKvList = getTsKvList(dto.getAttributes(), dto.getNextTs()); + //数据稳定但是有延迟,打开这个注释 +// //todo 数据断点问题解决 +// if(tsKvList.size()!=dto.getAttributes().size()){ +// continue; +// } + long frequency = dto.getFrequency() * 1000L; + long offset = dto.getFormulaOffset() * 1000L; + //第一次计算 + if (dto.getNextTs() == null) { + //获取最大值 + OptionalLong max = tsKvList.parallelStream().filter(tsKv -> dto.getAttributes().stream() + .anyMatch(attr -> StringUtils.equals(tsKv.getThingCode(), attr.getThingCode()) && StringUtils.equals(tsKv.getThingAttr(), attr.getThingAttr()))) + .mapToLong(TsKvDTO::getTs).max(); + if (max.isPresent()) { + dto.setNextTs(dto.getRepeat() ? max.getAsLong() : max.getAsLong() + frequency); + } + } else { + //重复计算 + // 1、超过了设置计算周期,计算下一笔数据 + // 2、未超过了设置计算周期,继续计算当前数据 + //不重复计算 只计算一次 + if (dto.getRepeat()) { + long nextCycleTime = dto.getNextTs() + frequency + offset; + dto.setNextTs(nowTime < nextCycleTime ? dto.getNextTs() : dto.getNextTs() + frequency); + } else { + dto.setNextTs(nowTime < dto.getNextTs() + frequency ? dto.getNextTs() : dto.getNextTs() + frequency); + } + } + if (CollectionUtil.isNotEmpty(tsKvList)) { + result.addAll(tsKvList.parallelStream() + .map(tsKv -> new CalculationForm(tsKv.getThingCode(), tsKv.getThingAttr(), tsKv.getTs(), new BigDecimal(tsKv.getVal()))) + .collect(Collectors.toList())); + } + } + return result; + } + + /** + * 查询元素数据 + * + * @param list 计算设置列表 + * @return list + */ + private List getFormulaTsKvList(List list) { + List result = Lists.newArrayList(); + for (CalculationFormulaSettingDTO dto : list) { + List tsList = dto.getCalculationLogList().parallelStream().map(CalculationFormulaLogEntity::getCalculationTs).collect(Collectors.toList()); + for (Long ts : tsList) { + List tsKvList = getTsKvList(dto.getAttributes(), ts); + if (CollectionUtil.isNotEmpty(tsKvList)) { + result.addAll(tsKvList.parallelStream() + .map(tsKv -> new CalculationForm(tsKv.getThingCode(), tsKv.getThingAttr(), tsKv.getTs(), new BigDecimal(tsKv.getVal()))) + .collect(Collectors.toList())); + } + } + } + return result; + } + + /** + * 查询元素数据 + * + * @param attributes 元素列表 + * @param ts 时间 + * @return list + */ + private List getTsKvList(List attributes, Long ts) { + try { + //按物编码分组 + Map> collect = attributes.parallelStream() + .collect(Collectors.groupingBy(item -> item.getThingId() + "," + item.getThingCode(), + Collectors.mapping(CalculationFormulaAttributeDTO::getThingAttr, Collectors.toList()))); + return Objects.isNull(ts) ? tsKvService.getLastTimeSeries(collect) : tsKvService.getTimeSeries(collect, ts - 900000L, ts + 1000L); + } catch (Exception e) { + log.error("物计算获取数据失败: {}", e.getMessage()); + return Lists.newArrayList(); + } + } + + /** + * 获取待计算数据 + * + * @param nowTime 当前时间 + * @return list + */ + private List getFormulaSettingList(long nowTime) { + List calculationList = settingDao.selectListByQuery(QueryWrapper.create() + .eq(CalculationFormulaSettingEntity::getEnable, true) + .eq(CalculationFormulaSettingEntity::getType, TriggerTypeEnum.JOB.getValue()) + .and(CALCULATION_FORMULA_SETTING_ENTITY.NEXT_TS.isNull().or(CALCULATION_FORMULA_SETTING_ENTITY.NEXT_TS.le(nowTime))) + ); + if (CollectionUtil.isEmpty(calculationList)) { + return Lists.newArrayList(); + } + List times = calculationList.stream().map(CalculationFormulaSettingEntity::getNextTs).filter(Objects::nonNull).distinct().collect(Collectors.toList()); + List calculationIds = calculationList.stream().map(CalculationFormulaSettingEntity::getId).collect(Collectors.toList()); + return settingService.selectCalculationList(times, calculationIds, TriggerTypeEnum.JOB); + + } + + /** + * 获取异常物计算设置 + * + * @param logList 异常计算列表 + * @return list + */ + private List getFormulaSettingList(List logList) { + Map> calculationMap = logList.parallelStream().collect(Collectors.groupingBy(CalculationFormulaLogEntity::getCalculationId)); + List calculationIds = new ArrayList<>(calculationMap.keySet()); + List formulaSettingEntities = settingDao.selectListByIds(calculationIds); + List attributeList = attributeService.getByCalculationIdList(calculationIds); + return formulaSettingEntities.parallelStream() + .map(item -> { + CalculationFormulaSettingDTO dto = ConvertUtils.sourceToTarget(item, CalculationFormulaSettingDTO.class); + dto.setCalculationLogList(calculationMap.get(item.getId())); + dto.setAttributes(attributeList.stream().filter(attr -> Objects.equals(attr.getCalculationId(), item.getId())).collect(Collectors.toList())); + return dto; + }) + .filter(item -> CollectionUtil.isNotEmpty(item.getCalculationLogList())) + .collect(Collectors.toList()); + } + + /** + * 计算日志 + * + * @param logEntityList 历史计算日志列表 + * @param calculationJson 当前计算 + * @return CalculationFormulaLogEntity + */ + @Override + public CalculationFormulaLogEntity convertLogEntity(List logEntityList, CalculationJson calculationJson) { + List calculationInfo = calculationJson.getAttrList().stream().map(attr -> { + JSONObject info = new JSONObject(); + info.put("defVal", attr.getDefVal()); + info.put("label", attr.getFormulaLabel()); + info.put("value", attr.getLastValue()); + return info; + }).collect(Collectors.toList()); + return logEntityList.stream() + .filter(log -> Objects.equals(calculationJson.getCalculationId(), log.getCalculationId()) && Objects.equals(calculationJson.getLastTime(), log.getCalculationTs())) + .findFirst() + .map(log -> new CalculationFormulaLogEntity(log.getId(), JSONObject.toJSONString(calculationInfo), calculationJson.getCalculationStatus().getValue(), log.getCalculationNum() + 1)) + .orElseGet(() -> new CalculationFormulaLogEntity(calculationJson.getCalculationId(), calculationJson.getLastTime(), JSONObject.toJSONString(calculationInfo), calculationJson.getCalculationStatus().getValue())); + } + + /** + * 推送tb + * + * @param calculationJsonList 计算结果 + * @param logEntityList 已存在日志 + * @return list 日志列表 + */ + @Override + public List sendTb(List calculationJsonList, List logEntityList) { + List logList = new ArrayList<>(calculationJsonList.size()); + Map> calculationResultList = getCalculationResultList(calculationJsonList); + calculationResultList.forEach((thingCode, list) -> { + CalculationStatusEnum status = CalculationStatusEnum.SUCCESS; + try { + //deviceDataFacade.saveEntityTelemetryByRuleEngine(Lists.newArrayList(new TelemetrySaveDTO(thingCode, list))); + } catch (Exception e) { + log.error("{}推送失败: {}", thingCode, e.getMessage()); + status = CalculationStatusEnum.ERROR; + } + CalculationStatusEnum finalStatus = status; + logList.addAll(calculationJsonList.parallelStream() + .filter(item -> StringUtils.equals(thingCode, item.getResultCode())) + .map(item -> { + //排除默认值计算 + if (!CalculationStatusEnum.DEFAULT.equals(item.getCalculationStatus()) && CalculationStatusEnum.ERROR.equals(finalStatus)) { + item.setCalculationStatus(finalStatus); + } + return convertLogEntity(logEntityList, item); + }) + .collect(Collectors.toList())); + }); + return logList; + } + + /** + * 返回计算成功值 + * + * @param calculationJsonList 计算列表 + * @return list + */ + private Map> getCalculationResultList(List calculationJsonList) { + return calculationJsonList.parallelStream() + .filter(CalculationJson::getSendTb) + .filter(item -> CalculationStatusEnum.SUCCESS.equals(item.getCalculationStatus()) + || CalculationStatusEnum.DEFAULT.equals(item.getCalculationStatus())) + .collect(Collectors.groupingBy(CalculationJson::getResultCode, Collectors.mapping(item -> { + Map map = Maps.newHashMapWithExpectedSize(1); + map.put(item.getResultAttr(), item.getLastValue()); + return new TsKvReqParam(item.getLastTime(), map); + }, Collectors.toList()))); + } + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobLogServiceImpl.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobLogServiceImpl.java new file mode 100644 index 0000000..bcae311 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobLogServiceImpl.java @@ -0,0 +1,41 @@ + + +package com.thing.quartz.timetask.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.quartz.timetask.dto.ScheduleJobDTO; +import com.thing.quartz.timetask.dto.ScheduleJobLogDTO; +import com.thing.quartz.timetask.entity.ScheduleJobLogEntity; +import com.thing.quartz.timetask.mapper.ScheduleJobLogMapper; +import com.thing.quartz.timetask.service.ScheduleJobLogService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class ScheduleJobLogServiceImpl extends BaseServiceImpl implements ScheduleJobLogService { + + @Override + public PageData page(Map params) { + return getPageData(params, ScheduleJobLogDTO.class); + } + + public QueryWrapper getWrapper(Map params){ + String jobId = (String)params.get("jobId"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like( "bean_name", jobId,StringUtils.isNotBlank(jobId)); + return wrapper; + } + + @Override + public ScheduleJobLogDTO get(Long id) { + ScheduleJobLogEntity entity = mapper.selectOneById(id); + return ConvertUtils.sourceToTarget(entity, ScheduleJobLogDTO.class); + } + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobServiceImpl.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobServiceImpl.java new file mode 100644 index 0000000..640ac75 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/service/impl/ScheduleJobServiceImpl.java @@ -0,0 +1,112 @@ + + +package com.thing.quartz.timetask.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.ScheduleStatus; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.quartz.timetask.dto.ScheduleJobDTO; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import com.thing.quartz.timetask.mapper.ScheduleJobMapper; +import com.thing.quartz.timetask.service.ScheduleJobService; +import com.thing.quartz.timetask.utils.ScheduleUtils; +import org.apache.commons.lang3.StringUtils; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +@Service +public class ScheduleJobServiceImpl extends BaseServiceImpl implements ScheduleJobService { + @Autowired + private Scheduler scheduler; + + @Override + public PageData page(Map params) { + return getPageData(params, ScheduleJobDTO.class); + } + + @Override + public ScheduleJobDTO get(Long id) { + ScheduleJobEntity entity = mapper.selectOneById(id); + return ConvertUtils.sourceToTarget(entity, ScheduleJobDTO.class); + } + + public QueryWrapper getWrapper(Map params){ + String beanName = (String)params.get("beanName"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like( "bean_name", beanName,StringUtils.isNotBlank(beanName)); + return wrapper; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + entity.setStatus(ScheduleStatus.NORMAL.getValue()); + this.save(entity); + ScheduleUtils.createScheduleJob(scheduler, entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(ScheduleJobDTO dto) { + ScheduleJobEntity entity = ConvertUtils.sourceToTarget(dto, ScheduleJobEntity.class); + + ScheduleUtils.updateScheduleJob(scheduler, entity); + + this.updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatch(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.deleteScheduleJob(scheduler, id); + } + + //删除数据 + this.removeByIds(Arrays.asList(ids)); + } + + @Override + public int updateBatch(Long[] ids, int status){ + Map map = new HashMap<>(2); + map.put("ids", ids); + map.put("status", status); + return mapper.updateBatch(map); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void run(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.run(scheduler, mapper.selectOneById(id)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void pause(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.pauseJob(scheduler, id); + } + updateBatch(ids, ScheduleStatus.PAUSE.getValue()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void resume(Long[] ids) { + for(Long id : ids){ + ScheduleUtils.resumeJob(scheduler, id); + } + updateBatch(ids, ScheduleStatus.NORMAL.getValue()); + } + +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/AlarmTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/AlarmTask.java new file mode 100644 index 0000000..37eb5f2 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/AlarmTask.java @@ -0,0 +1,27 @@ +package com.thing.quartz.timetask.task; + +import com.thing.quartz.timetask.service.AlarmJobService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * @author zhenghh. 2022-07-20 + **/ +@Slf4j +@RequiredArgsConstructor +@Component("AlarmTask") +public class AlarmTask implements ITask { + + private final AlarmJobService alarmJobService; + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Override + public void run(String params) { + alarmJobService.alarm(); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ITask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ITask.java new file mode 100644 index 0000000..8729834 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ITask.java @@ -0,0 +1,22 @@ + + +package com.thing.quartz.timetask.task; + +import org.springframework.scheduling.annotation.Async; + +/** + * 定时任务接口,所有定时任务都要实现该接口 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface ITask { + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Async + void run(String params); +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/PushMsgTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/PushMsgTask.java new file mode 100644 index 0000000..67f8de4 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/PushMsgTask.java @@ -0,0 +1,29 @@ +package com.thing.quartz.timetask.task; + +import com.thing.msg.push.service.MsgPushService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 推送定时任务 + * + * @author zzx 2022-07-14 + **/ +@Slf4j +@RequiredArgsConstructor +@Component("PushMsgTask") +public class PushMsgTask implements ITask { + private final MsgPushService msgPushService; + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Override + public void run(String params) { + //数据推送 + msgPushService.sendMsgByTimeTask(); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/RepeatPushTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/RepeatPushTask.java new file mode 100644 index 0000000..9a2b0c6 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/RepeatPushTask.java @@ -0,0 +1,37 @@ +package com.thing.quartz.timetask.task; + +import cn.hutool.core.collection.CollectionUtil; +import com.thing.alarm.msgpush.service.MsgPushSettingService; +import com.thing.msg.history.dto.MsgHisDTO; +import com.thing.msg.history.service.MsgHisService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * @author 2022-07-26 zhouzx + **/ +@Slf4j +@RequiredArgsConstructor +@Component("RepeatPushTask") +public class RepeatPushTask implements ITask{ + + private final MsgHisService msgHisService; + + private final MsgPushSettingService msgPushSettingService; + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Override + public void run(String params) { + //1.检索推送记录表,重复推送为开启,且未处理完成的告警 + List list = msgHisService.getRepeatPushMsgList(); + if (CollectionUtil.isEmpty(list)) return; + //2.进行推送操作 + msgPushSettingService.repeatPushMsg(list); + } +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/TestTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/TestTask.java new file mode 100644 index 0000000..eb0e571 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/TestTask.java @@ -0,0 +1,29 @@ + + +package com.thing.quartz.timetask.task; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +/** + * 测试定时任务(演示Demo,可删除) + * + * testTask为spring bean的名称 + * + * @author Mark sunlightcs@gmail.com + */ +@Component("testTask") +@Slf4j +public class TestTask implements ITask{ + + @Override + public void run(String params) { + log.info("testTask 开始执行==============================="); + try { + Thread.sleep(1000*6); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + log.info("testTask 执行完毕==============================="); + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ThingStatusTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ThingStatusTask.java new file mode 100644 index 0000000..00a4cbd --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/ThingStatusTask.java @@ -0,0 +1,81 @@ +package com.thing.quartz.timetask.task; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.GateWayStatus; +import com.thing.common.core.enumeration.ThingStatus; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.model.dto.IotThingModelDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Comparator; +import java.util.Date; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 设备在线离线 + * + * @author zhenghh. 2022-02-14 + **/ + +@Slf4j +@RequiredArgsConstructor +@Component("ThingStatusTask") +public class ThingStatusTask implements ITask { + + /** + * 时间间隔45min + */ + private static final long TIME_INTERVAL = 45 * 60 * 1000; + + private final ThingManageContextService thingManageContextService; + private final TsKvService tsKvService; + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Override + public void run(String params) { + log.info("设备在线离线 statusTask start"); + //所有物 + Optional> optionalList = thingManageContextService.findModelByGateway(GateWayStatus.NO_GATE_WAY.getValue()); + if(optionalList.isEmpty()) { + return; + } + //获取最新值 + List codes = optionalList.get().stream().map(s -> s.get(CacheNameEnum.ModelField.THING_MODEL_CODE.getField()).asText()).toList(); + List lastTsKvList = tsKvService.findLatestByCodesAndAttrs(codes, null, true); + //处理 + List statusList = optionalList.get().stream() + .map(item -> { + //找到最大时间的属性 + Optional optionalMax = lastTsKvList.stream() + .filter(tsKvDTO -> StringUtils.equals(tsKvDTO.getThingCode(),item.get(CacheNameEnum.ModelField.THING_MODEL_CODE.getField()).asText())) + .max(Comparator.comparing(TsKvDTO::getTs)); + if(optionalMax.isEmpty()){ + item.put(CacheNameEnum.ModelField.THING_MODEL_STATUS.getField(),ThingStatus.OFFLINE.getCode()); + }else{ + TsKvDTO tsKvDTO = optionalMax.get(); + //根据当前时间和获取的最新时间 若大于45min 则离线 否则为在线 + boolean isOffline = System.currentTimeMillis() - tsKvDTO.getTs() > TIME_INTERVAL; + item.put(CacheNameEnum.ModelField.THING_MODEL_STATUS.getField(),isOffline ? ThingStatus.OFFLINE.getCode() : ThingStatus.ONLINE.getCode()); + item.put(CacheNameEnum.ModelField.THING_MODEL_STATUS_TS.getField(),new Date(tsKvDTO.getTs()).getTime()); + } + return JacksonUtil.convertValue(item, IotThingModelDTO.class); + }).collect(Collectors.toList()); + //修改状态 + thingManageContextService.saveModelBatch(statusList); + log.info("设备在线离线 statusTask end"); + } + +} diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/task/WeChatTokenRefreshTask.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/WeChatTokenRefreshTask.java new file mode 100644 index 0000000..d501b18 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/task/WeChatTokenRefreshTask.java @@ -0,0 +1,33 @@ + + +package com.thing.quartz.timetask.task; + +import com.thing.msg.cache.service.MsgCacheService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * @author zy@xxx.com + */ +@Slf4j +@RequiredArgsConstructor +@Component("WeChatTokenRefreshTask") +public class WeChatTokenRefreshTask implements ITask { + + @Autowired + private MsgCacheService msgCacheService; + + /** + * 执行定时任务接口 + * + * @param params 参数,多参数使用JSON数据 + */ + @Override + public void run(String params) { + log.info("WeChatTokenRefreshTask定时任务正在执行"); + msgCacheService.refreshToken(null); + } +} + diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleJob.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleJob.java new file mode 100644 index 0000000..636fdc9 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleJob.java @@ -0,0 +1,79 @@ +package com.thing.quartz.timetask.utils; + + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ExceptionUtils; +import com.thing.common.core.utils.IpUtils; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import com.thing.quartz.timetask.entity.ScheduleJobLogEntity; +import com.thing.quartz.timetask.service.ScheduleJobLogService; +import org.quartz.JobExecutionContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.quartz.QuartzJobBean; + +import java.lang.reflect.Method; +import java.net.Inet4Address; +import java.net.InetAddress; +import java.net.NetworkInterface; +import java.net.UnknownHostException; +import java.util.Date; +import java.util.Enumeration; + + +/** + * 定时任务 + * + * @author Mark sunlightcs@gmail.com + */ +public class ScheduleJob extends QuartzJobBean { + private Logger logger = LoggerFactory.getLogger(getClass()); + + @Override + protected void executeInternal(JobExecutionContext context) { + ScheduleJobEntity scheduleJob = (ScheduleJobEntity) context.getMergedJobDataMap(). + get(ScheduleUtils.JOB_PARAM_KEY); + + //数据库保存执行记录 + ScheduleJobLogEntity log = new ScheduleJobLogEntity(); + log.setJobId(scheduleJob.getId()); + log.setBeanName(scheduleJob.getBeanName()); + log.setParams(scheduleJob.getParams()); + log.setCreateDate(new Date()); + + //任务开始时间 + long startTime = System.currentTimeMillis(); + + try { + //执行任务 + logger.info("任务准备执行,任务ID:{}", scheduleJob.getId()); + Object target = SpringContextUtils.getBean(scheduleJob.getBeanName()); + Method method = target.getClass().getDeclaredMethod("run", String.class); + method.invoke(target, scheduleJob.getParams()); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + //任务状态 + log.setStatus(Constant.SUCCESS); + log.setIp(IpUtils.getLocalIpV4()); + + //logger.info("任务执行完毕,任务ID:{} 总共耗时:{} 毫秒", scheduleJob.getId(), times); + } catch (Exception e) { + logger.error("任务执行失败,任务ID:{}", scheduleJob.getId(), e); + + //任务执行总时长 + long times = System.currentTimeMillis() - startTime; + log.setTimes((int)times); + + //任务状态 + log.setStatus(Constant.FAIL); + log.setError(ExceptionUtils.getErrorStackTrace(e)); + }finally { + //获取spring bean + ScheduleJobLogService scheduleJobLogService = SpringContextUtils.getBean(ScheduleJobLogService.class); + scheduleJobLogService.save(log); + } + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleUtils.java b/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleUtils.java new file mode 100644 index 0000000..7f6d6b3 --- /dev/null +++ b/modules/quartz/src/main/java/com/thing/quartz/timetask/utils/ScheduleUtils.java @@ -0,0 +1,155 @@ + + +package com.thing.quartz.timetask.utils; + +import com.thing.common.core.enumeration.ScheduleStatus; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.quartz.timetask.entity.ScheduleJobEntity; +import org.quartz.*; + +/** + * 定时任务工具类 + * + * @author Mark sunlightcs@gmail.com + */ +public class ScheduleUtils { + private final static String JOB_NAME = "TASK_"; + /** + * 任务调度参数key + */ + public static final String JOB_PARAM_KEY = "JOB_PARAM_KEY"; + + /** + * 获取触发器key + */ + public static TriggerKey getTriggerKey(Long jobId) { + return TriggerKey.triggerKey(JOB_NAME + jobId); + } + + /** + * 获取jobKey + */ + public static JobKey getJobKey(Long jobId) { + return JobKey.jobKey(JOB_NAME + jobId); + } + + /** + * 获取表达式触发器 + */ + public static CronTrigger getCronTrigger(Scheduler scheduler, Long jobId) { + try { + return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId)); + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 创建定时任务 + */ + public static void createScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //构建job信息 + JobDetail jobDetail = JobBuilder.newJob(ScheduleJob.class).withIdentity(getJobKey(scheduleJob.getId())).build(); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + //按新的cronExpression表达式构建一个新的trigger + CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(getTriggerKey(scheduleJob.getId())).withSchedule(scheduleBuilder).build(); + + //放入参数,运行时的方法可以获取 + jobDetail.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.scheduleJob(jobDetail, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 更新定时任务 + */ + public static void updateScheduleJob(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + TriggerKey triggerKey = getTriggerKey(scheduleJob.getId()); + + //表达式调度构建器 + CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCronExpression()) + .withMisfireHandlingInstructionDoNothing(); + + CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getId()); + + //按新的cronExpression表达式重新构建trigger + trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build(); + + //参数 + trigger.getJobDataMap().put(JOB_PARAM_KEY, scheduleJob); + + scheduler.rescheduleJob(triggerKey, trigger); + + //暂停任务 + if(scheduleJob.getStatus() == ScheduleStatus.PAUSE.getValue()){ + pauseJob(scheduler, scheduleJob.getId()); + } + + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 立即执行任务 + */ + public static void run(Scheduler scheduler, ScheduleJobEntity scheduleJob) { + try { + //参数 + JobDataMap dataMap = new JobDataMap(); + dataMap.put(JOB_PARAM_KEY, scheduleJob); + + scheduler.triggerJob(getJobKey(scheduleJob.getId()), dataMap); + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 暂停任务 + */ + public static void pauseJob(Scheduler scheduler, Long jobId) { + try { + scheduler.pauseJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 恢复任务 + */ + public static void resumeJob(Scheduler scheduler, Long jobId) { + try { + scheduler.resumeJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } + + /** + * 删除定时任务 + */ + public static void deleteScheduleJob(Scheduler scheduler, Long jobId) { + try { + scheduler.deleteJob(getJobKey(jobId)); + } catch (SchedulerException e) { + throw new SysException(ErrorCode.JOB_ERROR, e); + } + } +} \ No newline at end of file diff --git a/modules/quartz/src/main/resources/mapper/task/IotTaskMapper.xml b/modules/quartz/src/main/resources/mapper/task/IotTaskMapper.xml new file mode 100644 index 0000000..16e59f8 --- /dev/null +++ b/modules/quartz/src/main/resources/mapper/task/IotTaskMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobLogMapper.xml b/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobLogMapper.xml new file mode 100644 index 0000000..4c7af0f --- /dev/null +++ b/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobLogMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobMapper.xml b/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobMapper.xml new file mode 100644 index 0000000..1f4c7ec --- /dev/null +++ b/modules/quartz/src/main/resources/mapper/timetask/ScheduleJobMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + update schedule_job set status = #{status} where id in + + #{id} + + + + \ No newline at end of file diff --git a/modules/report-analysis/pom.xml b/modules/report-analysis/pom.xml new file mode 100644 index 0000000..7ae3320 --- /dev/null +++ b/modules/report-analysis/pom.xml @@ -0,0 +1,51 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + report-analysis + jar + ThingBI Server Modules report-analysis + + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-aop + + + com.thing.common + cache + + + com.thing.common + orm + + + + + + + com.thing.modules + thing + + + com.thing.modules + alarm + + + com.thing.modules + publicorg + + + + \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyDictRelationController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyDictRelationController.java new file mode 100644 index 0000000..1f5fa6f --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyDictRelationController.java @@ -0,0 +1,113 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.dto.CarbonEnergyDictRelationDTO; +import com.thing.carbon.config.dto.CarbonThingEnergyDictReqDTO; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 能源属性关系 + * + * @author ssy + * @since 3.0 2023-09-19 + */ +@RestController +@RequestMapping("v2/config/carbon/energy/dict/relation") +@Tag(name = "用量属性维护") +@RequiredArgsConstructor +public class CarbonEnergyDictRelationController { + + private final CarbonEnergyDictRelationService carbonEnergyDictRelationService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "energyVarietyId", description = "能源品种id"), + @Parameter(name = "attrType", description = "用量区间类型[base, am, hh, dd, mm, yy]") + }) + public Result> page( @RequestParam Map params) { + params.put(Constant.ORDER_FIELD, "energy_variety_id, attr_code"); + PageData page = carbonEnergyDictRelationService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + CarbonEnergyDictRelationDTO data = carbonEnergyDictRelationService.getByIdAs(id,CarbonEnergyDictRelationDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonEnergyDictRelationDTO dto) { + checkPermission(); + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + carbonEnergyDictRelationService.handleSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonEnergyDictRelationDTO dto) { + checkPermission(); + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + dto.setUpdateDate(System.currentTimeMillis()); + carbonEnergyDictRelationService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + checkPermission(); + AssertUtils.isArrayEmpty(ids, "id"); + carbonEnergyDictRelationService.batchDelete(ids); + return new Result<>(); + } + + private void checkPermission() { + UserDetail user = SecurityUser.getUser(); + if (!Objects.equals(user.getSuperAdmin(), SuperAdminEnum.YES.value())) { + throw new SysException("非管理员无法操作!"); + } + } + + @PostMapping("list") + @Operation(summary="物的用能属性") + public Result> getThingEnergyDict(@RequestBody CarbonThingEnergyDictReqDTO params) { + return new Result>().ok(carbonEnergyDictRelationService.getThingEnergyDictList(params)); + } + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyPriceController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyPriceController.java new file mode 100644 index 0000000..9ee7d5f --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyPriceController.java @@ -0,0 +1,168 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.dto.CarbonEnergyPriceDTO; +import com.thing.carbon.config.dto.CarbonEnergyPriceReportReqDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.service.CarbonEnergyPriceService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 能源价格 + * + * @author ssy + * @since 3.0 2023-09-14 + */ +@RestController +@Tag(name = "能源价格") +@RequiredArgsConstructor +@RequestMapping("v2/config/carbon/energy/price") +public class CarbonEnergyPriceController { + + private final CarbonEnergyPriceService carbonEnergyPriceService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = "attrCode", description = "属性编号"), + @Parameter(name = "beginTime", description = "适用日期-开始时间"), + @Parameter(name = "endTime", description = "适用日期-结束时间") + }) + public Result> page( @RequestParam Map params) { + params.put(Constant.ORDER_FIELD, "attr_code, begin_time"); + PageData page = carbonEnergyPriceService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + CarbonEnergyPriceDTO data = carbonEnergyPriceService.getByIdAs(id,CarbonEnergyPriceDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonEnergyPriceDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class); + if(Objects.isNull(dto.getCompanyId())){ + dto.setCompanyId(UserContext.getRealCompanyId()); + } + carbonEnergyPriceService.checkAndSave(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonEnergyPriceDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class); + dto.setCompanyId(UserContext.getRealCompanyId()); + carbonEnergyPriceService.checkAndUpdate(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody List ids) { + //效验数据 + Long[] idArr = ids.stream().map(Long::parseLong).toArray(Long[]::new); + carbonEnergyPriceService.batchDelete(idArr); + return new Result<>(); + } + + @GetMapping("tenantList") + @Operation(summary="侧边栏所有有数据的企业列表") + public Result> tenantList() { + return new Result>().ok(carbonEnergyPriceService.tenantList()); + } + + @GetMapping("list") + @Operation(summary="根据企业编码code查询企业所有配置,查询不到返回默认企业的配置") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code") + }) + public Result> list( @RequestParam Map params) { + params.put(Constant.ORDER_FIELD, "attr_code, begin_time"); + params.put(Constant.ORDER, "asc"); + return new Result>().ok(carbonEnergyPriceService.listDTOs(params)); + } + + @GetMapping("delTenant/{id}") + @Operation(summary="根据企业编码,删除企业所有配置") + public Result delTenant(@PathVariable("id") Long delTenant) { + // 如果不是超级管理员,不允许删除默认租户下的数据 + UserDetail user = SecurityUser.getUser(); + boolean isSuperAdmin = Objects.equals(user.getSuperAdmin(), SuperAdminEnum.YES.value()); + if(!isSuperAdmin && Objects.equals(delTenant, 1001L)){ + throw new SysException("请勿删除默认数据"); + } + carbonEnergyPriceService.delTenant(delTenant); + return new Result().ok("删除成功"); + } + + @GetMapping("attrs") + @Operation(summary="查询当前企业在所有时间段内的用量属性列表") + public Result> attrsInAllTime() { + return new Result>().ok(carbonEnergyPriceService.attrsInAllTime()); + } + + @PostMapping("thing/energy/varieties") + @Operation(summary="查询当前企业在所有时间段内的用量属性列表") + public Result> thingEnergyVarieties(@RequestBody String[] thingIds) { + List ids = Arrays.stream(thingIds).map(Long::parseLong).collect(Collectors.toList()); + return new Result>().ok(carbonEnergyPriceService.thingEnergyVarieties(ids)); + } + + @PostMapping("report") + @Operation(summary= "能源成本报表") + public Result> energyPriceReport(@RequestBody CarbonEnergyPriceReportReqDTO request) { + ValidatorUtils.validateEntity(request); + List res = carbonEnergyPriceService.energyPriceReport(request); + return new Result>().ok(res); + } + + @PostMapping("report/export") + @Operation(summary = "能源成本报表-导出") + public void energyPriceReportExport(@RequestBody CarbonEnergyPriceReportReqDTO request, HttpServletResponse response) { + if ("1".equals(request.getType())) { + carbonEnergyPriceService.energyPriceReportExport(request, response); + } else { + carbonEnergyPriceService.energyPriceReportExportVertical(request, response); + } + } + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyVarietyController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyVarietyController.java new file mode 100644 index 0000000..46b9805 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonEnergyVarietyController.java @@ -0,0 +1,134 @@ +package com.thing.carbon.config.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.IsDefaultEnum; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + + +/** +* 能源品种基本信息 +* +* @author xc +* @since 3.0 2023-07-31 +*/ +@RestController +@RequestMapping("v2/config/carbonenergyvariety") +@Tag(name="能源品种基本信息") +public class CarbonEnergyVarietyController { + @Autowired + private CarbonEnergyVarietyService carbonEnergyVarietyService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "name", description = "能源品种"), + }) + public Result> page( @RequestParam Map params){ + PageData page = carbonEnergyVarietyService.pageCarbonEnergyVarietyDTO(params); + return new Result>().ok(page); + } + + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "tenantCode", description = "企业编码"), + }) + public Result> list( @RequestParam Map params){ + params.put("type","能源"); + List list = carbonEnergyVarietyService.listCarbonEnergyVarietyDTO(params); + return new Result>().ok(list); + } + + @GetMapping("listEnergy") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "tenantCode", description = "企业编码"), + }) + public Result> listEnergy( @RequestParam Map params){ + List list = carbonEnergyVarietyService.listCarbonEnergyVarietyDTO(params); + return new Result>().ok(list); + } + + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CarbonEnergyVarietyDTO temp = carbonEnergyVarietyService.getByIdAs(id,CarbonEnergyVarietyDTO.class); + isOperate(temp); + return new Result().ok(temp); + } + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody CarbonEnergyVarietyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + return carbonEnergyVarietyService.saveCarbonEnergyVarietyDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + public Result update(@RequestBody CarbonEnergyVarietyDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + carbonEnergyVarietyService.updateDto(dto); + + return new Result().ok("修改成功"); + } + + @DeleteMapping + @Operation(summary="删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + carbonEnergyVarietyService.batchDelete(ids); + + return new Result(); + } + + /** + * 确认是否可以编辑 + * @param temp + */ + public static void isOperate(CarbonEnergyVarietyDTO temp) { + if(ObjectUtil.equals(temp.getIsDefault(), IsDefaultEnum.Y.getValue())) { + if(!"1001".equals(String.valueOf(UserContext.getTenantCode()))){ + temp.setIsOperate("1"); + } + } + if (!String.valueOf(temp.getTenantCode()).equals(String.valueOf(UserContext.getTenantCode()))){ + if(!UserContext.isAdmin()){ + temp.setIsOperate("1"); + } + } + } + + + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonPeakConfigController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonPeakConfigController.java new file mode 100644 index 0000000..fe36720 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonPeakConfigController.java @@ -0,0 +1,137 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.dto.CarbonPeakConfigDTO; +import com.thing.carbon.config.service.CarbonPeakConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.tenant.dto.SysTenantDTO; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + + +/** +* 尖峰谷平配置 +* +* @author xc +* @since 3.0 2023-09-21 +*/ +@RestController +@RequestMapping("v2/config/carbonpeakconfig") +@Tag(name="尖峰谷平配置") +public class CarbonPeakConfigController { + @Autowired + private CarbonPeakConfigService carbonPeakConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "year", description = "年份"), + @Parameter(name = "baseAttrCode", description = "来源物属性编码"), + @Parameter(name = "buildAttrCode", description = "映射物属性编码") + }) + public Result> page( @RequestParam Map params){ + PageData page = carbonPeakConfigService.pageCarbonPeakConfigDTO(params); + + return new Result>().ok(page); + } + + + @GetMapping("list") + @Operation(summary="列表查询") + @Parameters({ + @Parameter(name = "tenantCode", description = "所属企业/租户code"), + @Parameter(name = "year", description = "年份"), + @Parameter(name = "baseAttrCode", description = "来源物属性编码"), + @Parameter(name = "buildAttrCode", description = "映射物属性编码") + }) + public Result> list( @RequestParam Map params){ + List list = carbonPeakConfigService.listCarbonPeakConfigDTO(params); + return new Result>().ok(list); + } + + + + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CarbonPeakConfigDTO data = carbonPeakConfigService.getByIdAs(id,CarbonPeakConfigDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody CarbonPeakConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + return carbonPeakConfigService.saveCarbonPeakConfigDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody CarbonPeakConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + return carbonPeakConfigService.updateCarbonPeakConfigDTO(dto); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + carbonPeakConfigService.batchDelete(ids); + + return new Result(); + } + + + @PostMapping("batchSave") + @Operation(summary="批量保存") + public Result batchSave(@RequestBody CarbonPeakConfigDTO[] dtos){ + carbonPeakConfigService.batchSave(dtos); + return new Result().ok("保存成功"); + } + + + @GetMapping("tenantList") + @Operation(summary="侧边栏所有有数据的企业列表") + public Result> tenantList(){ + List dtos =carbonPeakConfigService.tenantList(); + return new Result>().ok(dtos); + } + + @GetMapping("delTenant/{tenantCode}") + @Operation(summary="根据企业编码,删除企业所有配置") + public Result delTenant(@PathVariable("tenantCode") Long tenantCode){ + carbonPeakConfigService.delTenant(tenantCode); + return new Result().ok("删除成功"); + } + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonTsKvController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonTsKvController.java new file mode 100644 index 0000000..9b086ce --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/CarbonTsKvController.java @@ -0,0 +1,39 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.service.CarbonTsKvService; +import com.thing.common.core.web.response.Result; +import com.thing.publicorg.benchmarkingproject.dto.ThingSequentialInfoParamDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * 企业遥测数据缓存 + * + * @author ssy + * @since 3.0 2023-09-12 + */ +@RestController +@RequestMapping("v2/config/carbon/tskv") +@RequiredArgsConstructor +@Tag(name="企业遥测数据缓存") +public class CarbonTsKvController { + + private final CarbonTsKvService carbonTsKvService; + + /** + * 存储: pgsql和TB都做一次存储,pgsql的存储理解为TB的缓存 + * 计算:基于参数去控制是否需要汇总计算月、年的数据 + */ + @PostMapping + @Operation(summary="将设备的属性,属性值保存至pgsql和tb中") + public Result saveSequentialInfoToTb(@RequestBody ThingSequentialInfoParamDTO thingSequentialParamDTO){ + carbonTsKvService.handleSave(thingSequentialParamDTO); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/MeterReadController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/MeterReadController.java new file mode 100644 index 0000000..b77c810 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/MeterReadController.java @@ -0,0 +1,47 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.dto.MeterReadReqDTO; +import com.thing.carbon.config.dto.MeterReadRespDTO; +import com.thing.carbon.config.service.MeterReadService; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + + +/** + * Author: SiYang + * Date: 2023/10/19 16:03 + * Description: 抄表数据控制层 + */ +@RestController +@RequestMapping("v2/meter/read") +@Tag(name = "抄表数据模块") +@RequiredArgsConstructor +public class MeterReadController { + + private final MeterReadService meterReadService; + + @PostMapping("report") + @Operation(summary="抄表数据报表") + public Result> report(@RequestBody MeterReadReqDTO request) { + ValidatorUtils.validateEntity(request); + return new Result>().ok(meterReadService.report(request)); + } + + + @PostMapping("export") + @Operation(summary="抄表数据报表导出") + public void export(@RequestBody MeterReadReqDTO request, HttpServletResponse response) { + ValidatorUtils.validateEntity(request); + meterReadService.export(request, response); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/TransformerBizConfigController.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/TransformerBizConfigController.java new file mode 100644 index 0000000..4a460c1 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/controller/TransformerBizConfigController.java @@ -0,0 +1,105 @@ +package com.thing.carbon.config.controller; + +import com.thing.carbon.config.dto.TransformerBizConfigDTO; +import com.thing.carbon.config.service.TransformerBizConfigService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** +* 变压器业务配置 +* +* @author ssy +* @since 3.0 2023-12-18 +*/ +@RestController +@RequestMapping("v2/transformer/biz/config") +@Schema(description="变压器业务配置") +@RequiredArgsConstructor +public class TransformerBizConfigController { + private final TransformerBizConfigService transformerBizConfigService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + }) + public Result> page(@RequestParam Map params){ + PageData page = transformerBizConfigService.handlePage(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + public Result> list(@RequestParam Map params){ + List data = transformerBizConfigService.handleList(params); + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id){ + TransformerBizConfigDTO data = transformerBizConfigService.handleDetail(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@RequestBody TransformerBizConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class); + transformerBizConfigService.handleSave(dto); + return new Result<>(); + } + + @PostMapping("batchSave") + @Operation(summary = "批量保存") + @LogOperation("批量保存") + public Result batchSave(@RequestBody TransformerBizConfigDTO[] configList){ + transformerBizConfigService.handleBatchSave(Arrays.asList(configList)); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody TransformerBizConfigDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class); + transformerBizConfigService.handleUpdate(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody String[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + transformerBizConfigService.handleDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyDictRelationDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyDictRelationDTO.java new file mode 100644 index 0000000..91604b4 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyDictRelationDTO.java @@ -0,0 +1,88 @@ +package com.thing.carbon.config.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 能源属性关系 +* +* @author ssy +* @since 3.0 2023-09-19 +*/ +@Data +@Schema( name= "能源属性关系") +public class CarbonEnergyDictRelationDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + + @Schema(description = "数据主键id") + @NotNull(message = "主键不能为空", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "能源品种id") + @NotNull(message = "能源品种id不能为空", groups = AddGroup.class) + private Long energyVarietyId; + + @Schema(description = "能源品种名称") + private String energyVarietyName; + + @Schema(description = "能源品种Code") + private String energyVarietyCode; + + @Schema(description = "属性code") + private String attrCode; + + @Schema(description = "属性code列表") + @NotEmpty(message = "属性列表不能为空", groups = AddGroup.class) + private List attrCodeList; + + @Schema(description = "属性名称") + @NotNull(message = "属性名称不能为空", groups = UpdateGroup.class) + private String attrName; + + @Schema(description = "用量区间") + private String attrType; + + @Schema(description = "用量区间列表: am, hh, dd, mm, yy") + @NotEmpty(message = "用量区间列表不能为空", groups = AddGroup.class) + private List attrTypeList; + +// @Schema(description = "属性组id") +// private Long groupId; + + @Schema(description = "属性组") + private String groupName; + + @Schema(description = "所属企业/租户code") + private Long tenantCode; + + @Schema(description = "企业详情id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建者id") + private Long creator; + + @Schema(description = "创建时间") + private Long createDate; + + @Schema(description = "更新者id") + private Long updater; + + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceDTO.java new file mode 100644 index 0000000..b7cc76c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceDTO.java @@ -0,0 +1,108 @@ +package com.thing.carbon.config.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import javax.validation.constraints.Min; +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** + * 能源价格 + * + * @author ssy + * @since 3.0 2023-09-14 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "能源价格") +public class CarbonEnergyPriceDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + @NotNull(message = "主键不能为空", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "属性编号") + @NotNull(message = "属性编号不能为空", groups = {AddGroup.class, UpdateGroup.class}) + private String attrCode; + + @Schema(description = "属性名称") + @NotNull(message = "属性名称不能为空", groups = {AddGroup.class, UpdateGroup.class}) + private String attrName; + + @Schema(description = "能源品种id") + @NotNull(message = "能源品种id不能为空", groups = AddGroup.class) + private Long energyVarietyId; + + @Schema(description = "能源品种名称") + private String energyVarietyName; + + @Schema(description = "能源品种编号") + private String energyVarietyCode; + + @Schema(description = "能源标签") + @NotNull(message = "标签不能为空", groups = {AddGroup.class, UpdateGroup.class}) + private String tag; + + @Schema(description = "能源价格(元)") + @NotNull(message = "价格不能为空", groups = {AddGroup.class, UpdateGroup.class}) + @Min(value = 0, message = "价格必须为数字,最小值为0", groups = {AddGroup.class, UpdateGroup.class}) + private BigDecimal price; + + @Schema(description = "序号") + @NotNull(message = "序号不能为空", groups = {AddGroup.class, UpdateGroup.class}) + @Min(value = 0, message = "需要必须为数字,最小值为0", groups = {AddGroup.class, UpdateGroup.class}) + private Integer serialNumber; + + @Schema(description = "适用时间范围起始日期") + @NotNull(message = "适用时间范围起始日期不能为空", groups = {AddGroup.class, UpdateGroup.class}) + @JsonFormat(pattern = DateTimeUtils.DATE_PATTERN_STR) + private Date beginTime; + + @Schema(description = "适用时间范围截止日期") + @NotNull(message = "适用时间范围截止日期不能为空", groups = {AddGroup.class, UpdateGroup.class}) + @JsonFormat(pattern = DateTimeUtils.DATE_PATTERN_STR) + private Date endTime; + + @Schema(description = "计量单位") + @NotNull(message = "单位不能为空", groups = {AddGroup.class, UpdateGroup.class}) + private String unit; + + @Schema(description = "所属企业/租户code") + private Long tenantCode; + + @Schema(description = "企业详情id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建者id") + private Long creator; + + @Schema(description = "创建时间") + private Long createDate; + + @Schema(description = "更新者id") + private Long updater; + + @Schema(description = "更新时间") + private Long updateDate; + + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceReportReqDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceReportReqDTO.java new file mode 100644 index 0000000..ede3e92 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyPriceReportReqDTO.java @@ -0,0 +1,66 @@ +package com.thing.carbon.config.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotEmpty; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * Author: SiYang + * Date: 2023/09/25 8:47 + * Description: 能源成本报表请求参数 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "能源价格报表") +public class CarbonEnergyPriceReportReqDTO { + + @Schema(description = "开始时间") + private String beginTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "物Id") + @NotEmpty(message = "物Id列表不能为空") + private List thingIds; + + @Schema(description = "能源品种Id列表") + private List energyVarietyIds; + + @Schema(description = "属性类型(用量区间) am, dd, mm") + @NotNull(message = "用量区间不能为空") + private String attrType; + + @Schema(description = "时间类型 week, month, year, any") + @NotNull(message = "时间类型不能为空, 可选值:week, month, year, any") + private String timeType; + + @Schema(description = "是否进行价格试算,该参数为true时,energyPriceMap必填") + private Boolean isVirtualPrice; + + @Schema(description = "价格试算必填参数") + private Map virtualEnergyPriceMap; + + @Schema(description = "物关系根Id") + @NotNull(message = "物关系根Id不能为空") + private Long rootId; + + @Schema(description = "顶级物实体Id列表") + private List rootThingIds; + + @Schema(description = "1:竖直样式; 2:传统样式") + private String type; +} + + diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyDTO.java new file mode 100644 index 0000000..bc1ce9b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyDTO.java @@ -0,0 +1,53 @@ +package com.thing.carbon.config.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.springframework.boot.convert.Delimiter; + +import java.io.Serializable; + +/** +* 能源品种基本信息 +* +* @author xc +* @since 3.0 2023-07-31 +*/ +@Data +@Accessors(chain = true) +@Schema( name= "能源品种基本信息") +public class CarbonEnergyVarietyDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + @Schema(description = "能源品种名称 电,水,天然气,蒸汽...") + private String name; + @Schema(description = "能源品种类型 依靠当前系统定义,如电-A29...") + private String code; + @Schema(description = "源排放类别,中文描述如,电力,化石燃料,热力等") + private String type; + @Schema(description = "是否默认,0默认,1自定义,默认数据只有管理员可以操作") + private String isDefault; + @Schema(description = "备注") + private String remarks; + @Schema(description = "企业编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "是否可以编辑 0可编辑 1不可编辑") + @Delimiter(",") + private String isOperate ="0"; +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyReqDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyReqDTO.java new file mode 100644 index 0000000..a19feb9 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonEnergyVarietyReqDTO.java @@ -0,0 +1,32 @@ +package com.thing.carbon.config.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +/** + * Author: SiYang + * Date: 2023/10/16 15:19 + * Description: 能源品种查询参数 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class CarbonEnergyVarietyReqDTO { + + @Schema(description = "物id") + private Long thingId; + + @Schema(description = "关系根id") + private Long rootId; + + @Schema(description = "设备根id") + private Long rootThingId; + + @Schema(description = "属性类型") + private String attrType; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonPeakConfigDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonPeakConfigDTO.java new file mode 100644 index 0000000..52dfe19 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonPeakConfigDTO.java @@ -0,0 +1,61 @@ +package com.thing.carbon.config.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; + +/** +* 尖峰谷平配置 +* +* @author xc +* @since 3.0 2023-09-21 +*/ +@Data +@Schema( name= "尖峰谷平配置") +public class CarbonPeakConfigDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "能源品种id") + private Long evId; + @Schema(description = "能源名称") + private String evName; + @Schema(description = "年份(单选)") + private String year; + @Schema(description = "月份(可多选)") + private String months; + @Schema(description = "开始时间(0点至输入时间分钟值)例如01:00 = 60") + private Long startTime; + @Schema(description = "结束时间(0点至输入时间分钟值)例如02:00 = 120") + private Long endTime; + + @Schema(description = "开始时间(0点至输入时间分钟值)例如01:00 = 60") + private String startTimeF; + @Schema(description = "结束时间(0点至输入时间分钟值)例如02:00 = 120") + private String endTimeF; + + @Schema(description = "来源物属性编码") + private String baseAttrCode; + @Schema(description = "映射物属性编码") + private String buildAttrCode; + @Schema(description = "备注") + private String remarks; + @Schema(description = "所属企业/租户code") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者id") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者id") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonThingEnergyDictReqDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonThingEnergyDictReqDTO.java new file mode 100644 index 0000000..e6ad0c0 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonThingEnergyDictReqDTO.java @@ -0,0 +1,35 @@ +package com.thing.carbon.config.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/09/20 11:26 + * Description: 物的用量属性查询参数 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class CarbonThingEnergyDictReqDTO { + + /** + * 物id列表 + */ + private List thingIds; + + /** + * 能源品种id列表 + */ + private List energyVarietyIds; + + /** + * 属性类型(用量区间):base, am, hh, dd, mm, yy + */ + private List attrTypes; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTimeLabelData.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTimeLabelData.java new file mode 100644 index 0000000..7edf586 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTimeLabelData.java @@ -0,0 +1,205 @@ +package com.thing.carbon.config.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.common.core.annotation.CustomExcel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * Author: SiYang + * Date: 2023/09/21 14:02 + * Description: 时间标签数据, 给数据打上了时间与label标签 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class CarbonTimeLabelData { + + private static final Map weekMap = new HashMap<>(); + + static { + weekMap.put(1, "周一"); + weekMap.put(2, "周二"); + weekMap.put(3, "周三"); + weekMap.put(4, "周四"); + weekMap.put(5, "周五"); + weekMap.put(6, "周六"); + weekMap.put(7, "周日"); + } + + /** + * 数据的日期时间 + */ + @JsonIgnore + private LocalDateTime dateTime; + + private Long ts; + + private Integer year; + + private Integer month; + + private Integer hour; + + private Integer weekOfYear; + + private Integer dateOfMonth; + + private Integer dateOfWeek; + + /** + * 属性编号 + */ + private String attrCode; + + /** + * 数据库值 + */ + private BigDecimal value; + + /** + * 通过自定义的算法计算后的值 + */ + private BigDecimal calculatedValue; + + /** + * 数据标签, 根据接口需求自己定义 + */ + private String label; + + /** + * 导出时使用的值 + */ + @CustomExcel(columnNameGenerator = "generateTimeLabelTitle", width = 12, orderGenerator = "generateOrderNum", groupGenerator = "getGroupName") + private String exportValue; + + /** + * 时间类型:week, month, year, any + */ + private String timeType; + + /** + * 聚合之后,日的概念将会模糊,需要置空 + * + * @param label 期望生成的目标数据的标签 + * @return copy后的数据 + */ + public CarbonTimeLabelData copy(String label) { + return new CarbonTimeLabelData() + .setYear(this.year) + .setAttrCode(this.attrCode) + .setValue(BigDecimal.ZERO) + .setLabel(label); + } + + public CarbonTimeLabelData add(CarbonTimeLabelData data) { + BigDecimal currentVal = Optional.ofNullable(this.getValue()).orElse(BigDecimal.ZERO); + BigDecimal inputVal = Optional.ofNullable(data.getValue()).orElse(BigDecimal.ZERO); + + BigDecimal currentCalcVal = Optional.ofNullable(this.getCalculatedValue()).orElse(BigDecimal.ZERO); + BigDecimal inputCalcVal = Optional.ofNullable(data.getCalculatedValue()).orElse(BigDecimal.ZERO); + + this.setValue(currentVal.add(inputVal)); + this.setCalculatedValue(currentCalcVal.add(inputCalcVal)); + return this; + } + + @SuppressWarnings("unused") + private String generateTimeLabelTitle() { + StringBuilder strBuilder = new StringBuilder(); + switch (timeType) { + case "week": + if (Objects.equals(label, "item")) { + strBuilder + .append(addZeroPrefix(month)) + .append("-") + .append(addZeroPrefix(dateOfMonth)) + .append(" ") + .append(weekMap.get(dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + strBuilder.append(weekOfYear).append("周 总计"); + } + break; + case "day": + if (Objects.equals(label, "dayTotal")) { + strBuilder.append(month).append("天"); + } + break; + case "month": + if (Objects.equals(label, "item")) { + strBuilder.append(dateOfMonth).append("日"); + } + break; + case "year": + if (Objects.equals(label, "monthTotal")) { + strBuilder.append(month).append("月"); + } + break; + case "any": + break; + default: + } + return strBuilder.toString(); + } + + private String addZeroPrefix(Integer num) { + return (num < 10 && num > 0) ? "0" + num : num.toString(); + } + + @SuppressWarnings("unused") + public String getExportValue() { + return Objects.nonNull(getCalculatedValue()) ? getCalculatedValue().toString() : "--"; + } + + /** + * 序号生成: + * 1. 日期越大序号越小,最近的时间排在前面 + * 2. 周总计排在周数据前面 + */ + @SuppressWarnings("unused") + private int generateOrderNum() { + // 前面的序号已经到5了,最小不能比6小 + int orderNum = 6; + switch (timeType) { + case "week": + // 算法可以自定义,这里只是从数学上给出一个粗浅的计算方法 + if (Objects.equals(label, "item")) { + orderNum += (((52 - weekOfYear) << 3) + (8 - dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + orderNum += (52 - weekOfYear) << 3; + } + break; + case "month": + orderNum += (31 - dateOfMonth); + break; + case "day": + orderNum += (24 - hour); + break; + case "year": + orderNum += (12 - month); + break; + case "any": + break; + default: + } + return orderNum; + } + + @SuppressWarnings("unused") + private String getGroupName() { + if (!Objects.equals(timeType, "week")) { + return null; + } + return Objects.isNull(weekOfYear) ? null : weekOfYear + "周"; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTsKvDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTsKvDTO.java new file mode 100644 index 0000000..6c5613e --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/CarbonTsKvDTO.java @@ -0,0 +1,40 @@ +package com.thing.carbon.config.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** +* 企业遥测数据缓存 +* +* @author ssy +* @since 3.0 2023-09-12 +*/ +@Data +@Schema( name= "企业遥测数据缓存") +public class CarbonTsKvDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "企业设备id") + private Long thingId; + @Schema(description = "设备编码") + private String thingCode; + @Schema(description = "属性编码") + private String attrKey; + @Schema(description = "时间戳") + private Long ts; + @Schema(description = "遥测值") + private String val; + @Schema(description = "时间") + private Date dayTime; + @Schema(description = "创建时间") + private Long createTime; + @Schema(description = "更新时间") + private Long updateTime; + @Schema(description = "属性类型:dd, mm, yy") + private String attributeType; + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadReqDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadReqDTO.java new file mode 100644 index 0000000..1e65104 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadReqDTO.java @@ -0,0 +1,30 @@ +package com.thing.carbon.config.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/10/19 16:07 + * Description: 抄表数据请求 + */ +@Data +public class MeterReadReqDTO { + + @Schema(description = "物Id列表") + @NotEmpty(message = "物不能为空") + private List thingIds; + + @Schema(description = "能源品种Id列表") + @NotEmpty(message = "能源品种不能为空") + private List energyVarietyIds; + + @Schema(description = "开始时间") + @NotNull(message = "开始时间不能为空") + private String beginTime; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadRespDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadRespDTO.java new file mode 100644 index 0000000..1330f71 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/MeterReadRespDTO.java @@ -0,0 +1,41 @@ +package com.thing.carbon.config.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Map; + +/** + * Author: SiYang + * Date: 2023/10/19 16:07 + * Description: 抄表数据响应 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class MeterReadRespDTO { + + @Schema(description = "物名称") + private String thingName; + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "属性编码") + private String attrCode; + + @Schema(description = "能源品种名称") + private String energyVarietyName; + + @Schema(description = "能源属性单位") + private String unit; + + @Schema(description = "数据") + private Map data; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/TransformerBizConfigDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/TransformerBizConfigDTO.java new file mode 100644 index 0000000..a3d9d15 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/dto/TransformerBizConfigDTO.java @@ -0,0 +1,68 @@ +package com.thing.carbon.config.dto; + +import com.thing.thing.bizconfig.dto.IotThingBizConfigDTO; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.Objects; + +/** + * Author: SiYang + * Date: 2023/12/18 17:18 + * Description: 变压器业务配置 + * 用来定义出变压器分析业务中所需的具体属性 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class TransformerBizConfigDTO extends IotThingBizConfigDTO { + @Serial + private static final long serialVersionUID = -6832549893262327185L; + public static final String CONFIG_TYPE = "Transformer"; + + @Schema(description = "额定容量") + private BigDecimal ratedCapacity; + + @Schema(description = "容量阈值") + private BigDecimal capacityThreshold; + + @Schema(description = "付费方式") + private String payMode; + + @Schema(description = "基本电价") + private BigDecimal basePrice; + + /** 负载率阈值: 容量阈值/额定容量 */ + @SuppressWarnings("unused") + public BigDecimal getLoadRateThreshold() { + if (Objects.isNull(capacityThreshold) || Objects.isNull(ratedCapacity)) { + return null; + } + return capacityThreshold.divide(ratedCapacity, 2, RoundingMode.HALF_UP); + } + + @Override + public String getType() { + return CONFIG_TYPE; + } + + @Override + public void generateItems() { + appendItem("ratedCapacity", "额定容量", ratedCapacity); + appendItem("capacityThreshold", "容量阈值", capacityThreshold); + appendItem("payMode", "付费方式", payMode); + appendItem("basePrice", "基本电价", basePrice); + } + + @Override + public void fillConfigValue() { + ratedCapacity = getItemValue("ratedCapacity", toBigDecimal()); + capacityThreshold = getItemValue("capacityThreshold", toBigDecimal()); + basePrice = getItemValue("basePrice", toBigDecimal()); + payMode = getItemValue("payMode"); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyDictRelationEntity.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyDictRelationEntity.java new file mode 100644 index 0000000..c2191b9 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyDictRelationEntity.java @@ -0,0 +1,95 @@ +package com.thing.carbon.config.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.*; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 能源属性关系 + * + * @author ssy + * @since 3.0 2023-09-19 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Table("carbon_energy_dict_relation") +public class CarbonEnergyDictRelationEntity extends BaseInfoEntity{ + + @Serial + private static final long serialVersionUID = 1L; + + @Id + private Long id; + /** + * 能源品种id + */ + private Long energyVarietyId; + /** + * 属性code + */ + private String attrCode; + /** + * 属性名称 + */ + private String attrName; + /** + * 属性类型 + */ + private String attrType; + private String groupName; + /** + * 属性组id + */ +// private Long groupId; + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 公司id + */ + private Long companyId; + + /** + * 部门id + */ + private Long deptId; + + /*------------------------修改记录信息--------------------------------*/ + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Long updateDate; + + public void setTenantInfo(Long tenantCode) { + this.setTenantCode(tenantCode); + this.setCompanyId(tenantCode); + this.setDeptId(tenantCode); + } +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceEntity.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceEntity.java new file mode 100644 index 0000000..58d48f2 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceEntity.java @@ -0,0 +1,62 @@ +package com.thing.carbon.config.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.*; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * 能源价格 + * + * @author ssy + * @since 3.0 2023-09-14 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Table("carbon_energy_price") +public class CarbonEnergyPriceEntity extends BaseInfoEntity { + private static final long serialVersionUID = 1L; + /** + * 属性编码 + */ + private String attrCode; + /** + * 属性名称 + */ + private String attrName; + /** + * 能源品种Id + */ + private Long energyVarietyId; + /** + * 能源标签 + */ + private String tag; + /** + * 能源价格(元) + */ + private BigDecimal price; + /** + * 序号 + */ + private Integer serialNumber; + /** + * 适用时间范围起始日期 + */ + private Date beginTime; + /** + * 适用时间范围截止日期 + */ + private Date endTime; + /** + * 计量单位 + */ + private String unit; + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceInfo.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceInfo.java new file mode 100644 index 0000000..7ab759b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyPriceInfo.java @@ -0,0 +1,18 @@ +package com.thing.carbon.config.entity; + + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CarbonEnergyPriceInfo { + + private String baseCode; + private String baseName; + private String baseUint; + private String attrCode; + private String attrName; + private BigDecimal price; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyVarietyEntity.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyVarietyEntity.java new file mode 100644 index 0000000..0ba3b81 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonEnergyVarietyEntity.java @@ -0,0 +1,45 @@ +package com.thing.carbon.config.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 能源品种基本信息 + * + * @author xc + * @since 3.0 2023-07-31 + */ +@EqualsAndHashCode(callSuper=false) +@Data +@Table("iot_carbon_energy_variety") +public class CarbonEnergyVarietyEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 能源品种名称 +电,水,天然气,蒸汽... + */ + private String name; + /** + * 能源品种类型 +依靠当前系统定义,如电-A29... + */ + private String code; + /** + * 能源排放类别,中文描述如,电力,化石燃料,热力等 + */ + private String type; + /** + * 是否默认,0默认,1自定义,默认数据只有管理员可以操作 + */ + private String isDefault; + /** + * 备注 + */ + private String remarks; +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonPeakConfigEntity.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonPeakConfigEntity.java new file mode 100644 index 0000000..f0ec5d0 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonPeakConfigEntity.java @@ -0,0 +1,69 @@ +package com.thing.carbon.config.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 尖峰谷平配置 + * + * @author xc + * @since 3.0 2023-09-21 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("carbon_peak_config") +public class CarbonPeakConfigEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 能源品种id + */ + private Long evId; + + /** + * 年份(单选) + */ + private String year; + /** + * 月份(可多选) + */ + private String months; + /** + * 开始时间(0点至输入时间分钟值)例如01:00 = 60 + */ + private Long startTime; + /** + * 结束时间(0点至输入时间分钟值)例如02:00 = 120 + */ + private Long endTime; + + /** + * 开始时间(0点至输入时间分钟值)例如01:00 + */ + private String startTimeF; + /** + * 结束时间(0点至输入时间分钟值)例如02:00 + */ + private String endTimeF; + + /** + * 关联物属性编码 + */ + private String baseAttrCode; + /** + * 映射物属性编码 + */ + private String buildAttrCode; + /** + * 备注 + */ + private String remarks; + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonTsKvEntity.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonTsKvEntity.java new file mode 100644 index 0000000..c361b9d --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/entity/CarbonTsKvEntity.java @@ -0,0 +1,67 @@ +package com.thing.carbon.config.entity; + + +import com.mybatisflex.annotation.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 企业遥测数据缓存 + * + * @author ssy + * @since 3.0 2023-09-12 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper=false) +@Table("carbon_ts_kv") +public class CarbonTsKvEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 企业设备id + */ + private Long thingId; + /** + * 设备编码 + */ + private String thingCode; + /** + * 属性编码 + */ + private String attrKey; + /** + * 时间戳 + */ + private Long ts; + /** + * 遥测值 + */ + private String val; + /** + * 时间 + */ + private Date dayTime; + /** + * 创建时间 + */ + private Long createTime; + /** + * 更新时间 + */ + private Long updateTime; + /** + * 属性类型:dd, mm, yy + */ + private String attributeType; +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonEnergyVarietyExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonEnergyVarietyExcel.java new file mode 100644 index 0000000..7a909d9 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonEnergyVarietyExcel.java @@ -0,0 +1,52 @@ +package com.thing.carbon.config.excel;/* +package iot.thing.config.excel; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +*/ +/** + * 能源品种基本信息 + * + * @author xc + * @since 3.0 2023-07-31 + *//* + +@Data +@ContentRowHeight(20) +@HeadRowHeight(20) +@ColumnWidth(25) +public class CarbonEnergyVarietyExcel { + @ExcelProperty(value = "数据主键id", index = 0) + private Long id; + @ExcelProperty(value = "能源品种名称 +电,水,天然气,蒸汽...", index = 1) + private String name; + @ExcelProperty(value = "能源品种类型 +依靠当前系统定义,如电-A29...", index = 2) + private String code; + @ExcelProperty(value = "能源排放类别,中文描述 +如,直接排放,间接排放...", index = 3) + private String type; + @ExcelProperty(value = "是否默认,0默认,1自定义,默认数据只有管理员可以操作", index = 4) + private String isDefault; + @ExcelProperty(value = "备注", index = 5) + private String remarks; + @ExcelProperty(value = "企业编码", index = 6) + private Long tenantCode; + @ExcelProperty(value = "企业详情id", index = 7) + private Long companyId; + @ExcelProperty(value = "部门id", index = 8) + private Long deptId; + @ExcelProperty(value = "创建者id", index = 9) + private Long creator; + @ExcelProperty(value = "创建时间", index = 10) + private Long createDate; + @ExcelProperty(value = "更新者id", index = 11) + private Long updater; + @ExcelProperty(value = "更新时间", index = 12) + private Long updateDate; +}*/ diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictExcel.java new file mode 100644 index 0000000..5b51013 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictExcel.java @@ -0,0 +1,158 @@ +package com.thing.carbon.config.excel; + +import com.alibaba.fastjson.JSONArray; +import com.thing.carbon.config.dto.CarbonTimeLabelData; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/09/26 11:43 + * Description: 物的用量属性Excel对象 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class CarbonThingEnergyDictExcel { + + /** + * 物名称 + */ + @CustomExcel(name = "结构", width = 25, needMerge = true) + private String thingName; + + /** + * 能源品种 + */ + @CustomExcel(name = "能源品种", needMerge = true, orderNum = 1) + private String energyVarietyName; + + /** + * 属性名称 + */ + @CustomExcel(name = "类型", width = 18, orderNum = 2) + private String attrName; + + /** + * 属性单位 + */ + private String attrUnit; + + @CustomExcel(name = "总量", width = 12, orderNum = 3) + private BigDecimal totalValue; + + @CustomExcel(name = "价格", orderNum = 4) + private String price; + + @CustomExcel(name = "总价", width = 12, orderNum = 5) + private BigDecimal totalCalculatedValue; + + @CustomExcelCollection + private List dataList; + + public static List convertFromDto( + List sourceList, String timeType) { + return sourceList.stream() + .map( + source -> { + Map dataMap = source.getDataMap(); + List dataList = + JSONArray.parseArray( + JSONArray.toJSONString(dataMap.get("data")), + CarbonTimeLabelData.class); + dataList.forEach(data -> data.setTimeType(timeType)); + + // 只需展示基础数据 + if(timeType.equals("day")){ + dataList.removeIf(data -> !data.getLabel().equals("item")); + } + return new CarbonThingEnergyDictExcel() + .setThingName(source.getThingName()) + .setEnergyVarietyName(source.getEnergyVarietyName()) + .setAttrName( + source.getAttrName() + + (Objects.nonNull(source.getAttrUnit()) + ? "(" + source.getAttrUnit() + ")" + : "")) + .setAttrUnit(source.getAttrUnit()) + .setTotalValue((BigDecimal) dataMap.get("totalValue")) + .setPrice( + Objects.nonNull(dataMap.get("price")) + ? dataMap.get("price").toString() + : "--") + .setTotalCalculatedValue( + (BigDecimal) dataMap.get("totalCalculatedValue")) + .setDataList(dataList); + }) + .collect(Collectors.toList()); + } + + /** + * 现在needMerge注解是无效的,不得不自定义合并方法 + * + * @param dataList 数据列表 + * @return 合并区域 + */ + @SuppressWarnings("Duplicates") + public static List calculateMergeRegion(List dataList, String timeType) { + boolean isWeek = Objects.equals(timeType, "week"); + List mergeRegionList = new ArrayList<>(); + Map> thingCountMap = new HashMap<>(); + Map> thingEnergyCountMap = new HashMap<>(); + int beginRowOfThing = isWeek ? 1 : 0; + int beginRowOfEnergy = isWeek ? 1 : 0; + for (CarbonThingEnergyDictExcel currentData : dataList) { + if (!thingCountMap.containsKey(currentData.getThingName())) { + thingCountMap.put( + currentData.getThingName(), + MutablePair.of(++beginRowOfThing, beginRowOfThing)); + } else { + Pair thingCountPair = + thingCountMap.get(currentData.getThingName()); + thingCountPair.setValue(++beginRowOfThing); + } + + String energyKey = + currentData.getThingName() + "_" + currentData.getEnergyVarietyName(); + if (!thingEnergyCountMap.containsKey(energyKey)) { + thingEnergyCountMap.put( + energyKey, MutablePair.of(++beginRowOfEnergy, beginRowOfEnergy)); + } else { + Pair energyCountPair = thingEnergyCountMap.get(energyKey); + energyCountPair.setValue(++beginRowOfEnergy); + } + } + + thingCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 0, 0)); + }); + thingEnergyCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 1, 1)); + }); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictVerticalExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictVerticalExcel.java new file mode 100644 index 0000000..7b5d7ab --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/CarbonThingEnergyDictVerticalExcel.java @@ -0,0 +1,205 @@ +package com.thing.carbon.config.excel; + + +import com.alibaba.fastjson.JSONArray; +import com.thing.carbon.config.dto.CarbonTimeLabelData; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Author: SiYang + * Date: 2024/04/02 10:52 + * Description: 物的用量属性Excel对象(竖直样式) + */ +@Data +@NoArgsConstructor +@SuppressWarnings("unused") +public class CarbonThingEnergyDictVerticalExcel { + + private static final String VALUE_WITH_UNIT = "%s(%s)"; + + /** + * 数据值: 能源(品种)、总价、日期等 + */ + @CustomExcel(name = "结构", width = 25) + private String tag; + + @CustomExcelCollection + private List dataList; + + public CarbonThingEnergyDictVerticalExcel(String tag) { + this.tag = tag; + } + + public List getDataList() { + if (Objects.isNull(dataList)) { + dataList = new ArrayList<>(); + } + return this.dataList; + } + + @Data + @Accessors(chain = true) + public static class CellData { + @CustomExcel(keyGenerator = "generateKey", columnNameGenerator = "generateTitle", width = 12) + private String value; + + private String thingCode; + + private String thingName; + + private String attrCode; + + private String attrName; + + private String energyName; + + private String unit; + + public static CellData convert(ThingEnergyDictData source) { + return new CellData() + .setThingCode(source.getThingCode()) + .setThingName(source.getThingName()) + .setAttrCode(source.getAttrCode()) + .setAttrName(source.getAttrName()) + .setEnergyName(source.getEnergyVarietyName()) + .setUnit(source.getAttrUnit()); + } + + public String generateKey() { + return thingCode + attrCode; + } + + public String generateTitle() { + return thingName; + } + + } + + public static List convertFromDto(List sourceList, String timeType) { + List res = new ArrayList<>(); + Map excelMap = new TreeMap<>(Comparator.reverseOrder()); + CarbonThingEnergyDictVerticalExcel energyInfo = new CarbonThingEnergyDictVerticalExcel("能源"); + CarbonThingEnergyDictVerticalExcel attrInfo = new CarbonThingEnergyDictVerticalExcel("能源"); + CarbonThingEnergyDictVerticalExcel priceInfo = new CarbonThingEnergyDictVerticalExcel("单价"); + CarbonThingEnergyDictVerticalExcel totalPriceInfo = new CarbonThingEnergyDictVerticalExcel("总价"); + + sourceList.forEach(s -> { + Map dataMap = s.getDataMap(); + List dataList = JSONArray.parseArray(JSONArray.toJSONString(dataMap.get("data")), CarbonTimeLabelData.class); + if(!"any".equals(timeType)){ + dataList.forEach( + labelData -> { + // 只需展示基础数据 + if (!Objects.equals(labelData.getLabel(), "item")) { + return; + } + if (labelData.getTs()>System.currentTimeMillis() ) { + return; + } + String timeTag = getTimeTag(labelData, timeType); + excelMap.computeIfAbsent( + timeTag, + k -> new CarbonThingEnergyDictVerticalExcel(timeTag)) + .getDataList() + .add(CellData.convert(s).setValue(labelData.getExportValue())); + }); + } + + energyInfo.getDataList().add(CellData.convert(s).setValue(String.format(VALUE_WITH_UNIT, s.getEnergyVarietyName(), s.getAttrUnit()))); + attrInfo.getDataList().add(CellData.convert(s).setValue(s.getAttrName())); + priceInfo.getDataList().add(CellData.convert(s).setValue(dataMap.get("price").toString())); + String val = dataMap.get("totalCalculatedValue").toString(); + if(StringUtils.isNotEmpty(val)){ + totalPriceInfo.getDataList().add(CellData.convert(s).setValue(val)); + }else { + totalPriceInfo.getDataList().add(CellData.convert(s).setValue("--")); + } + }); + + res.add(energyInfo); + res.add(attrInfo); + res.add(priceInfo); + res.add(totalPriceInfo); + excelMap.forEach((k,v) -> res.add(v)); + + return res; + } + + private static String getTimeTag(CarbonTimeLabelData labelData, String timeType) { + LocalDateTime time = DateTimeUtils.parseDateTime(labelData.getTs()); + switch (timeType) { + case "day": + case "any": + return time.format(DateTimeUtils.DATE_TIME_PATTERN); + case "week": + case "month": + return time.format(DateTimeUtils.DATE_PATTERN); + case "year": + return time.format(DateTimeUtils.YEAR_MONTH_PATTERN01); + default: + return ""; + } + } + + public static List calculateMergeRegion(List dataList) { + if (CollectionUtils.isEmpty(dataList)) { + return Collections.emptyList(); + } + List mergeRegionList = new ArrayList<>(); + CarbonThingEnergyDictVerticalExcel firstRowData = dataList.get(0); + List firstRowDataList = firstRowData.getDataList(); + Map> thingColRangeMap = new HashMap<>(); + Map> energyColRangeMap = new HashMap<>(); + AtomicInteger colOfThing = new AtomicInteger(0); + AtomicInteger colOfEnergy = new AtomicInteger(0); + firstRowDataList.forEach( + cellData -> { + thingColRangeMap + .computeIfAbsent( + cellData.getThingCode(), + k -> MutablePair.of(colOfThing.get() + 1, colOfThing.get())) + .setValue(colOfThing.incrementAndGet()); + energyColRangeMap + .computeIfAbsent( + cellData.getThingCode() + "_" + cellData.getEnergyName(), + k -> MutablePair.of(colOfEnergy.get() + 1, colOfEnergy.get())) + .setValue(colOfEnergy.incrementAndGet()); + }); + + thingColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(0, 0, pair.getLeft(), pair.getRight())); + }); + + energyColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(1, 1, pair.getLeft(), pair.getRight())); + }); + + mergeRegionList.add(new CellRangeAddress(1, 2, 0, 0)); + + return mergeRegionList; + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/MeterReadExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/MeterReadExcel.java new file mode 100644 index 0000000..db6c42a --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/excel/MeterReadExcel.java @@ -0,0 +1,102 @@ +package com.thing.carbon.config.excel; + +import com.thing.carbon.config.dto.MeterReadRespDTO; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.utils.DateTimeUtils; +import io.micrometer.common.util.StringUtils; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/10/20 13:21 + * Description: 抄表数据excel对象 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class MeterReadExcel { + + @CustomExcel(name = "计量点", width = 25) + private String thingName; + + @CustomExcel(name = "能源", width = 15, orderNum = 1) + private String energyVarietyName; + + @CustomExcelCollection + private List data; + + public static List convert(List sources) { + if (CollectionUtils.isEmpty(sources)) { + return Collections.emptyList(); + } + return sources.stream() + .map( + source -> { + Map dataMap = source.getData(); + List dataList = new ArrayList<>(); + dataMap.forEach( + (k, v) -> + dataList.add( + new TsVal() + .setTs( + DateTimeUtils.parseDateTime( + Long.parseLong(k))) + .setVal(v))); + return new MeterReadExcel() + .setThingName(source.getThingName() + source.getAttrCode()) + .setEnergyVarietyName( + source.getEnergyVarietyName() + + (StringUtils.isBlank(source.getUnit()) + ? "" + : "(" + source.getUnit() + ")")) + .setData(dataList); + }) + .collect(Collectors.toList()); + } + + @Data + @Accessors(chain = true) + @NoArgsConstructor + @AllArgsConstructor + public static class TsVal { + + private static final String COL_NAME_FORMAT = "%s-%s"; + + private LocalDateTime ts; + + @CustomExcel(columnNameGenerator = "colNameGenerator", width = 12, orderGenerator = "colOrderGenerator") + private String val; + + @SuppressWarnings("unused") + private String colNameGenerator() { + return String.format( + COL_NAME_FORMAT, + addZeroPrefix(ts.getMonth().getValue()), + addZeroPrefix(ts.getDayOfMonth())); + } + + @SuppressWarnings("unused") + private int colOrderGenerator(){ + int orderNum = 2; + orderNum += ts.getDayOfMonth(); + return orderNum; + } + + private String addZeroPrefix(Integer num) { + return (num < 10 && num > 0) ? "0" + num : num.toString(); + } + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyDictRelationMapper.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyDictRelationMapper.java new file mode 100644 index 0000000..d3c568c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyDictRelationMapper.java @@ -0,0 +1,22 @@ +package com.thing.carbon.config.mapper; + + +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 能源属性关系 +* +* @author ssy +* @since 3.0 2023-09-19 +*/ +@Mapper +public interface CarbonEnergyDictRelationMapper extends PowerBaseMapper { + + void insertBatchs(@Param("list") List list, @Param("currentTime") long currentTime); + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyPriceMapper.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyPriceMapper.java new file mode 100644 index 0000000..1e7a133 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyPriceMapper.java @@ -0,0 +1,25 @@ +package com.thing.carbon.config.mapper; + + +import com.thing.carbon.config.entity.CarbonEnergyPriceEntity; +import com.thing.carbon.config.entity.CarbonEnergyPriceInfo; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; + +/** +* 能源价格 +* +* @author ssy +* @since 3.0 2023-09-14 +*/ +@Mapper +public interface CarbonEnergyPriceMapper extends PowerBaseMapper { + + List selectAttrsInAllTime(@Param("tenantCode") Long tenantCode); + + List getPeakValleyPrice(String key, Long tenantCode, Date date); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyVarietyMapper.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyVarietyMapper.java new file mode 100644 index 0000000..9594116 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonEnergyVarietyMapper.java @@ -0,0 +1,17 @@ +package com.thing.carbon.config.mapper; + + +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** +* 能源品种基本信息 +* +* @author xc +* @since 3.0 2023-07-31 +*/ +@Mapper +public interface CarbonEnergyVarietyMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonPeakConfigMapper.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonPeakConfigMapper.java new file mode 100644 index 0000000..fe2bb54 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonPeakConfigMapper.java @@ -0,0 +1,20 @@ +package com.thing.carbon.config.mapper; + +import com.thing.carbon.config.entity.CarbonPeakConfigEntity; +import com.thing.carbon.peakvalley.dto.EnergyReq; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 尖峰谷平配置 +* +* @author xc +* @since 3.0 2023-09-21 +*/ +@Mapper +public interface CarbonPeakConfigMapper extends PowerBaseMapper { + + List energy(Long tenantCode); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonTsKvMapper.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonTsKvMapper.java new file mode 100644 index 0000000..b689b7f --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/mapper/CarbonTsKvMapper.java @@ -0,0 +1,23 @@ +package com.thing.carbon.config.mapper; + + +import com.thing.carbon.config.entity.CarbonTsKvEntity; +import com.thing.common.orm.mapper.PowerBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 企业遥测数据缓存 +* +* @author ssy +* @since 3.0 2023-09-12 +*/ +@Mapper +public interface CarbonTsKvMapper extends PowerBaseMapper { + + void manualInsertBatch(@Param("list") List list, @Param("currentTime") long currentTime); + + void deleteByThingIdAndAttrCodeAndTs(@Param("thingId") Long thingId, @Param("attrKey") String attrKey, @Param("ts") Long ts); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyDictRelationService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyDictRelationService.java new file mode 100644 index 0000000..53c280b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyDictRelationService.java @@ -0,0 +1,35 @@ +package com.thing.carbon.config.service; + + + +import com.thing.carbon.config.dto.CarbonEnergyDictRelationDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonThingEnergyDictReqDTO; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 能源属性关系 + * + * @author ssy + * @since 3.0 2023-09-19 + */ +public interface CarbonEnergyDictRelationService extends IBaseService { + + void handleSave(CarbonEnergyDictRelationDTO dto); + + PageData handlePage(Map params); + + List getThingEnergyDictList(CarbonThingEnergyDictReqDTO params); + + List getByAttrAndVariety( + Collection attrCodes, Collection varietyIds, String attrType); + + List findEnergyVarietyByCodes(Collection attrCodes); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyPriceService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyPriceService.java new file mode 100644 index 0000000..53b5880 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyPriceService.java @@ -0,0 +1,48 @@ +package com.thing.carbon.config.service; + + +import com.thing.carbon.config.dto.CarbonEnergyPriceDTO; +import com.thing.carbon.config.dto.CarbonEnergyPriceReportReqDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.entity.CarbonEnergyPriceEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.tenant.dto.SysTenantDTO; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +/** + * 能源价格 + * + * @author ssy + * @since 3.0 2023-09-14 + */ +public interface CarbonEnergyPriceService extends IBaseService { + + List tenantList(); + + List listDTOs(Map params); + + void batchSave(CarbonEnergyPriceDTO[] dtoArr); + + void checkAndSave(CarbonEnergyPriceDTO dto); + + void checkAndUpdate(CarbonEnergyPriceDTO dto); + + void delTenant(Long tenantCode); + + List attrsInAllTime(); + + List energyPriceReport(CarbonEnergyPriceReportReqDTO request); + + void energyPriceReportExport(CarbonEnergyPriceReportReqDTO request, HttpServletResponse response); + + void energyPriceReportExportVertical(CarbonEnergyPriceReportReqDTO request, HttpServletResponse response); + + PageData handlePage(Map params); + + List thingEnergyVarieties(List thingIds); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyVarietyService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyVarietyService.java new file mode 100644 index 0000000..32924da --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonEnergyVarietyService.java @@ -0,0 +1,31 @@ +package com.thing.carbon.config.service; + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import org.apache.ibatis.annotations.Param; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 能源品种基本信息 + * + * @author xc + * @since 3.0 2023-07-31 + */ +public interface CarbonEnergyVarietyService extends IBaseService { + + PageData pageCarbonEnergyVarietyDTO(Map params); + + Result saveCarbonEnergyVarietyDTO(CarbonEnergyVarietyDTO dto); + + List listCarbonEnergyVarietyDTO(Map params); + + List selectByIds(@Param("ids") Collection ids); + + CarbonEnergyVarietyEntity selectByCode(String key); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonPeakConfigService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonPeakConfigService.java new file mode 100644 index 0000000..e07d9cc --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonPeakConfigService.java @@ -0,0 +1,34 @@ +package com.thing.carbon.config.service; + +import com.thing.carbon.config.dto.CarbonPeakConfigDTO; +import com.thing.carbon.config.entity.CarbonPeakConfigEntity; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.tenant.dto.SysTenantDTO; + +import java.util.List; +import java.util.Map; + +/** + * 尖峰谷平配置 + * + * @author xc + * @since 3.0 2023-09-21 + */ +public interface CarbonPeakConfigService extends IBaseService { + + PageData pageCarbonPeakConfigDTO(Map params); + + List listCarbonPeakConfigDTO(Map params); + + void batchSave(CarbonPeakConfigDTO[] dtos); + + List tenantList(); + + void delTenant(Long tenantCode); + + Result saveCarbonPeakConfigDTO(CarbonPeakConfigDTO dto); + + Result updateCarbonPeakConfigDTO(CarbonPeakConfigDTO dto); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonTsKvService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonTsKvService.java new file mode 100644 index 0000000..dec8ed5 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/CarbonTsKvService.java @@ -0,0 +1,18 @@ +package com.thing.carbon.config.service; + + +import com.thing.carbon.config.entity.CarbonTsKvEntity; +import com.thing.common.orm.service.IBaseService; +import com.thing.publicorg.benchmarkingproject.dto.ThingSequentialInfoParamDTO; + +/** + * 企业遥测数据缓存 + * + * @author ssy + * @since 3.0 2023-09-12 + */ +public interface CarbonTsKvService extends IBaseService { + + void handleSave(ThingSequentialInfoParamDTO thingSequentialParamDTO); + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/MeterReadService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/MeterReadService.java new file mode 100644 index 0000000..d0f103b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/MeterReadService.java @@ -0,0 +1,20 @@ +package com.thing.carbon.config.service; + + +import com.thing.carbon.config.dto.MeterReadReqDTO; +import com.thing.carbon.config.dto.MeterReadRespDTO; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/10/19 16:14 + * Description: 抄表数据服务接口 + */ +public interface MeterReadService { + + List report(MeterReadReqDTO request); + + void export(MeterReadReqDTO request, HttpServletResponse response); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/TransformerBizConfigService.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/TransformerBizConfigService.java new file mode 100644 index 0000000..af14f76 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/TransformerBizConfigService.java @@ -0,0 +1,30 @@ +package com.thing.carbon.config.service; + +import com.thing.carbon.config.dto.TransformerBizConfigDTO; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author siyang + * @date 2024/7/8 15:43 + * @description + */ +public interface TransformerBizConfigService extends IBaseService { + PageData handlePage(Map params); + List handleList(Map params); + TransformerBizConfigDTO handleDetail(Long id); + + void handleSave(TransformerBizConfigDTO configDTO); + void handleBatchSave(List configDTOList); + + void handleUpdate(TransformerBizConfigDTO configDTO); + + void handleDelete(String[] ids); + + + TransformerBizConfigDTO getByThingId(Long thingId); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyDictRelationServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyDictRelationServiceImpl.java new file mode 100644 index 0000000..e4e8348 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyDictRelationServiceImpl.java @@ -0,0 +1,322 @@ +package com.thing.carbon.config.service.impl; + +import cn.hutool.core.map.MapUtil; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.dto.CarbonEnergyDictRelationDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonThingEnergyDictReqDTO; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.mapper.CarbonEnergyDictRelationMapper; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.orm.utils.IdGenerator; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 能源属性关系 + * + * @author ssy + * @since 3.0 2023-09-19 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CarbonEnergyDictRelationServiceImpl extends BaseServiceImpl implements CarbonEnergyDictRelationService { + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + private final ThingManageContextService thingManageContextService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + Long energyVarietyId = MapUtil.getLong(params, "energyVarietyId"); + String attrType = MapUtil.getStr(params, "attrType"); + wrapper.eq( CarbonEnergyDictRelationEntity::getEnergyVarietyId, energyVarietyId, ObjectUtils.isNotEmpty(energyVarietyId)) + .eq(CarbonEnergyDictRelationEntity::getAttrType, attrType,StringUtils.isNotBlank(attrType)); + return wrapper; + } + + /** + * 1. 基于属性编号和用量区间生成所有待入库的对象列表 + * 2. 查询字典表,给派生属性进行命名。 + * 2.1 若有多条数据,则基于group_id筛选 + * 2.2 若没有查到数据,则不生成名称 + */ + @Override + public void handleSave(CarbonEnergyDictRelationDTO dto) { + Map attrCodeTypeMap = generateAttrCodeAndTypeMap(dto.getAttrCodeList(), dto.getAttrTypeList()); + Map attrCodeNameMap = findAttrCodeNameMap(dto.getGroupName(), new ArrayList<>(attrCodeTypeMap.keySet())); + List entities = generateEnergyDictEntities(dto, attrCodeNameMap, attrCodeTypeMap); + // 批量新增或修改 + mapper.insertBatchs(entities, System.currentTimeMillis()); + } + + /** + * 基于基础属性编号与所选用量时间区间生成派生属性,返回基础属性+派生属性的Map + */ + private Map generateAttrCodeAndTypeMap(List baseAttrCodeList, List attrTypeList) { + Map attrCodeTypeMap = new HashMap<>(); + + baseAttrCodeList.forEach( + attrCode -> { + // 基础属性 + attrCodeTypeMap.put(attrCode, "base"); + // 派生属性 + attrTypeList.forEach( + attrType -> attrCodeTypeMap.put(attrCode + attrType, attrType)); + }); + + return attrCodeTypeMap; + } + + /** + * 先直接按属性code查询字典表: + * 1. 属性code唯一,直接取该属性的名称 + * 2. 属性code不唯一,则按照groupId来进行筛选,若筛选过后没有属性值,则取第一个属性的名称 + * 3. 属性code不存在,则不存储该名称 + * + * @param attrCodeList 包含基础属性与派生属性,基础属性默认在数据库中必然存在,否则不会有该参数传入 + */ + private Map findAttrCodeNameMap(String groupName, List attrCodeList) { + Map attrCodeNameMap = new HashMap<>(); + // 基于属性编号查询所有属性数据 + List dictList = thingManageContextService.findDictByGroupNameAndCode(groupName, attrCodeList).orElseGet(Collections::emptyList); + + // 按照属性code分组 + Map> dictInfoMap = + dictList.stream() + .collect( + Collectors.groupingBy( + IotThingDictDTO::getCode, + Collectors.toMap( + IotThingDictDTO::getGroupName, + IotThingDictDTO::getName, + (v1, v2) -> v1))); + // 遍历属性Code, 从dictInfoMap中挑选合适的属性名称 + attrCodeList.forEach( + attrCode -> { + Map groupDictNameMap = dictInfoMap.get(attrCode); + if (Objects.isNull(groupDictNameMap) || groupDictNameMap.isEmpty()) { + attrCodeNameMap.put(attrCode, ""); + } else { + String groupAttrName = groupDictNameMap.get(groupName); + if (StringUtils.isNotBlank(groupAttrName)) { + attrCodeNameMap.put(attrCode, groupAttrName); + } else { + attrCodeNameMap.put( + attrCode, new ArrayList<>(groupDictNameMap.values()).get(0)); + } + } + }); + + return attrCodeNameMap; + } + + /** + * 生成数据实体 + * + * @param dto 请求参数 + * @param attrCodeNameMap 属性code与查询到的属性名称的map + * @param attrCodeTypeMap 属性code与其属性类型的map + * @return 待入库的entity列表 + */ + private List generateEnergyDictEntities( + CarbonEnergyDictRelationDTO dto, + Map attrCodeNameMap, + Map attrCodeTypeMap) { + + UserDetail user = SecurityUser.getUser(); + + List entities = new ArrayList<>(); + attrCodeNameMap.forEach( + (attrCode, attrName) -> + entities.add( + new CarbonEnergyDictRelationEntity() + .setId(IdGenerator.nextId()) + .setEnergyVarietyId(dto.getEnergyVarietyId()) + //.setGroupId(dto.getGroupId()) + .setGroupName(dto.getGroupName()) + .setAttrCode(attrCode) + .setAttrName(attrName) + .setAttrType(attrCodeTypeMap.get(attrCode)) + .setTenantCode(user.getTenantCode()) + .setCompanyId(user.getCompanyId()) + .setDeptId(user.getDeptId()) + .setCreator(user.getId()))); + return entities; + } + + + /** + * 多查询一次能源品种表,将相关属性放到分页数据中 + */ + @Override + public PageData handlePage(Map params) { + Page pages = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + PageData page = getPageData(pages, CarbonEnergyDictRelationDTO.class); + + List list = page.getList(); + Set energyVarietyIdSet = list.stream().map(CarbonEnergyDictRelationDTO::getEnergyVarietyId).collect(Collectors.toSet()); + if (energyVarietyIdSet.isEmpty()) { + return page; + } + List energyVarietyEntities = carbonEnergyVarietyService.selectByIds(new ArrayList<>(energyVarietyIdSet)); + Map energyVarietyMap = energyVarietyEntities.stream().collect(Collectors.toMap(CarbonEnergyVarietyEntity::getId, Function.identity())); + for (CarbonEnergyDictRelationDTO energyDictRelation : list) { + Long energyVarietyId = energyDictRelation.getEnergyVarietyId(); + CarbonEnergyVarietyEntity energyVariety = energyVarietyMap.get(energyVarietyId); + energyDictRelation.setEnergyVarietyName(energyVariety.getName()); + energyDictRelation.setEnergyVarietyCode(energyVariety.getCode()); + } + return page; + } + + /** + * 查询物的用量属性 + * + * @param params 参数 + * @return 物的用量属性列表 + */ + @Override + public List getThingEnergyDictList(CarbonThingEnergyDictReqDTO params) { + // 物与属性 + + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(params.getThingIds(), null); + + if(optionalList.isEmpty()){ + return Collections.emptyList(); + } + + // 物的用量属性 + Set dictCodeSet = + optionalList.get().stream() + .map(IotThingDictRelationParamDTO::getCode) + .collect(Collectors.toSet()); + + List energyDictList = + mapper.selectListByQuery(QueryWrapper.create().in(CarbonEnergyDictRelationEntity::getAttrCode, dictCodeSet) + .in(CarbonEnergyDictRelationEntity::getAttrType, + params.getAttrTypes(),!CollectionUtils.isEmpty(params.getAttrTypes()))); + + Map attrCodeEnergyDictMap = + energyDictList.stream() + .collect( + Collectors.toMap( + CarbonEnergyDictRelationEntity::getAttrCode, + Function.identity())); + + // 属性对应的能源品种信息 + List energyVarietyIds = + energyDictList.stream() + .map(CarbonEnergyDictRelationEntity::getEnergyVarietyId) + .distinct() + .collect(Collectors.toList()); + + List energyVarietyEntities = + carbonEnergyVarietyService.selectByIds( + CollectionUtils.isEmpty(params.getEnergyVarietyIds()) + ? energyVarietyIds + : params.getEnergyVarietyIds()); + + Map energyVarietyMap = + energyVarietyEntities.stream() + .collect( + Collectors.toMap( + CarbonEnergyVarietyEntity::getId, Function.identity())); + + // 数据组装 + List resultList = new ArrayList<>(); + for (IotThingDictRelationParamDTO thingDict : optionalList.get()) { + // 过滤掉非用能属性 + if (!attrCodeEnergyDictMap.containsKey(thingDict.getCode())) { + continue; + } + CarbonEnergyDictRelationEntity energyDict = attrCodeEnergyDictMap.get(thingDict.getCode()); + + // 过滤掉不满足参数要求的用能属性 + if (!energyVarietyMap.containsKey(energyDict.getEnergyVarietyId())) { + continue; + } + CarbonEnergyVarietyEntity energyVariety = energyVarietyMap.get(energyDict.getEnergyVarietyId()); + Optional optional = thingManageContextService.findEntityById(thingDict.getEntityId()); + if(optional.isEmpty()){ + continue; + } + + resultList.add( + new ThingEnergyDictData() + .setThingId(thingDict.getEntityId()) + .setThingCode(optional.get().getCode()) + .setThingName(optional.get().getName()) + .setAttrUnit(thingDict.getUnit()) + .setAttrCode(energyDict.getAttrCode()) + .setAttrName(energyDict.getAttrName()) + .setAttrType(energyDict.getAttrType()) + .setEnergyVarietyId(energyVariety.getId()) + .setEnergyVarietyName(energyVariety.getName()) + .setEnergyVarietyCode(energyVariety.getCode()) + ); + } + return resultList; + } + + @Override + public List getByAttrAndVariety( + Collection attrCodes, Collection varietyIds, String attrType) { + return mapper.selectListByQuery( + QueryWrapper.create() + .in( + CarbonEnergyDictRelationEntity::getAttrCode, + attrCodes, !CollectionUtils.isEmpty(attrCodes)) + .in( + CarbonEnergyDictRelationEntity::getEnergyVarietyId, + varietyIds, + !CollectionUtils.isEmpty(varietyIds)) + .eq( + CarbonEnergyDictRelationEntity::getAttrType, + attrType, + Objects.nonNull(attrType))); + } + + @Override + public List findEnergyVarietyByCodes(Collection attrCodes) { + List entities = + mapper.selectListByQuery( + QueryWrapper.create() + .in(CarbonEnergyDictRelationEntity::getAttrCode, attrCodes)); + Set varietyIds = + entities.stream() + .map(CarbonEnergyDictRelationEntity::getEnergyVarietyId) + .collect(Collectors.toSet()); + List energyVarietyEntities = + carbonEnergyVarietyService.selectByIds(varietyIds); + return ConvertUtils.sourceToTarget(energyVarietyEntities, CarbonEnergyVarietyDTO.class); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyPriceServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyPriceServiceImpl.java new file mode 100644 index 0000000..3e184d6 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyPriceServiceImpl.java @@ -0,0 +1,934 @@ +package com.thing.carbon.config.service.impl; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSONArray; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.dto.*; +import com.thing.carbon.config.entity.CarbonEnergyPriceEntity; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.excel.CarbonThingEnergyDictExcel; +import com.thing.carbon.config.excel.CarbonThingEnergyDictVerticalExcel; +import com.thing.carbon.config.mapper.CarbonEnergyPriceMapper; +import com.thing.carbon.config.service.CarbonEnergyPriceService; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.utils.export.ExcelEntityGenerator; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantGroupService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.temporal.WeekFields; +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.thing.carbon.config.entity.table.CarbonEnergyPriceEntityTableDef.CARBON_ENERGY_PRICE_ENTITY; + +/** + * 能源价格 + * + * @author ssy + * @since 3.0 2023-09-14 + */ +@Service +@RequiredArgsConstructor +@Slf4j +public class CarbonEnergyPriceServiceImpl extends BaseServiceImpl implements CarbonEnergyPriceService { + + private final SysTenantMapper sysTenantDao; + private final SysTenantGroupService sysTenantGroupService; + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + private final ThingManageContextService thingManageContextService; + + private final TsKvService tsKvService; + private static final int THREAD_POOL_SIZE = 5; + private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE); + + @Override + public QueryWrapper getWrapper(Map params) { + Long tenantCode = MapUtil.getLong(params, "tenantCode"); + String attrCode = MapUtil.getStr(params, "attrCode"); + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper + .eq(CarbonEnergyPriceEntity::getTenantCode, ObjectUtils.isNotEmpty(tenantCode) ? tenantCode : 1001) + .eq( CarbonEnergyPriceEntity::getAttrCode, attrCode,StringUtils.isNotBlank(attrCode)); + + if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) { + wrapper.le(CarbonEnergyPriceEntity::getBeginTime, endTime) + .ge(CarbonEnergyPriceEntity::getEndTime, beginTime); + } + + return wrapper; + } + + @Override + public PageData handlePage(Map params) { + PageData page = getPageData(params, CarbonEnergyPriceDTO.class); + List list = page.getList(); + setEnergyVarietyInfo(list); + return page; + } + + @Override + public List thingEnergyVarieties(List thingIds) { + // 当前物的所有属性(codeList1) + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(thingIds, null); + List dictList = optionalList.orElseGet(Collections::emptyList); + Set attrCodes = + dictList.stream() + .map(d -> AttributeTypeEnum.removeSuffix(d.getCode())) + .collect(Collectors.toSet()); + // 当前租户的所有能源价格配置 + List energyPriceList = attrsInAllTime(); + // 每个能源品种对应一批属性(codeList2) + Map> energyAttrGroup = energyPriceList.stream().collect(Collectors.groupingBy(CarbonEnergyPriceDTO::getEnergyVarietyId)); + if (CollectionUtils.isEmpty(energyAttrGroup)) { + return null; + } + // 能源品种详情 + List energyVarietyEntities = carbonEnergyVarietyService.selectByIds(new ArrayList<>(energyAttrGroup.keySet())); + // 循环能源品种,判断在当前能源品种下的 codeList2 是否包含 codeList1 中的任意属性 + // 如果包含,则该能源品种为有效的能源品种,需要返回给前端 + return energyVarietyEntities.stream() + .filter( + ev -> { + List list = energyAttrGroup.get(ev.getId()); + Set vaildAttrSet = + list.stream() + .map(CarbonEnergyPriceDTO::getAttrCode) + .collect(Collectors.toSet()); + return vaildAttrSet.stream().anyMatch(attrCodes::contains); + }) + .map(ev -> ConvertUtils.sourceToTarget(ev, CarbonEnergyVarietyDTO.class)) + .collect(Collectors.toList()); + } + + private void setEnergyVarietyInfo(List list){ + Set energyVarietyIdSet = list.stream().map(CarbonEnergyPriceDTO::getEnergyVarietyId).collect(Collectors.toSet()); + if (energyVarietyIdSet.isEmpty()) { + return; + } + List energyVarietyEntities = carbonEnergyVarietyService.selectByIds(new ArrayList<>(energyVarietyIdSet)); + Map energyVarietyMap = energyVarietyEntities.stream().collect(Collectors.toMap(CarbonEnergyVarietyEntity::getId, Function.identity())); + for (CarbonEnergyPriceDTO energyPrice : list) { + Long energyVarietyId = energyPrice.getEnergyVarietyId(); + CarbonEnergyVarietyEntity energyVariety = energyVarietyMap.get(energyVarietyId); + energyPrice.setEnergyVarietyName(energyVariety.getName()); + energyPrice.setEnergyVarietyCode(energyVariety.getCode()); + } + } + + @Override + public void checkAndSave(CarbonEnergyPriceDTO dto) { + checkBusinessProperties(dto); + long currentTime = System.currentTimeMillis(); + dto.setCreateDate(currentTime); + dto.setUpdateDate(currentTime); + saveDto(dto); + } + + @Override + public void checkAndUpdate(CarbonEnergyPriceDTO dto) { + checkBusinessProperties(dto); + dto.setUpdateDate(System.currentTimeMillis()); + updateDto(dto); + } + + @Override + public void delTenant(Long tenantCode) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", tenantCode); + this.mapper.deleteByQuery(wrapper); + } + + @Override + public List attrsInAllTime() { + List entities = + mapper.selectAttrsInAllTime(UserContext.getTenantCode()); + return entities.stream() + .map( + e -> + new CarbonEnergyPriceDTO() + .setAttrName(e.getAttrName()) + .setAttrCode(e.getAttrCode()) + .setEnergyVarietyId(e.getEnergyVarietyId()) + .setPrice(e.getPrice()) + .setTag(e.getTag())) + .collect(Collectors.toList()); + } + + /** 校验当前能源价格对象的业务属性: 同一个属性,适用日期不允许存在交叉 */ + private void checkBusinessProperties(CarbonEnergyPriceDTO dto) { + Set ids = + mapper + .selectListByQuery( + QueryWrapper.create() + .eq(CarbonEnergyPriceEntity::getAttrCode, dto.getAttrCode()) + .eq(CarbonEnergyPriceEntity::getTenantCode, dto.getTenantCode(), Objects.nonNull(dto.getTenantCode())) + .and( + (CARBON_ENERGY_PRICE_ENTITY.BEGIN_TIME.le(dto.getBeginTime()) + .and(CARBON_ENERGY_PRICE_ENTITY.END_TIME.ge(dto.getBeginTime()))) + .or(CARBON_ENERGY_PRICE_ENTITY.BEGIN_TIME.le(dto.getEndTime()) + .and(CARBON_ENERGY_PRICE_ENTITY.END_TIME.ge(dto.getEndTime()))))) + .stream() + .map(CarbonEnergyPriceEntity::getId) + .collect(Collectors.toSet()); + + if (Objects.isNull(dto.getId()) && !ids.isEmpty()) { + throw new SysException("适用日期存在交叉,请修改"); + } + + // 存在id,则说明是更新操作,判断除了自身之外是否还有其他 + if (Objects.nonNull(dto.getId())) { + int otherEnergyPriceCount = + ids.stream() + .filter(id -> !Objects.equals(id, dto.getId())) + .collect(Collectors.toSet()) + .size(); + if (otherEnergyPriceCount > 0) { + throw new SysException("适用日期存在交叉,请修改"); + } + } + } + + @Override + @SuppressWarnings("Duplicates") + public List tenantList() { + Map params = new HashMap<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + List tenantCodeList; + List resultTenantCodeList = new ArrayList<>(); + + if (!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + tenantCodeList = sysTenantGroupService.getChildren(tenantCode); + tenantCodeList.add(tenantCode); + tenantCodeList.add(1001L); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("tenant_code", tenantCodeList).select("tenant_code"); + this.mapper + .selectListByQuery(wrapper) + .forEach(temp -> resultTenantCodeList.add(temp.getTenantCode())); + } else { + this.mapper + .selectListByQuery(QueryWrapper.create().select("tenant_code")) + .forEach(temp -> resultTenantCodeList.add(temp.getTenantCode())); + } + if (resultTenantCodeList.isEmpty()) { + resultTenantCodeList.add(-1L); + } + params.put("tenantCodeList", resultTenantCodeList); + return sysTenantDao.queryList(params).parallelStream() + .sorted(Comparator.comparingLong(SysTenantDTO::getTenantCode)) + .collect(Collectors.toList()); + } + + @Override + public List listDTOs(Map params) { + Long tenantCode = + Optional.ofNullable(MapUtils.getLong(params, "tenantCode")) + .orElse(UserContext.getTenantCode()); + List dtoList = listAs(params,CarbonEnergyPriceDTO.class); + if (CollectionUtils.isEmpty(dtoList)) { + params.put("tenantCode", "1001"); + dtoList = listAs(params,CarbonEnergyPriceDTO.class); + dtoList.forEach( + temp -> { + long currentTime = System.currentTimeMillis(); + temp.setId(null); + temp.setTenantCode(tenantCode); + temp.setCompanyId(tenantCode); + temp.setCreator(SecurityUser.getUserId()); + temp.setCreateDate(currentTime); + temp.setUpdateDate(currentTime); + }); + this.batchSave(dtoList.toArray(CarbonEnergyPriceDTO[]::new)); + } + setEnergyVarietyInfo(dtoList); + return dtoList; + } + + @Override + public void batchSave(CarbonEnergyPriceDTO[] dtoArr) { + for (CarbonEnergyPriceDTO dto : dtoArr) { + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + this.saveDto(dto); + } + } + + /** + * 特殊逻辑说明: + * 1. 在指定查询时间内,如果存在多个能源价格,则数据项采用实际价格,而单价返回最新的价格(因为前端展示的单价只有一个) + * 2. 在指定查询时间内,如果能源价格并不覆盖到所有的区间,那么没被覆盖的区间不进行计算,返回 -- 或者 null + * + * @param request 查询参数 + * @return 报表数据 + */ + @Override + public List energyPriceReport(CarbonEnergyPriceReportReqDTO request) { + // 查询物的用能属性 + CarbonThingEnergyDictReqDTO energyDictRequest = + new CarbonThingEnergyDictReqDTO() + .setThingIds(request.getThingIds().stream().map(Long::parseLong).collect(Collectors.toList())) + .setEnergyVarietyIds(request.getEnergyVarietyIds().stream().map(Long::parseLong).collect(Collectors.toList())) + .setAttrTypes(Collections.singletonList(request.getAttrType())); + Long tenantCode = UserContext.getTenantCode(); + if (CollectionUtils.isEmpty(request.getEnergyVarietyIds())) { + return Collections.emptyList(); + } + List thingEnergyDictList = getEnergyDict(energyDictRequest, tenantCode); + if (thingEnergyDictList.isEmpty()) { + return Collections.emptyList(); + } + + Map> thingDataGroup = thingEnergyDictList.stream().collect(Collectors.groupingBy(ThingEnergyDictData::getThingId)); + + // 按照单个物的粒度并发查询和处理报表数据 + List>> futures = new ArrayList<>(); + thingDataGroup.forEach( + (thingId, dataList) -> { + Future> future = + EXECUTOR.submit( + () -> handleEnergyPriceReport(request, dataList, tenantCode)); + futures.add(future); + }); + List result = new ArrayList<>(); + for (Future> future : futures) { + try { + result.addAll(future.get()); + } catch (Exception ignore) { + } + } + return result; + } + + /** + * 能源成本报表导出 + * + * @param request 请求 + * @param response 响应 + */ + @Override + public void energyPriceReportExport(CarbonEnergyPriceReportReqDTO request, HttpServletResponse response) { + // 报表原始数据 + List originalDataList = energyPriceReport(request); + + // 数据类型转换 + List excelDataList = CarbonThingEnergyDictExcel.convertFromDto(originalDataList, request.getTimeType()); + + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + + // 计算合并区域 + List regions = CarbonThingEnergyDictExcel.calculateMergeRegion(excelDataList, request.getTimeType()); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "能源成本报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + + // 导出 + ExcelUtils.downLoadExcel("能源成本报表.xls", response, workbook); + } + + @Override + public void energyPriceReportExportVertical(CarbonEnergyPriceReportReqDTO request, HttpServletResponse response) { + // 报表原始数据 + List originalDataList = energyPriceReport(request); + + // 数据类型转换 + List excelDataList = CarbonThingEnergyDictVerticalExcel.convertFromDto(originalDataList, request.getTimeType()); + + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + + // 计算合并区域 + List regions = CarbonThingEnergyDictVerticalExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "能源成本报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + + // 导出 + ExcelUtils.downLoadExcel("能源成本报表.xls", response, workbook); + } + + /** + * 通过能源价格设置去查询物的用量属性 + * 1. 查询【物】的所有属性 + * 2. 【属性】匹配能源价格设置,获得【能源】信息 + * 3. 组装数据结构 + * + * @param request 请求参数 + * @param tenantCode 租户 + * @return 物的用量属性 + */ + private List getEnergyDict(CarbonThingEnergyDictReqDTO request, Long tenantCode){ + List resultList = new ArrayList<>(); + // 查询物的所有属性 + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(request.getThingIds(), null); + if(optionalList.isEmpty()){ + return Collections.emptyList(); + } + // 查询所有指定能源品种的能源价格实体列表 + List energyPriceEntities = + mapper.selectListByQuery( + QueryWrapper.create() + .eq(CarbonEnergyPriceEntity::getTenantCode, tenantCode) + .in(CarbonEnergyPriceEntity::getEnergyVarietyId, request.getEnergyVarietyIds())); + // 转成属性能源map + Map attrVarietyMap = + energyPriceEntities.stream() + .collect( + Collectors.toMap( + e -> e.getAttrCode() + request.getAttrTypes().get(0), + CarbonEnergyPriceEntity::getEnergyVarietyId, + (v1, v2) -> v1)); + + // 建立属性与属性标签的映射 + Map energyDictTagMap = + energyPriceEntities.stream() + .collect( + Collectors.toMap( + CarbonEnergyPriceEntity::getAttrCode, + CarbonEnergyPriceEntity::getTag, + (v1, v2) -> v1)); + + // 查询所有能源品种并转成map + List energyVarietyEntities = + carbonEnergyVarietyService.selectByIds(request.getEnergyVarietyIds()); + Map varietyMap = + energyVarietyEntities.stream() + .collect( + Collectors.toMap( + CarbonEnergyVarietyEntity::getId, Function.identity())); + + for (IotThingDictRelationParamDTO thingDict : optionalList.get()) { + String dictCode = thingDict.getCode(); + //能源价格统计和峰平谷尖统计一个企业 只可能会有一种情景,所以这边先手动去除一下 + if(dictCode.contains("_rush")||dictCode.contains("_peak")||dictCode.contains("_valley")||dictCode.contains("_normal")){ + continue; + } + if (!attrVarietyMap.containsKey(dictCode)) { + continue; + } + Long energyVarietyId = attrVarietyMap.get(dictCode); + if (!varietyMap.containsKey(energyVarietyId)) { + continue; + } + CarbonEnergyVarietyEntity energyVariety = varietyMap.get(energyVarietyId); + String noSuffixAttrCode = AttributeTypeEnum.removeSuffix(dictCode); + Optional> optional = thingManageContextService.findViewEntityAllByIds(Collections.singletonList(thingDict.getEntityId())); + resultList.add( + new ThingEnergyDictData() + .setThingId(thingDict.getEntityId()) + .setThingCode(optional.get().get(0).getEntityCode()) + .setThingName(optional.get().get(0).getEntityName()) + .setAttrUnit(thingDict.getUnit()) + .setAttrCode(dictCode) + .setAttrName(energyDictTagMap.get(noSuffixAttrCode)) + .setAttrType(getAttrType(dictCode)) + .setEnergyVarietyId(energyVariety.getId()) + .setEnergyVarietyName(energyVariety.getName()) + .setEnergyVarietyCode(energyVariety.getCode())); + } + return resultList; + } + + private String getAttrType(String attrCode) { + return attrCode.substring(attrCode.length() - 2); + } + + private List handleEnergyPriceReport( + CarbonEnergyPriceReportReqDTO request, + List thingEnergyDictList, + Long tenantCode) { + // 取第一个物的用能属性,拿到物的编号 + ThingEnergyDictData commonThingEnergyDict = thingEnergyDictList.get(0); + String thingCode = commonThingEnergyDict.getThingCode(); + // 取出所有物的用量属性的Code集合 + Set energyDictSet = thingEnergyDictList.stream().map(ThingEnergyDictData::getAttrCode).collect(Collectors.toSet()); + + // 检查与转化时间参数 + Pair timeRange = getTimeRange(request.getBeginTime(), request.getEndTime()); + + // 查询能源价格,只有在指定时间区间内维护了价格的属性才参与计算;如果是试算价格,则适用于全年 + List energyPriceList; + if(request.getIsVirtualPrice()){ + energyPriceList = generateEnergyPrice(request.getVirtualEnergyPriceMap()); + }else { + energyPriceList = findEnergyPrice(timeRange, tenantCode); + } + energyPriceList.forEach(e -> e.setAttrCode(e.getAttrCode() + request.getAttrType())); + // 能源价格按照用量属性分组 + Map> energyPriceMap = energyPriceList.stream().collect(Collectors.groupingBy(CarbonEnergyPriceEntity::getAttrCode)); + // 只有同时配置了用量属性和能源价格的属性才需要去查询数据,否则数据将没有意义 + energyDictSet.retainAll(energyPriceMap.keySet()); + if (CollectionUtils.isEmpty(energyDictSet)) { + return Collections.emptyList(); + } + // 查询物的用能属性对应的值 + List tsKvMapList = findThingAttrTsKv(thingCode, new ArrayList<>(energyDictSet), timeRange.getLeft(), timeRange.getRight()); + // 将查询到的数据转为标签数据集, 并按照属性分组 + Map> labelDataMap = convert(tsKvMapList).stream().collect(Collectors.groupingBy(CarbonTimeLabelData::getAttrCode)); + // 【核心逻辑】遍历物的用量属性,填充dataMap中的值 + assembleDataForThingEnergyDict(thingEnergyDictList, energyPriceMap, labelDataMap, timeRange, request.getTimeType()); + // 在上文操作中,有些无效数据被置空了,这里过滤一下; 此外,数据量全为0的也不需要展示 + List baseDataList = + thingEnergyDictList.stream() + .filter(e -> Objects.nonNull(e) && dataValueNotEmpty(e.getDataMap(), request.getTimeType())) + .collect(Collectors.toList()); + // 生成合计行数据 + List result = generateEnergyVarietyTotalList(baseDataList, request.getTimeType()); + // 排序 + result.sort(Comparator.comparing(ThingEnergyDictData::getThingName).thenComparing(ThingEnergyDictData::getEnergyVarietyName)); + return result; + } + + @SuppressWarnings("unchecked") + private boolean dataValueNotEmpty(Map dataMap, String timeType) { + List list = (List) dataMap.get("data"); + + Set validDataSet = list.stream() + .filter( + i -> + Objects.equals( + i.getLabel(), + Objects.equals("year", timeType) + ? "monthTotal" + : "item") + && Objects.nonNull(i.getCalculatedValue()) + ).collect(Collectors.toSet()); + boolean itemValueValidate = !validDataSet.isEmpty(); + return itemValueValidate; + } + + private List convert(List tsKvMapList) { + return tsKvMapList.parallelStream() + .map( + dataMap -> { + Long ts =dataMap.getTs(); + LocalDateTime time = DateTimeUtils.parseDateTime(ts); + return new CarbonTimeLabelData() + .setDateTime(time) + .setYear(time.getYear()) + .setMonth(time.getMonthValue()) + .setWeekOfYear(time.get(WeekFields.ISO.weekOfYear())) + .setDateOfMonth(time.getDayOfMonth()) + .setDateOfWeek(time.getDayOfWeek().getValue()) + .setHour(time.getHour()) + .setTs(time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) + .setAttrCode(dataMap.getAttrKey()) + .setValue(new BigDecimal(dataMap.getVal())) + .setLabel("item"); + }) + .collect(Collectors.toList()); + } + + private void assembleDataForThingEnergyDict( + List thingEnergyDictList, + Map> energyPriceMap, + Map> labelDataMap, + Pair timeRange, + String timeType) { + for (int i = 0; i < thingEnergyDictList.size(); i++) { + ThingEnergyDictData thingEnergyDict = thingEnergyDictList.get(i); + String attrCode = thingEnergyDict.getAttrCode(); + if (!energyPriceMap.containsKey(attrCode) || !labelDataMap.containsKey(attrCode)) { + thingEnergyDictList.set(i, null); + continue; + } + // 初始化数据map + Map dataMap = thingEnergyDict.initDataMap(); + // 当前属性在指定的查询时间范围内可能存在多个价格,按对应的价格进行计算 + List energyPriceEntities = energyPriceMap.get(attrCode); + // 当前属性的用量数据 + List timeLabelDataList = labelDataMap.get(attrCode); + Map timeDataMap = + timeLabelDataList.stream() + .collect( + Collectors.toMap( + item -> item.getDateTime().toString(), + Function.identity(), + (v1, v2) -> { + if (Objects.isNull(v1.getValue())) { + return v2; + } + if (Objects.isNull(v2.getValue())) { + return v1; + } + int res = v1.getValue().compareTo(v2.getValue()); + return res >= 0 ? v1 : v2; + })); + + // 基于timeRange和属性类别生成此范围内所有的空白数据,这是为了让前端展示没有数据的数据 + List dateTimeList = generateAllDateTime(timeRange, timeType); + List allTimeRangeDataList = generateEmptyData(dateTimeList, attrCode); + + // 标签数据存在时间顺序,保证最新的数据最后处理,保证dataMap中的price是最新的价格 + for (int j = 0; j < allTimeRangeDataList.size(); j++) { + CarbonTimeLabelData emptyData = allTimeRangeDataList.get(j); + if (!timeDataMap.containsKey(emptyData.getDateTime().toString())) { + continue; + } + CarbonTimeLabelData labelData = timeDataMap.get(emptyData.getDateTime().toString()); + allTimeRangeDataList.set(j, labelData); + LocalDateTime dateTime = labelData.getDateTime(); + // 能源价格已按beginTime排序,保证dataMap中的price是最新的价格 + for (CarbonEnergyPriceEntity energyPrice : energyPriceEntities) { + LocalDateTime beginTime = DateTimeUtils.parseDateTime(energyPrice.getBeginTime().getTime()); + LocalDateTime endTime = DateTimeUtils.parseDateTime(energyPrice.getEndTime().getTime()); + if (dateTime.isBefore(beginTime) || dateTime.isAfter(endTime)) { + continue; + } + BigDecimal calcValue = energyPrice.getPrice().multiply(labelData.getValue()).setScale(1, RoundingMode.HALF_UP); + labelData.setCalculatedValue(calcValue); + dataMap.put("price", energyPrice.getPrice()); + } + } + // 汇总数据,并组装到最后的数据map中 + aggregateAndAssemble(allTimeRangeDataList, dataMap, timeType); + } + } + + /** + * 按不同的维度聚合数据 + * + * @param timeLabelDataList 待汇聚的原始数据列表 + * @param dataMap 承载数据的map + */ + private void aggregateAndAssemble( + List timeLabelDataList, Map dataMap, String timeType) { + switch (timeType) { + case "week" -> putAggregationData(timeLabelDataList, "week"); + case "month", "year" -> putAggregationData(timeLabelDataList, "month"); + case "day" -> putAggregationData(timeLabelDataList, "day"); + } + // 年度数据作为统一的汇总值,无论哪个维度都需要计算 + putAggregationData(timeLabelDataList, "year"); + + // 目前不涉及到跨年聚合数据,因此无论按周、按月、按年汇总都可以使用按年汇总的数据。 + // 如果以后需求变更,那么在此处加个判断,按不同标签去计算即可 + List summaryDataList = + timeLabelDataList.stream() + .filter(item -> Objects.equals(item.getLabel(), "yearTotal")) + .toList(); + + BigDecimal totalValue = + summaryDataList.stream() + .map(CarbonTimeLabelData::getValue) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO) + .setScale(1, RoundingMode.HALF_UP); + + BigDecimal totalCalculatedValue = + summaryDataList.stream() + .map(CarbonTimeLabelData::getCalculatedValue) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + + dataMap.put("data", timeLabelDataList); + dataMap.put("totalValue", totalValue); + dataMap.put("totalCalculatedValue", totalCalculatedValue); + } + + /** + * 基于输入的数据与聚合维度,将聚合数据添加到原数据列表中 + * + * @param dataList 原数据列表 + * @param dim 聚合维度 + */ + private void putAggregationData( + List dataList, String dim) { + switch (dim) { + case "week" -> { + Map> weekDataMap = + dataList.stream() + .filter(item -> Objects.equals(item.getLabel(), "item")) + .collect(Collectors.groupingBy(CarbonTimeLabelData::getWeekOfYear)); + dataList.addAll(summary(weekDataMap, "weekTotal")); + } + case "month" -> { + Map> monthDataMap = + dataList.stream() + .filter(item -> Objects.equals(item.getLabel(), "item")) + .collect(Collectors.groupingBy(CarbonTimeLabelData::getMonth)); + dataList.addAll(summary(monthDataMap, "monthTotal")); + } + case "year" -> { + Map> yearDataMap = + dataList.stream() + .filter(item -> Objects.equals(item.getLabel(), "item")) + .collect(Collectors.groupingBy(CarbonTimeLabelData::getYear)); + dataList.addAll(summary(yearDataMap, "yearTotal")); + } + case "day" -> { + Map> dayDataMap = + dataList.stream() + .filter(item -> Objects.equals(item.getLabel(), "item")) + .collect(Collectors.groupingBy(CarbonTimeLabelData::getDateOfMonth)); + dataList.addAll(summary(dayDataMap, "dayTotal")); + } + case "any" -> { + } + // 任意时间不需要聚合 + default -> throw new SysException("not support for aggregation dim: " + dim); + } + } + + private List summary(Map> dataGroup, String label) { + List result = new ArrayList<>(); + dataGroup.forEach( + (group, list) -> { + CarbonTimeLabelData firstData = list.get(0); + CarbonTimeLabelData summaryData = firstData.copy(label); + switch (label) { + case "weekTotal" -> summaryData.setWeekOfYear(firstData.getWeekOfYear()); + case "monthTotal" -> summaryData.setMonth(firstData.getMonth()); + default -> { + } + } + result.add(list.stream().reduce(summaryData, CarbonTimeLabelData::add)); + }); + return result; + } + + /** + * 基于价格试算的入参,生成能源价格 + * + * @param energyPriceMap 自定义的用能属性价格 + * @return 虚拟的能源价格列表 + */ + private List generateEnergyPrice(Map energyPriceMap) { + List list = new ArrayList<>(); + energyPriceMap.forEach( + (attrCode, price) -> + list.add( + new CarbonEnergyPriceEntity() + .setAttrCode(attrCode) + .setPrice(price) + .setBeginTime(DateTimeUtils.getYearBeginDate()) + .setEndTime(new Date()))); + return list; + } + + private List findEnergyPrice(Pair timeRange, Long tenantCode) { + Date beginDate = DateTimeUtils.timestampToDate(timeRange.getLeft()); + Date endDate = DateTimeUtils.timestampToDate(timeRange.getRight()); + return mapper.selectListByQuery( + QueryWrapper.create() + .eq(CarbonEnergyPriceEntity::getTenantCode, tenantCode) + .le(CarbonEnergyPriceEntity::getBeginTime, endDate) + .ge(CarbonEnergyPriceEntity::getEndTime, beginDate) + .orderBy(CarbonEnergyPriceEntity::getBeginTime,false)); + } + + /** + * 基于基本数据列表生成能源品种的合计数据列表 + * + * @param baseDataList 基本数据列表 + * @param timeType 时间类型 + * @return 合计数据列表 + */ + @SuppressWarnings("Duplicates") + private List generateEnergyVarietyTotalList(List baseDataList, String timeType) { + Map> thingAttrGroup = baseDataList.stream().collect(Collectors.groupingBy(item -> item.getThingId() + "_" + item.getEnergyVarietyId())); + thingAttrGroup.forEach((thingEnergy, dataList) -> { + // 只有一条数据的话就不进行合计 + if (dataList.size() <= 1) { + return; + } + // 开始合计,先copy出来基本信息,初始化数据map + ThingEnergyDictData summaryData = dataList.get(0).copy(); + Map summaryDataMap = summaryData.initDataMap(); + + // 先计算总量和总价,比较显然 + List> dataMapList = dataList.stream().map(ThingEnergyDictData::getDataMap).toList(); + BigDecimal totalValue = dataMapList.stream().map(map -> (BigDecimal)map.get("totalValue")).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); + BigDecimal totalCalculatedValue = dataMapList.stream().map(map -> (BigDecimal)map.get("totalCalculatedValue")).filter(Objects::nonNull).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); + + // 把不同的物属性的数据列表进行合并,方便后续的统一分组计算 + List> diffAttrTimeLabelDataList = dataMapList.stream().map(map -> JSONArray.parseArray(JSONArray.toJSONString(map.get("data")), CarbonTimeLabelData.class)).toList(); + List mergeList = diffAttrTimeLabelDataList.stream().flatMap(Collection::parallelStream).collect(Collectors.toList()); + + // 设置信息 + summaryData.setAttrName("合计"); + summaryData.setAttrCode(null); + summaryDataMap.put("totalValue", totalValue); + summaryDataMap.put("totalCalculatedValue", totalCalculatedValue); + summaryDataMap.put("data", summaryTimeLabelData(mergeList, timeType)); + + baseDataList.add(summaryData); + }); + + return baseDataList; + } + + private List summaryTimeLabelData(List dataList, String timeType) { + List timeLabelData = new ArrayList<>(); + switch (timeType) { + case "week" -> { + Map> weekDataGroup = dataList.stream().collect(Collectors.groupingBy(item -> item.getLabel() + "_week_" + item.getWeekOfYear() + "_date_" + item.getDateOfWeek())); + weekDataGroup.forEach( + (timeLabel, timeLabelDataList) -> { + CarbonTimeLabelData commonInfo = timeLabelDataList.get(0); + timeLabelData.add( + commonBuilder(timeLabelDataList) + .setWeekOfYear(commonInfo.getWeekOfYear()) + .setDateOfWeek(commonInfo.getDateOfWeek()) + .setDateOfMonth(commonInfo.getDateOfMonth())); + }); + } + case "month" -> { + Map> monthDataGroup = dataList.stream().collect(Collectors.groupingBy(item -> item.getLabel() + "_month_" + item.getMonth() + "_date_" + item.getDateOfMonth())); + monthDataGroup.forEach( + (timeLabel, timeLabelDataList) -> { + CarbonTimeLabelData commonInfo = timeLabelDataList.get(0); + timeLabelData.add( + commonBuilder(timeLabelDataList) + .setDateOfMonth(commonInfo.getDateOfMonth())); + }); + } + case "year" -> { + Map> yearDataGroup = dataList.stream().collect(Collectors.groupingBy(item -> item.getLabel() + "_year_" + item.getYear() + "_month_" + item.getMonth())); + yearDataGroup.forEach( + (timeLabel, timeLabelDataList) -> + timeLabelData.add(commonBuilder(timeLabelDataList))); + } + case "any" -> { + } + default -> throw new SysException("not support for time type: " + timeType); + } + return timeLabelData; + } + + private CarbonTimeLabelData commonBuilder(List sourceList) { + CarbonTimeLabelData source = sourceList.get(0); + return new CarbonTimeLabelData() + .setYear(source.getYear()) + .setMonth(source.getMonth()) + .setLabel(source.getLabel()) + .setValue(sumValue(sourceList)) + .setCalculatedValue(sumCalculatedValue(sourceList)); + } + + private BigDecimal sumValue(List dataList) { + return dataList.stream() + .map(CarbonTimeLabelData::getValue) + .filter(Objects::nonNull) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + } + + private BigDecimal sumCalculatedValue(List dataList) { + return dataList.stream() + .map(CarbonTimeLabelData::getCalculatedValue) + .filter(Objects::nonNull) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + } + + private List findThingAttrTsKv( + String thingCode, List attrCodes, long startTs, long endTs) { + try { + return new ArrayList<>( + tsKvService.findTsKvByCodeAndAttrs(thingCode, attrCodes, startTs, endTs, null)); + } catch (Exception e) { + log.error("能源成本报表:查询ch异常:", e); + throw new SysException("能源成本报表数据查询异常,请联系管理员"); + } + } + + @SuppressWarnings("Duplicates") + private Pair getTimeRange(String beginTime, String endTime) { + Pair timeRange; + if (StringUtils.isBlank(beginTime) || StringUtils.isBlank(endTime)) { + timeRange = DateTimeUtils.getTimestampRangeForWeeks(3); + } else { + timeRange = + Pair.of( + DateTimeUtils.dateToTimestamp(beginTime), + DateTimeUtils.dateToTimestamp(endTime)); + } + return timeRange; + } + + private List generateAllDateTime(Pair timeRange, String timeType) { + List dateTimeList = new ArrayList<>(); + LocalDateTime begin = DateTimeUtils.parseDateTime(timeRange.getLeft()); + begin = begin.withHour(0).withMinute(0).withSecond(0).withNano(0); + LocalDateTime end = DateTimeUtils.parseDateTime(timeRange.getRight()); + while (begin.isBefore(end) || begin.isEqual(end)) { + dateTimeList.add(begin); + if("any".equals(timeType)){ + begin = begin.plusMinutes(15); + }else if ("day".equals(timeType)){ + begin = begin.plusHours(1); + }else { + begin = begin.plusDays(1); + } + } + return dateTimeList; + } + + private List generateEmptyData( + List dateTimeList, String attrCode) { + return dateTimeList.stream() + .map( + dateTime -> + new CarbonTimeLabelData() + .setDateTime(dateTime) + .setTs(dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) + .setYear(dateTime.getYear()) + .setMonth(dateTime.getMonthValue()) + .setWeekOfYear(dateTime.get(WeekFields.ISO.weekOfYear())) + .setDateOfMonth(dateTime.getDayOfMonth()) + .setDateOfWeek(dateTime.getDayOfWeek().getValue()) + .setAttrCode(attrCode) + .setLabel("item")) + .collect(Collectors.toList()); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyVarietyServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyVarietyServiceImpl.java new file mode 100644 index 0000000..5feab5d --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonEnergyVarietyServiceImpl.java @@ -0,0 +1,127 @@ +package com.thing.carbon.config.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryColumn; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.controller.CarbonEnergyVarietyController; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.mapper.CarbonEnergyVarietyMapper; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.util.TenantSubsetUtil; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.mybatisflex.core.query.QueryMethods.column; + +/** + * 能源品种基本信息 + * + * @author xc + * @since 3.0 2023-07-31 + */ +@Service +public class CarbonEnergyVarietyServiceImpl extends BaseServiceImpl implements CarbonEnergyVarietyService { + + @Autowired + TenantSubsetUtil tenantSubsetUtil; + + QueryColumn is_default = column("is_default"); + QueryColumn tenant_code = column("tenant_code"); + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String name = (String) params.get("name"); + wrapper.like( "name", name,StringUtils.isNotBlank(name)); + + + //切换租户/租户/企业登陆 + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + wrapper.and(is_default.eq("0").or(tenant_code.eq(currentTenantCode))); + return wrapper; + } + + + @Override + public PageData pageCarbonEnergyVarietyDTO(Map params) { + PageData data = getPageData(params, CarbonEnergyVarietyDTO.class); + + //数据创建者,以及超管,拥有编辑删除权限, + //默认数据不得编辑删除 + data.getList().forEach(CarbonEnergyVarietyController::isOperate); + + return data; + } + + @Override + public Result saveCarbonEnergyVarietyDTO(CarbonEnergyVarietyDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq( "name", dto.getName(),ObjectUtil.isNotEmpty(dto.getName())); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperTenant(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq("0").or(tenant_code.eq(currentTenantCode))); + } + if(this.mapper.selectCountByQuery(wrapper)>0){ + return new Result().error("能源品种已存在,请勿重复添加!"); + } + CarbonEnergyVarietyEntity entity =ConvertUtils.convertWithTypeAdapt(dto, CarbonEnergyVarietyEntity.class); + this.mapper.insert(entity); + return new Result().ok("添加成功!"); + } + + @Override + public List listCarbonEnergyVarietyDTO(Map params) { + Long tenantCode = null; + try { + tenantCode = Long.valueOf((String) params.get("tenantCode")); + } catch (NumberFormatException e) { + tenantCode=1001L; + } + QueryWrapper wrapper = new QueryWrapper(); + String type = MapUtils.getString(params,"type"); + //查询默认或本企业自己创建得能源品种集合 + if(ObjectUtil.isNotEmpty(type)){ + wrapper.eq("type","能源"); + } + wrapper.and(is_default.eq("0").or(tenant_code.eq(tenantCode))); + List entityList = this.mapper.selectListByQuery(wrapper); + return ConvertUtils.sourceToTarget(entityList, CarbonEnergyVarietyDTO.class); + } + + @Override + public List selectByIds(Collection ids) { + return mapper.selectListByQuery(QueryWrapper.create().in(CarbonEnergyVarietyEntity::getId, ids)); + } + + @Override + public CarbonEnergyVarietyEntity selectByCode(String key) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("code", key,ObjectUtil.isNotEmpty(key)); + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperTenant(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.and(is_default.eq("0").or(tenant_code.eq(currentTenantCode))); + } + return mapper.selectOneByQueryAs(wrapper,CarbonEnergyVarietyEntity.class); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonPeakConfigServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonPeakConfigServiceImpl.java new file mode 100644 index 0000000..b8451b5 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonPeakConfigServiceImpl.java @@ -0,0 +1,213 @@ +package com.thing.carbon.config.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.mapper.CarbonPeakConfigMapper; +import com.thing.carbon.config.dto.CarbonPeakConfigDTO; +import com.thing.carbon.config.entity.CarbonPeakConfigEntity; +import com.thing.carbon.config.service.CarbonPeakConfigService; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantGroupService; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 尖峰谷平配置 + * + * @author xc + * @since 3.0 2023-09-21 + */ +@Service +public class CarbonPeakConfigServiceImpl extends BaseServiceImpl implements CarbonPeakConfigService { + + @Autowired + SysTenantMapper sysTenantDao; + @Autowired + SysTenantGroupService sysTenantGroupService; + + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String tenantCode = MapUtils.getString(params,"tenantCode"); + + String year = MapUtils.getString(params,"year"); + String baseAttrCode = MapUtils.getString(params,"baseAttrCode"); + String buildAttrCode = MapUtils.getString(params,"buildAttrCode"); + + + if(StringUtils.isNotBlank(tenantCode)){ + wrapper.eq("tenant_code", Long.valueOf(tenantCode)); + }else { + wrapper.eq("tenant_code", 1001); + } + wrapper.eq("year",year,StringUtils.isNotBlank(year)); + wrapper.eq("base_attr_code",baseAttrCode,StringUtils.isNotBlank(baseAttrCode)); + wrapper.eq("build_attr_code",buildAttrCode,StringUtils.isNotBlank(buildAttrCode)); + return wrapper; + } + + + @Override + public PageData pageCarbonPeakConfigDTO(Map params) { + Page pages = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + return getPageData(pages, CarbonPeakConfigDTO.class); + } + + @Override + public List listCarbonPeakConfigDTO(Map params) { + Long tenantCode = MapUtils.getLong(params,"tenantCode"); + List dtoList = listAs(params,CarbonPeakConfigDTO.class); + if(ObjectUtil.isNull(dtoList)||dtoList.isEmpty()){ + params.put("tenantCode","1001"); + dtoList = listAs(params,CarbonPeakConfigDTO.class); + dtoList.forEach(temp->{ + temp.setId(null); + temp.setTenantCode(tenantCode); + temp.setCreator(SecurityUser.getUserId()); + temp.setCreateDate(System.currentTimeMillis()); + }); + if(CollectionUtil.isNotEmpty(dtoList)){ + mapper.insertBatch(ConvertUtils.sourceToTarget(dtoList,CarbonPeakConfigEntity.class)); + } + } + return dtoList; + } + + @Override + public void batchSave(CarbonPeakConfigDTO[] dtos) { + for (CarbonPeakConfigDTO dto : dtos) { + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + this.saveDto(dto); + } + } + + @Override + public List tenantList() { + Map params = new HashMap<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + List tenantCodeList; + List resultTenantCodeList= new ArrayList<>(); + + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + tenantCodeList=sysTenantGroupService.getChildren(tenantCode); + tenantCodeList.add(tenantCode); + tenantCodeList.add(1001L); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.in("tenant_code",tenantCodeList).select("tenant_code"); + this.mapper.selectListByQuery(wrapper).forEach(temp->{ + resultTenantCodeList.add(temp.getTenantCode()); + }); + }else { + this.mapper.selectListByQuery(QueryWrapper.create().select("tenant_code")).forEach(temp->{ + resultTenantCodeList.add(temp.getTenantCode()); + }); + } + if(resultTenantCodeList.isEmpty()){ + resultTenantCodeList.add(-1L); + } + params.put("tenantCodeList",resultTenantCodeList); + return sysTenantDao.queryList(params).parallelStream().sorted(Comparator.comparingLong(SysTenantDTO::getTenantCode)).collect(Collectors.toList()); + } + + @Override + public void delTenant(Long tenantCode) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code",tenantCode); + this.mapper.deleteByQuery(wrapper); + } + + @Override + public Result saveCarbonPeakConfigDTO(CarbonPeakConfigDTO dto) { + return checkAndInsertParam(dto,"IN"); + } + + + + @Override + public Result updateCarbonPeakConfigDTO(CarbonPeakConfigDTO dto) { + return checkAndInsertParam(dto,"UP"); + } + + + /** + * 校验修改数据 + * @param dto + * @param type + * @return + */ + public Result checkAndInsertParam(CarbonPeakConfigDTO dto,String type){ + if (dto.getEndTime() <= dto.getStartTime()) { + return new Result().ok("配置失败,开始时间不得小于结束时间!"); + } + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + + List carbonPeakConfigEntities = getPriceByDeptId(tenantCode); + List carbonPeakConfigEntityList = new ArrayList<>(); + if (CollectionUtil.isNotEmpty(carbonPeakConfigEntities)) { + if(type.equals("IN")){ + carbonPeakConfigEntityList = carbonPeakConfigEntities.stream().filter(item -> item.getStartTime() < dto.getEndTime() && + item.getEndTime() > dto.getStartTime()).collect(Collectors.toList()); + }else { + carbonPeakConfigEntityList = carbonPeakConfigEntities.stream().filter(item -> item.getStartTime() < dto.getEndTime() && + item.getEndTime() > dto.getStartTime()&& !dto.getId().equals(item.getId())).collect(Collectors.toList()); + } + + if (CollectionUtil.isNotEmpty(carbonPeakConfigEntityList)) { + List monthList = Arrays.asList(dto.getMonths().split(",")); + List entityList = carbonPeakConfigEntityList.stream().filter(item -> !CollectionUtil.intersection(monthList, + Arrays.asList(item.getMonths().split(","))).isEmpty()).collect(Collectors.toList()); + if (!entityList.isEmpty()) { + List entities = entityList.stream().filter(item ->item.getYear().equals(dto.getYear())).collect(Collectors.toList()); + if(!entities.isEmpty()){ + return new Result().ok("配置失败,尖峰谷平时间范围不能重叠!"); + } + } + } + } + if(type.equals("IN")){ + this.saveDto(dto); + }else { + this.updateDto(dto); + } + return new Result().ok("配置成功"); + } + + + /** + * 根据企业获取企业价格配置 + * + * @param tenantCode + * @return + */ + public List getPriceByDeptId(Long tenantCode) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", tenantCode); + return mapper.selectListByQuery(wrapper); + } +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonTsKvServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonTsKvServiceImpl.java new file mode 100644 index 0000000..5916c81 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/CarbonTsKvServiceImpl.java @@ -0,0 +1,327 @@ +package com.thing.carbon.config.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonTsKvEntity; +import com.thing.carbon.config.mapper.CarbonTsKvMapper; +import com.thing.carbon.config.service.CarbonTsKvService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.event.QueueConsumerEvent; +import com.thing.common.data.proto.QueueProto; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.publicorg.benchmarkingproject.dto.ThingSequentialInfoParamDTO; +import com.thing.publicorg.benchmarkingproject.dto.ThingSequentialParamDTO; +import com.thing.queue.util.Topics; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.service.IotThingDictService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 企业遥测数据缓存 + * + * @author ssy + * @since 3.0 2023-09-12 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class CarbonTsKvServiceImpl extends BaseServiceImpl implements CarbonTsKvService { + + private final IotThingEntityService iotThingEntityService; + private final IotThingDictService iotThingDictService; + private final IotThingDictRelationService iotThingDictRelationService; + private final TsKvService tsKvService; + private final ApplicationEventPublisher eventPublisher; + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + + /** + * 1. 判断是否需要计算月、年数据 + * 1.1 先查询月、年属性,如果没有对应的属性,则通过异常给出提示 + * 1.2 存在月、年属性,则进行汇总 + * 2. TB: 存储或删除 + * 3. pgsql: 存储(更新)或删除 + */ + @Override + public void handleSave(ThingSequentialInfoParamDTO thingSequentialParamDTO) { + Long tenantCode = UserContext.getRealTenantCode(); + + String thingCode = thingSequentialParamDTO.getCode(); + List tsKvParams = thingSequentialParamDTO.getTsKvParams(); + Map> tsKvMap = tsKvParams.stream().collect(Collectors.groupingBy(ThingSequentialParamDTO::getKey)); + + Optional thingOptional = iotThingEntityService.findEntityByCode(thingCode, tenantCode, true); + if (thingOptional.isEmpty()) { + return; + } + IotThingEntityDTO thing = thingOptional.get(); + // 验证是否需要汇总月数据 + List originalAttrCodes = new ArrayList<>(tsKvMap.keySet()); + Map computableMonthMap = checkAndGetNextAttrCodes(thingSequentialParamDTO.getCalculateMonth(), thing.getId(), originalAttrCodes); + Map monthParamMap = new HashMap<>(); + Map yearParamMap = new HashMap<>(); + if (!CollectionUtils.isEmpty(computableMonthMap)) { + monthParamMap.putAll(doCalculate(computableMonthMap, tsKvMap)); + } + + // 验证是否需要汇总年数据 + // 1. 当前是日 -> 必须有月数据 (monthParamMap不空) + // 2. 当前是月 (monthParamMap必然为空) -> 本身就是月数据 + Boolean calculateYear = thingSequentialParamDTO.getCalculateYear(); + Map computableYearMap; + if (!monthParamMap.isEmpty()) { + computableYearMap = checkAndGetNextAttrCodes(calculateYear, thing.getId(), new ArrayList<>(monthParamMap.keySet())); + if (!CollectionUtils.isEmpty(computableYearMap)) { + // 基于日数据汇总出了某月数据,但还需要查询其他月份的数据,然后统一汇总成年数据 + yearParamMap.putAll(doCalculate(computableYearMap, getAllMonthData(monthParamMap, thingCode))); + } + } else if (AttributeTypeEnum.mm.isCurrentType(getAttrType(originalAttrCodes.get(0)))) { + computableYearMap = checkAndGetNextAttrCodes(calculateYear, thing.getId(), originalAttrCodes); + if (!CollectionUtils.isEmpty(computableYearMap)) { + yearParamMap.putAll(doCalculate(computableYearMap, tsKvMap)); + } + } + + // 将年、月数据都统一放到待新增的参数中 + tsKvParams.addAll(monthParamMap.values()); + tsKvParams.addAll(yearParamMap.values()); + + // 检查新增或者删除, 分两个对象操作 + Map> saveOrDeleteGroup = tsKvParams.stream().collect(Collectors.groupingBy(param -> StringUtils.isNotBlank(param.getVal()))); + List prepareSaveList = saveOrDeleteGroup.get(true); + List prepareDeleteList = saveOrDeleteGroup.get(false); + + // 新增 + if (!CollectionUtils.isEmpty(prepareSaveList)) { + doSave(thing, prepareSaveList); + } + + // 删除 + if (!CollectionUtils.isEmpty(prepareDeleteList)) { + doRemove(thing, prepareDeleteList); + } + + } + + + /** + * 验证是否可以计算下一级指标数据 + * + * @param isCalculate 是否需要计算 + * @param thingId 物实体id + * @param currentAttrCodeList 当前属性code列表 + * @return 当前属性以及当前属性所对应的下一级属性所组成的map: Map(当前属性,下一级属性) + */ + private Map checkAndGetNextAttrCodes(Boolean isCalculate, Long thingId, List currentAttrCodeList) { + if (CollectionUtils.isEmpty(currentAttrCodeList)) { + throw new SysException("未指定物属性"); + } + if (isCalculate == null || !isCalculate) { + return null; + } + // 获取属性类型: dd, mm, yy + String currentAttrType = getAttrType(currentAttrCodeList.get(0)); + + // 获取待计算的下一个属性类型: dd -> mm, mm -> yy + String nextAttrType = Objects.requireNonNull(AttributeTypeEnum.match(currentAttrType).next()).name(); + Map attrCodeRelationMap = currentAttrCodeList.stream().collect(Collectors.toMap(Function.identity(), code -> code.replace(currentAttrType, nextAttrType), (v1, v2) -> v2)); + List nextAttrCodes = new ArrayList<>(attrCodeRelationMap.values()); + + // 基于推算出的下一个属性,去字典表及关系表中确认是否存在该属性 + Set dbNextAttrCodes = iotThingDictService.findDictByCodes(nextAttrCodes).stream().map(IotThingDictDTO::getCode).collect(Collectors.toSet()); + if (dbNextAttrCodes.isEmpty()) { + throw new SysException("请先维护属性:" + nextAttrCodes); + } + Set validNextAttrCodes = iotThingDictRelationService.findAllByEntityIdAndCodes(thingId, dbNextAttrCodes).stream().map(IotThingDictRelationDTO::getCode).collect(Collectors.toSet()); + Map validNextAttrIdMap = new HashMap<>(); + List inValidAttrCode = new ArrayList<>(1); + dbNextAttrCodes.forEach((attrCode) -> { + if (validNextAttrCodes.contains(attrCode)) { + validNextAttrIdMap.put(attrCode, true); + } + }); + dbNextAttrCodes.forEach((attrCode) -> { + if (!validNextAttrIdMap.containsKey(attrCode) && !validNextAttrCodes.contains(attrCode) && !inValidAttrCode.contains(attrCode)) { + inValidAttrCode.add(attrCode); + } + }); + if (!CollectionUtils.isEmpty(inValidAttrCode)) { + throw new SysException("请先维护属性:" + inValidAttrCode); + } + return attrCodeRelationMap; + } + + private String getAttrType(String attrCode) { + return attrCode.substring(attrCode.length() - 2); + } + + /** + * 获取下一个属性级别的时间 + */ + private String getNextAttrTypeTime(ThingSequentialParamDTO tsKvParam) { + String attrType = getAttrType(tsKvParam.getKey()); + return AttributeTypeEnum.match(attrType).nextAttributeTypeTime(tsKvParam.getTs()); + } + + /** + * 基于日的数据汇总出了某月数据,基于该月信息,找到对应年份的所有月数据 + * + * @param monthParamMap 基于日数据汇总得到的某一个月的数据 + * @param thingCode 物编码 + * @return 月份数据,无数据的月份不需要返回 + */ + private Map> getAllMonthData(Map monthParamMap, String thingCode) { + Map> monthKvMap = new HashMap<>(); + monthParamMap.forEach( + (attrCode, kvTs) -> { + List timeRange = AttributeTypeEnum.mm.getTimeRangeWithPattern(DateTimeUtils.parseDateTime(kvTs.getTs())); + long startTime = DateTimeUtils.dateToTimestamp(timeRange.get(0), DateTimeUtils.DATE_TIME_PATTERN_STR); + long endTime = DateTimeUtils.dateToTimestamp(timeRange.get(timeRange.size() - 1), DateTimeUtils.DATE_TIME_PATTERN_STR); + List tskvList; + try { + tskvList = tsKvService.findTsKvByCodeAndAttr(thingCode, attrCode, startTime, endTime, true); + } catch (Exception e) { + log.error("企业数据——查询月历史数据异常:", e); + throw new SysException("internal server error"); + } + // 最新的月数据:在所有查询出的月数据中,将汇总得到的月份数据覆盖查询到的同一时间的数据,那么整体就算是最新数据 + List latestAllMonthKvList = new ArrayList<>(); + if (CollectionUtils.isEmpty(tskvList)) { + latestAllMonthKvList.add(kvTs); + } else { + tskvList.forEach(monthData -> { + String monthDataTs = DateTimeUtils.millisecondsFormatStrDate(monthData.getTs()); + if (Objects.equals(kvTs.getTs(), monthDataTs)) { + latestAllMonthKvList.add(kvTs); + } else { + latestAllMonthKvList.add(new ThingSequentialParamDTO(monthDataTs, monthData.getAttrKey(), monthData.getVal())); + } + }); + } + monthKvMap.put(attrCode, latestAllMonthKvList); + }); + return monthKvMap; + } + + /** + * 计算汇总数据 + * + * @param computableAttrCodeRelationMap 可用户计算的属性map, k-v结构为“当前物属性-下一级物属性”, 比如: A29dd-A29mm + * @param tsKvMap 前一个属性以及对应值的map, 比如: A29dd-[{ts, val}, {ts, val}] + * @return 下一级属性的汇总数据,比如:A29mm-{ts, val} + */ + private Map doCalculate(Map computableAttrCodeRelationMap, Map> tsKvMap) { + Map resultMap = new HashMap<>(); + computableAttrCodeRelationMap.forEach( + (currentAttrCode, nextAttrCode) -> { + List tsKvList = tsKvMap.get(currentAttrCode); + Double sumValue = + tsKvList.stream() + .map(ThingSequentialParamDTO::getVal) + .filter(StringUtils::isNotBlank) + .map(Double::parseDouble) + .reduce(Double::sum) + .orElse(null); + resultMap.put( + nextAttrCode, + new ThingSequentialParamDTO( + getNextAttrTypeTime(tsKvList.get(0)), + nextAttrCode, + Objects.isNull(sumValue) ? "" : sumValue.toString())); + }); + return resultMap; + } + + private void doSave(IotThingEntityDTO thing, List prepareSaveList) { + // tskv 新增或更新 + List dataProtoList = + prepareSaveList.stream() + .map(e -> convert(thing.getCode(), e)) + .toList(); + if (!dataProtoList.isEmpty()) { + eventPublisher.publishEvent( + new QueueConsumerEvent(Topics.V1_TSKV_HISTORY.getValue(), dataProtoList)); + } + + // pgsql 新增或更新 + long currentTime = System.currentTimeMillis(); + List carbonTsKvSaveList = + prepareSaveList.stream() + .map(item -> new CarbonTsKvEntity() + .setThingId(thing.getId()) + .setThingCode(thing.getCode()) + .setAttrKey(item.getKey()) + .setTs(DateTimeUtils.dateToTimestamp(item.getTs(), DateTimeUtils.DATE_TIME_PATTERN_STR)) + .setVal(item.getVal()) + .setDayTime(DateTimeUtils.parse(item.getTs(), DateTimeUtils.DATE_PATTERN_STR)) + .setCreateTime(currentTime) + .setUpdateTime(currentTime) + .setAttributeType(getAttrType(item.getKey())) + ) + .collect(Collectors.toList()); + mapper.manualInsertBatch(carbonTsKvSaveList, currentTime); + } + + private void doRemove(IotThingEntityDTO thing, List prepareDeleteList) { + List deleteList = + prepareDeleteList.stream() + .map( + item -> { + TsKvDTO tsKvDTO = new TsKvDTO(); + tsKvDTO.setThingCode(thing.getCode()); + tsKvDTO.setAttrKey(item.getKey()); + tsKvDTO.setTs( + DateTimeUtils.dateToTimestamp( + item.getTs(), + DateTimeUtils.DATE_TIME_PATTERN_STR)); + return tsKvDTO; + }) + .toList(); + // tskv 删除: 需要一个批量删除方法 + deleteList.forEach(tsKvService::deleteTskv); + + // pgsql 删除 + for (ThingSequentialParamDTO item : prepareDeleteList) { + mapper.deleteByThingIdAndAttrCodeAndTs(thing.getId(), item.getKey(), DateTimeUtils.dateToTimestamp(item.getTs(), DateTimeUtils.DATE_TIME_PATTERN_STR)); + } + } + + private QueueProto.DataProto convert( + String thingCode, ThingSequentialParamDTO sequentialParam) { + if (Objects.isNull(sequentialParam.getVal())) { + return null; + } + return QueueProto.DataProto.newBuilder() + .setTskvProto( + QueueProto.TsKvProto.newBuilder() + .setThingCode(thingCode) + .setKey(sequentialParam.getKey()) + .setVal(sequentialParam.getVal()) + .setTs(Long.parseLong(sequentialParam.getTs())) + .build()) + .build(); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/MeterReadServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/MeterReadServiceImpl.java new file mode 100644 index 0000000..5f199ca --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/MeterReadServiceImpl.java @@ -0,0 +1,185 @@ +package com.thing.carbon.config.service.impl; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; + +import com.thing.carbon.config.dto.MeterReadReqDTO; +import com.thing.carbon.config.dto.MeterReadRespDTO; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.excel.MeterReadExcel; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbon.config.service.MeterReadService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.utils.export.ExcelEntityGenerator; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; + +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import io.micrometer.common.util.StringUtils; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.poi.ss.usermodel.Workbook; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.time.temporal.TemporalAdjusters; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.LongStream; + +/** + * Author: SiYang + * Date: 2023/10/19 16:15 + * Description: 抄表数据服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class MeterReadServiceImpl implements MeterReadService { + + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + private final ThingManageContextService thingManageContextService; + private final TsKvService tsKvService; + + /** + * 有些能源品种没有维护在数据库中,这里单独维护一个 + */ + private static final Set energyDictSet = new TreeSet<>(); + + static { + energyDictSet.add("A30"); + } + + @Override + public List report(MeterReadReqDTO request) { + List thingIds = request.getThingIds(); + List energyVarietyIds = request.getEnergyVarietyIds(); + + // 时间 + LocalDateTime beginDateTime = DateTimeUtils.parseDateTime(request.getBeginTime()); + LocalDateTime endDateTime = beginDateTime.with(TemporalAdjusters.lastDayOfMonth()); + long startTs = DateTimeUtils.parse(beginDateTime); + long endTs = DateTimeUtils.parse(endDateTime) + 1; + + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(thingIds, null); + List thingDictList = optionalList.orElseGet(Collections::emptyList); + List energyVarietyEntities = carbonEnergyVarietyService.selectByIds(energyVarietyIds); + Map varietyMap = energyVarietyEntities.stream().collect(Collectors.toMap(CarbonEnergyVarietyEntity::getCode, CarbonEnergyVarietyEntity::getName)); + varietyMap.put("A30", varietyMap.get("A29")); + + energyDictSet.addAll( + energyVarietyEntities.stream() + .map(CarbonEnergyVarietyEntity::getCode) + .collect(Collectors.toSet())); + + // 过滤出有效的物和属性 + List validThingDictList = + thingDictList.stream() + .filter(e -> energyDictSet.contains(e.getCode())) + .collect(Collectors.toList()); + + if (CollectionUtils.isEmpty(validThingDictList)) { + return Collections.emptyList(); + } + + List thingCodes = validThingDictList.stream().map(IotThingDictRelationParamDTO::getEntityCode).distinct().collect(Collectors.toList()); + List attrCodes = validThingDictList.stream().map(IotThingDictRelationParamDTO::getCode).distinct().collect(Collectors.toList()); + + List tsKvList = findTsKv(thingCodes, attrCodes, startTs, endTs); + + Map> tsKvMap = tsKvList.stream().collect( + Collectors.groupingBy(e -> e.getThingCode() + ":" + e.getAttrKey(), + Collectors.toMap(e->String.valueOf(e.getTs()), this::getStandardVal) + )); + return convert(validThingDictList, tsKvMap, varietyMap); + } + + @Override + public void export(MeterReadReqDTO request, HttpServletResponse response) { + // 报表原始数据 + List report = report(request); + + // 数据类型转换 + List excelDataList = MeterReadExcel.convert(report); + + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + + // 导出 + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "抄表数据报表"), excelExportEntities, dataCollection); + ExcelUtils.downLoadExcel("抄表数据报表.xls", response, workbook); + } + + private List convert( + List thingDictList, + Map> tsKvMap, + Map varietyMap) { + return thingDictList.stream() + .map( + thingDict -> { + String key = thingDict.getEntityCode() + ":" + thingDict.getCode(); + Map dataMap = tsKvMap.get(key); + if (Objects.isNull(dataMap)) { + return null; + } + Optional optional = thingManageContextService.findEntityById(thingDict.getEntityId()); + return new MeterReadRespDTO() + .setThingName(optional.map(IotThingEntityInfoDTO::getName).orElse(null)) + .setThingCode(optional.map(IotThingEntityInfoDTO::getCode).orElse(null)) + .setAttrCode(thingDict.getCode()) + .setEnergyVarietyName(varietyMap.get(thingDict.getCode())) + .setUnit(thingDict.getUnit()) + .setData(new TreeMap<>(dataMap)); + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + + private List findTsKv( + List thingCodes, List attrCodes, Long startTs, Long endTs) { + List tsList = getTsSet(startTs, endTs); + List tskvList = tsKvService.findTsKvIntervalAggByCodesAndAttrs(thingCodes, attrCodes, startTs, endTs, AggType.MIN, 1, TimeType.DAY, true); + // 补全tskv + tskvList.stream() + .collect(Collectors.groupingBy(e -> e.getThingCode() + ":" + e.getAttrKey())) + .forEach( + (tag, list) -> { + String[] tagArr = tag.split(":"); + tsList.forEach( + ts -> { + if (list.stream().noneMatch(e -> e.getTs().equals(ts))) { + tskvList.add(new TsKvDTO(tagArr[0], tagArr[1], ts, "0")); + } + }); + }); + + return tskvList; + } + + private String getStandardVal(TsKvDTO tsKvDTO) { + String val = tsKvDTO.getVal(); + return String.format("%.1f", Double.parseDouble(StringUtils.isBlank(val) ? "0" : val)); + } + + private List getTsSet(Long startTime, Long endTime) { + endTime = Math.min(System.currentTimeMillis(), endTime); + return LongStream.iterate(startTime, i -> i + 86400000L) + .limit((endTime - startTime) / 86400000L) + .boxed() + .collect(Collectors.toList()); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/TransformerBizConfigServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/TransformerBizConfigServiceImpl.java new file mode 100644 index 0000000..9eb15e1 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/config/service/impl/TransformerBizConfigServiceImpl.java @@ -0,0 +1,104 @@ +package com.thing.carbon.config.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.dto.TransformerBizConfigDTO; +import com.thing.carbon.config.service.TransformerBizConfigService; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.bizconfig.dto.IotThingBizConfigDTO; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; +import com.thing.thing.bizconfig.mapper.IotThingBizConfigMapper; +import com.thing.thing.bizconfig.service.IotThingBizConfigService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/7/8 15:45 + * @description + */ +@Service +@RequiredArgsConstructor +public class TransformerBizConfigServiceImpl extends BaseServiceImpl implements TransformerBizConfigService { + + private final IotThingBizConfigService iotThingBizConfigService; + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public PageData handlePage(Map params) { + params.put("type", TransformerBizConfigDTO.CONFIG_TYPE); + PageData page = iotThingBizConfigService.handlePage(params); + List list = page.getList(); + List deviceConfigList = + list.stream() + .map(c -> IotThingBizConfigDTO.convertToDeviceConfig(c, TransformerBizConfigDTO.class)) + .collect(Collectors.toList()); + return new PageData<>(deviceConfigList, page.getTotal()); + } + + @Override + public List handleList(Map params) { + params.put("type", TransformerBizConfigDTO.CONFIG_TYPE); + List list = iotThingBizConfigService.handleList(params); + return list.stream() + .map( + c -> IotThingBizConfigDTO.convertToDeviceConfig(c, TransformerBizConfigDTO.class)) + .collect(Collectors.toList()); + } + + @Override + public TransformerBizConfigDTO handleDetail(Long id) { + IotThingBizConfigEntity config = iotThingBizConfigService.handleDetail(id); + return IotThingBizConfigDTO.convertToDeviceConfig(config, TransformerBizConfigDTO.class); + } + + @Override + public void handleSave(TransformerBizConfigDTO configDTO) { + iotThingBizConfigService.handleSave(configDTO); + } + + @Override + public void handleBatchSave(List configDTOList) { + // 删除逻辑应当由前端判断,这段逻辑是由于前端暂时没法处理才交由后端 + List existList = handleList(new HashMap<>()); + // 本次传入的所有设备 + Set thingSet = + configDTOList.stream() + .map(TransformerBizConfigDTO::getThingId) + .collect(Collectors.toSet()); + // 如果不在本次入参里,就进行删除 + Set toRemoveIds = + existList.stream() + .filter(e -> !thingSet.contains(e.getThingId())) + .map(TransformerBizConfigDTO::getId) + .collect(Collectors.toSet()); + if(!CollectionUtils.isEmpty(toRemoveIds)){ + iotThingBizConfigService.handleDelete(toRemoveIds); + } + iotThingBizConfigService.handleBatchSave(new ArrayList<>(configDTOList)); + } + + @Override + public void handleUpdate(TransformerBizConfigDTO configDTO) { + iotThingBizConfigService.handleUpdate(configDTO); + } + + @Override + public void handleDelete(String[] ids) { + iotThingBizConfigService.handleDelete(ids); + } + + @Override + public TransformerBizConfigDTO getByThingId(Long thingId) { + IotThingBizConfigEntity config = iotThingBizConfigService.getByThingIdAndType(thingId, TransformerBizConfigDTO.CONFIG_TYPE); + return IotThingBizConfigDTO.convertToDeviceConfig(config, TransformerBizConfigDTO.class); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyEffReportController.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyEffReportController.java new file mode 100644 index 0000000..a14b066 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyEffReportController.java @@ -0,0 +1,80 @@ +package com.thing.carbon.energyrepory.controller; + +import com.thing.carbon.energyrepory.dto.*; +import com.thing.carbon.energyrepory.service.EnergyEffService; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/06 10:15 + * Description: 能效报告控制器 + */ +@RestController +@RequestMapping("v2/energy/eff/report") +@Tag(name = "能效报告") +@RequiredArgsConstructor +public class EnergyEffReportController { + + private final EnergyEffService energyEffService; + + @PostMapping("usage") + @Operation(summary="用量分析报告") + @Parameters({ + @Parameter(name = "dateType", description = "日期类型,am=天,dd=月,mm=年"), + @Parameter(name = "dateStr", description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy"), + @Parameter(name = "thingCode", description = "物编码"), + }) + public Result> energyUsageReport(@RequestBody ReportParam param) { + List list = energyEffService.energyUsageReport(param); + return new Result>().ok(list); + } + + @PostMapping("load") + @Operation(summary="负荷分析报告") + @Parameters({ + @Parameter(name = "dateType", description = "日期类型,am=天,dd=月,mm=年"), + @Parameter(name = "dateStr", description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy"), + @Parameter(name = "thingCode", description = "物编码"), + }) + public Result energyLoadReport(@RequestBody ReportParam param) { + EnergyLoadAgg report = energyEffService.energyLoadReport(param); + return new Result().ok(report); + } + + @PostMapping("nightUsage") + @Operation(summary="夜间用能分析报告") + @Parameters({ + @Parameter(name = "dateType", description = "日期类型,am=天,dd=月,mm=年"), + @Parameter(name = "dateStr", description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy"), + @Parameter(name = "thingCode", description = "物编码"), + }) + public Result> energyNightUsageAgg(@RequestBody ReportParam param) { + List energyNightUsageAggs = energyEffService.getEnergyNightUsageAgg(param); + return new Result>().ok(energyNightUsageAggs); + } + + @PostMapping("alarmAnalysis") + @Operation(summary="告警分析报告") + @Parameters({ + @Parameter(name = "dateType", description = "日期类型,am=天,dd=月,mm=年"), + @Parameter(name = "dateStr", description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy"), + @Parameter(name = "thingCode", description = "物编码"), + }) + public Result energyAlarmAnalysis(@RequestBody ReportParam param) { + EnergyAlarm energyAlarm = energyEffService.getEnergyAlarmAnalysis(param); + return new Result().ok(energyAlarm); + } + + +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyUsageController.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyUsageController.java new file mode 100644 index 0000000..bc2d2f9 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/controller/EnergyUsageController.java @@ -0,0 +1,233 @@ +package com.thing.carbon.energyrepory.controller; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.energyrepory.dto.*; +import com.thing.carbon.energyrepory.service.EnergyUsageFlowService; +import com.thing.carbon.energyrepory.service.EnergyUsageOverViewService; +import com.thing.carbon.energyrepory.service.EnergyUsageService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.Result; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + + +import java.text.DecimalFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author SiYang + * @date 2024/01/02 13:37 + * @description 能耗统计控制器 + */ +@RestController +@RequestMapping("v2/energy/usage") +@Tag(name="能耗统计控制器") +@RequiredArgsConstructor +public class EnergyUsageController { + + private final EnergyUsageService energyUsageService; + private final EnergyUsageFlowService energyUsageFlowService; + private final EnergyUsageOverViewService energyUsageOverViewService; + + /*------------------------------能耗统计-----------------------------------*/ + + @PostMapping("varieties") + @Operation(summary="物的能源品种列表") + @Parameters({ + @Parameter(name = "rootId", description = "物关系Id"), + @Parameter(name = "thingIds", description = "物Id列表"), + @Parameter(name = "configType", description = "配置页类型"), + }) + public Result> thingEnergyVarieties(@RequestBody EnergyUsageStatisticRequest request) { + List thingIds = request.getThingIds().stream().map(Long::parseLong).collect(Collectors.toList()); + return new Result>() + .ok( + energyUsageService.getThingEnergyVarieties( + request.getRootId(), thingIds, request.getConfigType())); + } + + @PostMapping("statistic/report") + @Operation(summary= "能耗统计报表") + public Result> usageStatisticReport(@RequestBody EnergyUsageStatisticRequest request) { + ValidatorUtils.validateEntity(request); + List res = energyUsageService.usageStatisticReport(request); + return new Result>().ok(res); + } + + + @PostMapping("statistic/report/export") + public void usageStatisticReportExport(@RequestBody EnergyUsageStatisticRequest request, HttpServletResponse response) { + if ("1".equals(request.getType())) { + energyUsageService.usageStatisticReportExport(request, response); + } else { + energyUsageService.usageStatisticReportExportVertical(request, response); + } + } + + + /*------------------------------能流图-----------------------------------*/ + + @PostMapping("flow/variety/list") + @Operation(summary="基于物和属性类型查询列表") + public Result> flowVarietyList(@RequestBody CarbonEnergyVarietyReqDTO request ){ + List list = energyUsageFlowService.energyVarietyList(request); + return new Result>().ok(list); + } + + @PostMapping("flow/summary") + @Operation(summary="能流汇总数据") + @Parameters({ + @Parameter(name = Constant.BEGIN_TIME, description = "开始时间"), + @Parameter(name = Constant.END_TIME, description = "结束时间"), + @Parameter(name = "thingId", description = "物id"), + @Parameter(name = "attrType", description = "属性类型"), + @Parameter(name = "energyVarietyId", description = "能源属性Id"), + }) + public Result getFlowSummary(@RequestBody EnergyUsageReqDTO request) { + EnergyUsageSummaryDTO result = energyUsageFlowService.getFlowSummary(request); + return new Result().ok(result); + } + + /*------------------------------用能概况-----------------------------------*/ + + @PostMapping("overview/variety/list") + @Operation(summary="基于物和属性类型查询列表") + public Result> overviewVarietyList(@RequestBody CarbonEnergyVarietyReqDTO request ){ + List list = energyUsageOverViewService.energyVarietyList(request); + return new Result>().ok(list); + } + + @PostMapping("overview/time/compare") + @Operation(summary="时比数据") + @Parameters({ + @Parameter(name = Constant.BEGIN_TIME, description = "开始时间"), + @Parameter(name = Constant.END_TIME, description = "结束时间"), + @Parameter(name = "thingId", description = "物id"), + @Parameter(name = "attrType", description = "属性类型"), + @Parameter(name = "energyVarietyId", description = "能源属性Id"), + }) + public Result> timeCompareTsKv(@RequestBody EnergyUsageReqDTO request) { + Map result = energyUsageOverViewService.timeCompareTsKv(request); + return new Result>().ok(result); + } + + @PostMapping("overview/sub/or/brother/summary") + @Operation(summary="同级或父子级物属性汇总同比数据") + @Parameters({ + @Parameter(name = Constant.BEGIN_TIME, description = "开始时间"), + @Parameter(name = Constant.END_TIME, description = "结束时间"), + @Parameter(name = "thingId", description = "物id"), + @Parameter(name = "attrType", description = "属性类型"), + @Parameter(name = "energyVarietyId", description = "能源属性Id"), + }) + public Result> getDeviceValueSummary(@RequestBody EnergyUsageReqDTO request) { + List result = energyUsageOverViewService.subOrBrotherSummary(request); + return new Result>().ok(result); + } + + @GetMapping("overview/brother/list") + @Operation(summary="同级物信息") + @Parameters({ + @Parameter(name = "thingId", description = "物id"), + }) + public Result> getDeviceBrothers(@RequestParam Map params ) { + Long thingId = MapUtil.getLong(params, "thingId"); + Long rootId = MapUtil.getLong(params, "rootId"); + Long rootThingId = MapUtil.getLong(params, "rootThingId"); + List result = energyUsageOverViewService.brotherList(thingId, rootId, rootThingId); + return new Result>().ok(result); + } + + @PostMapping("overview/brother/compare") + @Operation(summary="类比数据(同级物属性)") + @Parameters({ + @Parameter(name = Constant.BEGIN_TIME, description = "开始时间"), + @Parameter(name = Constant.END_TIME, description = "结束时间"), + @Parameter(name = "thingIds", description = "物id列表"), + @Parameter(name = "attrType", description = "属性类型"), + @Parameter(name = "energyVarietyId", description = "能源属性Id"), + }) + public Result> getDeviceBrothersDictValue(@RequestBody EnergyUsageReqDTO request) { + ValidatorUtils.validateEntity(request, DefaultGroup.class); + List result = energyUsageOverViewService.brotherCompareTsKv(request); + return new Result>().ok(result); + } + + + @PostMapping("realTimeDataMap") + @Operation(summary = "用量分析(新的-pc,小程序共用(包含最大,最小,平均值,总值)") + public Result>> getRealtimeDataMap(@Validated @RequestBody AnalysisDataReqNewDTO request) { + Map> statistics = new HashMap<>(); + Map>> analysisDataRespMap = energyUsageService.getRealtimeDataMap(request); + for (Map.Entry>> stringMapEntry : analysisDataRespMap.entrySet()) { + Map> value = stringMapEntry.getValue(); + for (Map.Entry> stringListEntry : value.entrySet()) { + Map mapValue = new HashMap<>(); + mapValue.put(stringListEntry.getKey(),stringListEntry.getValue()); + List list = stringListEntry.getValue(); + if (CollectionUtil.isNotEmpty(list)){ + // 计算最大值 + Optional maxVal = list.stream() + .map(AnalysisDataRespDTO::getVal) + .filter(val -> val != null && !val.isEmpty()) + .map(Double::parseDouble) + .max(Double::compareTo); + // 如果没有数据,就默认为 0 + Double maxValStr = maxVal.orElse(0D); + mapValue.put("max",maxValStr); + // 计算最小值 + Optional minVal = list.stream() + .map(AnalysisDataRespDTO::getVal) + .filter(val -> val != null && !val.isEmpty()) + .map(Double::parseDouble) + .min(Double::compareTo); + // 如果没有数据,就默认为 0 + Double minValStr = minVal.orElse(0D); + mapValue.put("min",minValStr); + // 计算平均值 + + OptionalDouble averageVal = list.stream() + .mapToDouble(dto -> { + if (dto.getVal() != null) { + return Double.parseDouble(dto.getVal()); + } else { + return 0.0; + } + }).average(); + // 如果没有数据,就默认为 0 + double averageValDouble = averageVal.orElse(0d); + DecimalFormat decimalFormat = new DecimalFormat("#.##"); + String formattedAverage = decimalFormat.format(averageValDouble); + mapValue.put("average",formattedAverage); + // 计算总和 + double sumVal = list.stream() + .filter(dto -> dto.getVal() != null) + .mapToDouble(dto -> Double.parseDouble(dto.getVal())) + .sum(); + mapValue.put("sum",sumVal); + } + statistics.put(stringMapEntry.getKey(),mapValue); + } + } + return new Result>>().ok(statistics); + } + + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisDataReqNewDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisDataReqNewDTO.java new file mode 100644 index 0000000..bf46b13 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisDataReqNewDTO.java @@ -0,0 +1,20 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * @author xzw + */ +@Data +@Schema(description = "用量分析传参new") +public class AnalysisDataReqNewDTO { + + @Schema(description = "属性列表集合") + private List thingAttrCodeList; + + @Schema(description = "能耗报表需要的传参") + private EnergyUsageStatisticRequest request; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisThingAttrValueRespDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisThingAttrValueRespDTO.java new file mode 100644 index 0000000..c350a00 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/AnalysisThingAttrValueRespDTO.java @@ -0,0 +1,58 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.Map; +import java.util.TreeMap; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +public class AnalysisThingAttrValueRespDTO { + + @Schema(description = "物Id") + private Long thingId; + + @Schema(description = "物名称") + private String thingName; + + @Schema(description = "物编号") + private String thingCode; + + @Schema(description = "属性Code") + private String thingAttrCode; + + @Schema(description = "物属性名称") + private String thingAttrName; + + @Schema(description = "物属性单位") + private String thingAttrUnit; + + @Schema(description = "属性历史值") + private Map attrValues; + + @Schema(description = "根主键") + private Long rootId; + + public Map initAttrValues(){ + attrValues = new TreeMap<>(); + return attrValues; + } + + public static AnalysisThingAttrValueRespDTO convertFromThingEnergyDict(ThingEnergyDictData source) { + return new AnalysisThingAttrValueRespDTO() + .setThingId(source.getThingId()) + .setThingName(source.getThingName()) + .setThingCode(source.getThingCode()) + .setThingAttrCode(source.getAttrCode()) + .setThingAttrName(source.getAttrName()) + .setThingAttrUnit(source.getAttrUnit()) + ; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyAlarm.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyAlarm.java new file mode 100644 index 0000000..e41a353 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyAlarm.java @@ -0,0 +1,31 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * Author: ls + * Date: 2023/12/06 10:42 + * Description: 告警分析数据 + */ +@Data +@Schema( name= "告警分析数据") +public class EnergyAlarm { + + @Schema(description = "告警已经处理次数") + private Long processedNum =0L; + + @Schema(description = "告警未处理次数") + private Long unProcessedNum =0L; + + @Schema(description = "告警分析列表") + private List> alarmAnalysisList; + + @Schema(description = "告警明细") + private List> alarmAnalysisDetails; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadAgg.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadAgg.java new file mode 100644 index 0000000..db92dfb --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadAgg.java @@ -0,0 +1,42 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/07 08:41 + * Description: 用量分析聚合数据 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "负荷分析聚合数据") +public class EnergyLoadAgg { + /** + * 负荷专用属性: A16 + */ + public static final String ATTR_CODE = "A16"; + + @Schema(description = "报告标题") + private String title = "负荷分析"; + + @Schema(description = "单位") + private String unit = "kW"; + + @Schema(description = "当前时间段中的最大值") + private BigDecimal max; + + @Schema(description = "当前时间段中的均值") + private BigDecimal avg; + + @Schema(description = "当前时间段的数据详情") + private List dataList; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRate.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRate.java new file mode 100644 index 0000000..772ca85 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRate.java @@ -0,0 +1,27 @@ +package com.thing.carbon.energyrepory.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/25 11:37 + * Description: 负载率 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EnergyLoadRate { + + @Schema(description = "负载率") + private List loadRate; + + @Schema(description = "负载率阈值") + private BigDecimal loadRateThreshold; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRealTime.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRealTime.java new file mode 100644 index 0000000..8451b3c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoadRealTime.java @@ -0,0 +1,33 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/25 10:43 + * Description: 实时负荷 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "实时负荷") +public class EnergyLoadRealTime { + + /** + * 负荷专用属性: A16 + */ + public static final String ATTR_CODE = "A16"; + + @Schema(description = "昨日数据") + private List yesterdayData; + + @Schema(description = "今日数据") + private List todayData; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoss.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoss.java new file mode 100644 index 0000000..5c55aaf --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyLoss.java @@ -0,0 +1,38 @@ +package com.thing.carbon.energyrepory.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/25 13:52 + * Description: 电量输入输出及损耗 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EnergyLoss { + /** + * 变压器输入电量属性: A49dd + */ + public static final String INPUT_CODE = "A49dd"; + + /** + * 变压器输出电量属性: A29dd + */ + public static final String OUTPUT_CODE = "A29dd"; + + @Schema(description = "输入电量") + private List inputData; + + @Schema(description = "输出电量") + private List outputData; + + @Schema(description = "损耗量") + private List lossData; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyNightUsageAgg.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyNightUsageAgg.java new file mode 100644 index 0000000..e107e08 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyNightUsageAgg.java @@ -0,0 +1,34 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Map; + +/** + * Author: ls + * Date: 2023/12/06 10:42 + * Description: 夜间用量分析数据 + */ +@Data +@Schema( name= "夜间用量分析数据") +public class EnergyNightUsageAgg { + + @Schema(description = "夜间报告标题") + private String title; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "夜间用能总量") + private BigDecimal total; + + @Schema(description = "超限次数") + private Long overrunNum; + + @Schema(description = "当前时间段的数据详情") + private List> dataList; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTimeLabelData.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTimeLabelData.java new file mode 100644 index 0000000..6d845ce --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTimeLabelData.java @@ -0,0 +1,81 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.common.data.tskv.TsKvDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.jetbrains.annotations.NotNull; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/12/06 10:59 + * Description: 能的时间标签数据 + */ +@Data +@Schema( name= "时间标签数据") +@NoArgsConstructor +@AllArgsConstructor +public class EnergyTimeLabelData implements Comparable{ + + @Schema(description = "时间戳") + private Long ts; + + @Schema(description = "时间标签") + private TimeLabel label; + + @Schema(description = "数据值") + private BigDecimal data; + + public enum TimeLabel { + DAY,MONTH + } + + public static TimeLabel getByAttrType(String attrType) { + switch (attrType) { + case "am": + case "dd": + return TimeLabel.DAY; + case "mm": + return TimeLabel.MONTH; + default: + return null; + } + } + + public static List convert(List tsKvMapList) { + return tsKvMapList.stream().map(EnergyTimeLabelData::convert).collect(Collectors.toList()); + } + + public static EnergyTimeLabelData convert(TsKvDTO tsKvMap) { + Long ts = tsKvMap.getTs(); + String attrCode = tsKvMap.getAttrKey(); + String attrType = getAttrType(attrCode); + return new EnergyTimeLabelData( + ts, + getByAttrType(attrType), + BigDecimal.valueOf( + Optional.of(Double.parseDouble(tsKvMap.getVal())).orElse(0D)) + .setScale(1, RoundingMode.HALF_UP)); + } + + private static String getAttrType(String attrCode) { + try { + return attrCode.substring(attrCode.length() - 2); + } catch (Exception e) { + return ""; + } + } + + @Override + public int compareTo(@NotNull EnergyTimeLabelData o) { + return ts.compareTo(o.getTs()); + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTransformerEff.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTransformerEff.java new file mode 100644 index 0000000..f8113c7 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyTransformerEff.java @@ -0,0 +1,27 @@ +package com.thing.carbon.energyrepory.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/25 13:20 + * Description: 变压器效率 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class EnergyTransformerEff { + /** + * 变压器效率专用属性: transfEFF + */ + public static final String ATTR_CODE = "transfEFF"; + + @Schema(description = "变压器效率") + private List effData; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageAgg.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageAgg.java new file mode 100644 index 0000000..fea5081 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageAgg.java @@ -0,0 +1,56 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.math.BigDecimal; +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/06 10:42 + * Description: 用量分析聚合数据 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "用量分析聚合数据") +public class EnergyUsageAgg { + + @Schema(description = "报告标题") + private String title; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "能源品种id") + private Long energyVarietyId; + + @Schema(description = "能源品种名称") + private String energyVarietyName; + + @Schema(description = "当前时间段总用量") + private BigDecimal current; + + @Schema(description = "上一个时间段总用量") + private BigDecimal previous; + + @Schema(description = "总用量环比: (current - previous) / previous * 100%") + private BigDecimal chainRatio; + + @Schema(description = "当前时间段中的最大值") + private BigDecimal max; + + @Schema(description = "当前时间段中的最小值") + private BigDecimal min; + + @Schema(description = "当前时间段中的均值") + private BigDecimal avg; + + @Schema(description = "当前时间段的数据详情") + private List dataList; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageLabelData.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageLabelData.java new file mode 100644 index 0000000..0ae6df8 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageLabelData.java @@ -0,0 +1,168 @@ +package com.thing.carbon.energyrepory.dto; + +import cn.hutool.core.map.MapUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; +import org.springframework.beans.BeanUtils; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.temporal.WeekFields; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author SiYang + * @date 2024/01/03 15:32 + * @description 能耗标签数据 + */ +@Data +@Accessors(chain = true) +@Schema( name= "能耗时间标签数据") +public class EnergyUsageLabelData { + + /** + * 数据的日期时间 + */ + @JsonIgnore + private LocalDateTime dateTime; + + private Integer year; + + private Integer month; + + private Integer weekOfYear; + + private Integer dateOfMonth; + + private Integer dateOfWeek; + + private Integer hourOfDay; + + /** + * 时间类型:day, week, month, year, any + */ + private String timeType; + + /** + * 数据标签: 数据项/汇总项 + */ + private String label; + + /** + * 属性编号 + */ + private String attrCode; + + @Schema(description = "数据值") + private BigDecimal value; + + private Long ts; + + public static List convertFromTsKvMap(List tsKvMapList) { + return tsKvMapList.parallelStream() + .map( + dataMap -> { + Long ts = dataMap.getTs(); + LocalDateTime time = DateTimeUtils.parseDateTime(ts); + double val = Double.parseDouble(dataMap.getVal()); + String attrCode = dataMap.getAttrKey(); + return generateNonValueData(time, attrCode) + .setValue( + Objects.isNull(val) + ? null + : BigDecimal.valueOf(val) + .setScale(2, RoundingMode.HALF_UP)); + }) + .collect(Collectors.toList()); + } + + /** + * 该方法不愿意对不同属性的list进行聚合,请在调用该方法之前先把属性分组 + */ + public static List generateWeekTotal(List list) { + if (CollectionUtils.isEmpty(list)) { + return Collections.emptyList(); + } + String attrCode = list.get(0).getAttrCode(); + Map> yearWeekTotalValueMap = + list.stream() + .collect( + Collectors.groupingBy( + EnergyUsageLabelData::getYear, + Collectors.groupingBy( + EnergyUsageLabelData::getWeekOfYear, + Collectors.reducing( + BigDecimal.ZERO, + d -> Optional.ofNullable(d.getValue()).orElse(BigDecimal.ZERO), + BigDecimal::add)))); + + List weekTotalDaraList = new ArrayList<>(); + yearWeekTotalValueMap.forEach( + (year, weekTotalMap) -> + weekTotalMap.forEach( + (week, value) -> + weekTotalDaraList.add( + new EnergyUsageLabelData() + .setYear(year) + .setWeekOfYear(week) + .setAttrCode(attrCode) + .setValue(value) + .setLabel("weekTotal")))); + + return weekTotalDaraList; + } + + public static EnergyUsageLabelData generateNonValueData(LocalDateTime time, String attrCode) { + return new EnergyUsageLabelData() + .setDateTime(time) + .setYear(time.getYear()) + .setMonth(time.getMonthValue()) + .setWeekOfYear(time.get(WeekFields.ISO.weekOfYear())) + .setDateOfMonth(time.getDayOfMonth()) + .setDateOfWeek(time.getDayOfWeek().getValue()) + .setHourOfDay(time.getHour()) + .setAttrCode(attrCode) + .setLabel("item") + .setTs(time.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + } + + public static EnergyUsageLabelData sameTimeAgg(List list) { + EnergyUsageLabelData target = new EnergyUsageLabelData(); + BeanUtils.copyProperties(list.get(0), target); + target.setValue(summaryValue(list)); + return target; + } + + public static BigDecimal summaryValue(List list){ + return list.stream() + .map(EnergyUsageLabelData::getValue) + .filter(Objects::nonNull) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + } + + public EnergyUsageLabelData copy(){ + return new EnergyUsageLabelData() + .setDateTime(dateTime) + .setTs(dateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()) + .setYear(year) + .setMonth(month) + .setWeekOfYear(weekOfYear) + .setDateOfMonth(dateOfMonth) + .setDateOfWeek(dateOfWeek) + .setHourOfDay(hourOfDay) + .setTimeType(timeType) + .setLabel(label) + .setAttrCode(attrCode); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageReqDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageReqDTO.java new file mode 100644 index 0000000..22370bc --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageReqDTO.java @@ -0,0 +1,43 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.common.core.validator.group.DefaultGroup; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/10/08 10:42 + * Description: 用能查询请求 + */ +@Data +public class EnergyUsageReqDTO { + @NotNull(message = "开始时间不能为空", groups = DefaultGroup.class) + private String beginTime; + + @NotNull(message = "结束时间不能为空", groups = DefaultGroup.class) + private String endTime; + + @NotEmpty(message = "物id列表不能为空", groups = DefaultGroup.class) + private List thingIds; + + @NotNull(message = "属性类型不能为空", groups = DefaultGroup.class) + private String attrType; + + @NotNull(message = "能源品种Id不能为空", groups = DefaultGroup.class) + private Long energyVarietyId; + + @Schema(description = "物关系根Id") + private Long rootId; + + @Schema(description = "顶级物Id") + private Long rootThingId; + + @Schema(description = "显示层级, 默认2级") + private int depth = 2; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageStatisticRequest.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageStatisticRequest.java new file mode 100644 index 0000000..abed1b7 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageStatisticRequest.java @@ -0,0 +1,60 @@ +package com.thing.carbon.energyrepory.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "能源价格报表") +public class EnergyUsageStatisticRequest { + + @Schema(description = "开始时间") + private String beginTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "物Id") + @NotEmpty(message = "物Id列表不能为空") + private List thingIds; + + @Schema(description = "能源品种Id列表") + private List energyVarietyIds; + + @Schema(description = "属性类型(用量区间) am, dd, mm") + @NotNull(message = "用量区间不能为空") + private String attrType; + + @Schema(description = "时间类型 day, week, month, year, any") + @NotNull(message = "时间类型不能为空, 可选值:day, week, month, year, any") + private String timeType; + + @Schema(description = "物关系根Id") + @NotNull(message = "物关系根Id不能为空") + private Long rootId; + + @Schema(description = "顶级物实体Id列表") + private List rootThingIds; + + @Schema(description = "配置页类型") + private String configType; + + private String type; + + public boolean isAnyTime() { + return Objects.equals(timeType, "any"); + } + + public boolean isWeekTime() { + return Objects.equals(timeType, "week"); + } +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageSummaryDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageSummaryDTO.java new file mode 100644 index 0000000..6dc6a14 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/EnergyUsageSummaryDTO.java @@ -0,0 +1,77 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; + +import java.io.Serial; +import java.util.List; +import java.util.Objects; + +@Data +@Accessors(chain = true) +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode(callSuper = true) +public class EnergyUsageSummaryDTO extends ThingSortTreeDTO implements Comparable { + + @Serial + private static final long serialVersionUID = 5777370723294721928L; + + public static final String LOSS = "损耗"; + + @Schema(description = "属性Code") + private String attrCode; + + @Schema(description = "物属性名称") + private String attrName; + + @Schema(description = "物属性单位") + private String attrUnit; + + @Schema(description = "总值") + private String totalValue; + + @Schema(description = "总值占比") + private String percent; + + private List childrenUsageSummary; + + public static EnergyUsageSummaryDTO convertFromThingEnergyDict(ThingEnergyDictData source) { + if (Objects.isNull(source)) { + return null; + } + EnergyUsageSummaryDTO target = new EnergyUsageSummaryDTO() + .setAttrCode(source.getAttrCode()) + .setAttrName(source.getAttrName()) + .setAttrUnit(source.getAttrUnit()) + ; + target.setEntityId(source.getThingId()); + target.setEntityCode(source.getThingCode()); + target.setEntityName(source.getThingName()); + return target; + } + + @Override + public int compareTo(@NotNull EnergyUsageSummaryDTO o) { + if (StringUtils.isBlank(totalValue) || StringUtils.isBlank(o.getTotalValue())) { + return 0; + } + if (Objects.equals(getEntityName(), LOSS)) { + return 1; + } + if (Objects.equals(o.getEntityName(), LOSS)) { + return -1; + } + double v1 = Double.parseDouble(totalValue); + double v2 = Double.parseDouble(o.getTotalValue()); + return (int) (v2 - v1); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/PeakValleyDosageReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/PeakValleyDosageReq.java new file mode 100644 index 0000000..bf3821b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/PeakValleyDosageReq.java @@ -0,0 +1,67 @@ +package com.thing.carbon.energyrepory.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +@Data +@Schema( name= "峰平谷尖分析响应结果详细信息") +@AllArgsConstructor +@NoArgsConstructor +public class PeakValleyDosageReq { + + @Schema(description = "能源品种编码") + private String baseCode; + @Schema(description = "能源品种名称") + private String baseName; + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "属性编码") + private String attrCode; + + @Schema(description = "属性名称") + private String attrName; + + @Schema(description = "属性单位") + private String uint; + + @Schema(description = "用量 属性值") + private BigDecimal dosage; + + @Schema(description = "时间") + private Long ts; + + @Schema(description = "单价") + private BigDecimal uintPrice; + + @Schema(description = "用量的总价") + private BigDecimal totalPrice; + + @Schema(description = "年份") + private Integer year; + @Schema(description = "月份") + private Integer month; + @Schema(description = "当年第几周") + private Integer weekOfYear; + @Schema(description = "当月第几天") + private Integer dateOfMonth; + @Schema(description = "当周周几") + private Integer dateOfWeek; + @Schema(description = "当天几点") + private Integer hourOfDay; + @Schema(description = "数据标签 weekTotal周总计,item用量") + private String label; + + private Integer sort; + public PeakValleyDosageReq(String thingCode, String attrCode, BigDecimal dosage, Long ts) { + this.thingCode = thingCode; + this.attrCode = attrCode; + this.dosage = dosage; + this.ts = ts; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/ReportParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/ReportParam.java new file mode 100644 index 0000000..f2c7e94 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/ReportParam.java @@ -0,0 +1,49 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@Schema( name= "能效报告入参") +public class ReportParam { + + @Schema(description = "日期类型,am=天,dd=月,mm=年") + private String dateType; + + @Schema(description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy") + private String dateStr; + + @Schema(description = "物主键:必要参数") + private Long thingId; + + @Schema(description = "物编码") + private String thingCode; + + + @SuppressWarnings("Duplicates") + public LocalDateTime getBeginTime() { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am -> { + // yyyy-MM-dd + return DateTimeUtils.parseDateTime(dateStr + " 00:00:00"); + } + case dd -> { + // yyyy-MM + return DateTimeUtils.parseDateTime(dateStr + "-01 00:00:00"); + } + case mm -> { + // yyyy + return DateTimeUtils.parseDateTime(dateStr + "-01-01 00:00:00"); + } + default -> { + } + } + return null; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/TransformerAnalysisParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/TransformerAnalysisParam.java new file mode 100644 index 0000000..04c6a88 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/dto/TransformerAnalysisParam.java @@ -0,0 +1,49 @@ +package com.thing.carbon.energyrepory.dto; + +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.time.LocalDateTime; + +/** + * Author: SiYang + * Date: 2023/12/25 11:05 + * Description: 变压器分析查询参数 + */ +@Data +@Schema( name= "变压器分析查询参数") +public class TransformerAnalysisParam { + @Schema(description = "日期类型,hh=天,dd=月") + private String dateType; + + @Schema(description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy") + private String dateStr; + + @Schema(description = "物主键") + private Long thingId; + + @Schema(description = "物编码") + private String thingCode; + + + @SuppressWarnings("Duplicates") + public LocalDateTime getBeginTime() { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case hh -> { + // yyyy-MM-dd + return DateTimeUtils.parseDateTime(dateStr + " 00:00:00"); + } + case dd -> { + // yyyy-MM + return DateTimeUtils.parseDateTime(dateStr + "-01 00:00:00"); + } + default -> { + } + } + return null; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticExcel.java new file mode 100644 index 0000000..590b00a --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticExcel.java @@ -0,0 +1,269 @@ +package com.thing.carbon.energyrepory.excel; + +import com.alibaba.fastjson.JSONArray; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import lombok.Data; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author SiYang + * @date 2024/01/05 10:05 + * @description 用能统计报表导出对象 + */ +@Data +@Accessors(chain = true) +public class EnergyUsageStatisticExcel { + /** + * 物名称 + */ + @CustomExcel(name = "结构", width = 25, needMerge = true) + private String thingName; + + /** + * 能源品种 + */ + @CustomExcel(name = "能源品种", needMerge = true, orderNum = 1) + private String energyVarietyName; + + /** + * 属性名称 + */ + @CustomExcel(name = "类型", width = 18, orderNum = 2) + private String attrName; + + /** + * 属性单位 + */ + private String attrUnit; + + @CustomExcel(name = "总量", width = 12, orderNum = 3) + private BigDecimal totalValue; + + + @CustomExcelCollection + private List dataList; + + public static List convert( + List sourceList, String timeType) { + return sourceList.stream() + .map( + source -> { + Map dataMap = source.getDataMap(); + List dataList = + JSONArray.parseArray( + JSONArray.toJSONString(dataMap.get("data")), + LabelDataExcel.class); + dataList.forEach(data -> { + data.setTimeType(timeType); + data.setExportValue(Objects.nonNull(data.getValue()) ? data.getValue().toString() : "--"); + }); + return new EnergyUsageStatisticExcel() + .setThingName(source.getThingName()) + .setEnergyVarietyName(source.getEnergyVarietyName()) + .setAttrName( + source.getAttrName() + + (Objects.nonNull(source.getAttrUnit()) + ? "(" + source.getAttrUnit() + ")" + : "")) + .setAttrUnit(source.getAttrUnit()) + .setTotalValue((BigDecimal) dataMap.get("totalValue")) + .setDataList(dataList); + }) + .collect(Collectors.toList()); + } + + @SuppressWarnings("Duplicates") + public static List calculateMergeRegion(List dataList, boolean isWeekTime) { + List mergeRegionList = new ArrayList<>(); + Map> thingCountMap = new HashMap<>(); + Map> thingEnergyCountMap = new HashMap<>(); + int beginRowOfThing = isWeekTime ? 1 : 0; + int beginRowOfEnergy = isWeekTime ? 1 : 0; + for (EnergyUsageStatisticExcel currentData : dataList) { + if (!thingCountMap.containsKey(currentData.getThingName())) { + thingCountMap.put( + currentData.getThingName(), + MutablePair.of(++beginRowOfThing, beginRowOfThing)); + } else { + Pair thingCountPair = + thingCountMap.get(currentData.getThingName()); + thingCountPair.setValue(++beginRowOfThing); + } + + String energyKey = + currentData.getThingName() + "_" + currentData.getEnergyVarietyName(); + if (!thingEnergyCountMap.containsKey(energyKey)) { + thingEnergyCountMap.put( + energyKey, MutablePair.of(++beginRowOfEnergy, beginRowOfEnergy)); + } else { + Pair energyCountPair = thingEnergyCountMap.get(energyKey); + energyCountPair.setValue(++beginRowOfEnergy); + } + } + + thingCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 0, 0)); + }); + thingEnergyCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 1, 1)); + }); + + return mergeRegionList; + } + + @Data + public static class LabelDataExcel { + private static final Map weekMap = new HashMap<>(); + + static { + weekMap.put(1, "周一"); + weekMap.put(2, "周二"); + weekMap.put(3, "周三"); + weekMap.put(4, "周四"); + weekMap.put(5, "周五"); + weekMap.put(6, "周六"); + weekMap.put(7, "周日"); + } + + /** + * 数据的日期时间 + */ + @JsonIgnore + private LocalDateTime dateTime; + + private Integer year; + + private Integer month; + + private Integer weekOfYear; + + private Integer dateOfMonth; + + private Integer dateOfWeek; + + private Integer hourOfDay; + + private String attrCode; + + private BigDecimal value; + + private String label; + + /** + * 导出时使用的值 + */ + @CustomExcel(columnNameGenerator = "colNameGenerator", width = 12, orderGenerator = "colOrderGenerator", groupGenerator = "getGroupName") + private String exportValue; + + /** + * 时间类型:week, month, year, any + */ + private String timeType; + + @SuppressWarnings("unused") + private String colNameGenerator() { + StringBuilder strBuilder = new StringBuilder(); + switch (timeType) { + case "day": + if (Objects.equals(label, "item")) { + strBuilder.append(hourOfDay).append("时"); + } + break; + case "week": + if (Objects.equals(label, "item")) { + strBuilder + .append(addZeroPrefix(month)) + .append("-") + .append(addZeroPrefix(dateOfMonth)) + .append(" ") + .append(weekMap.get(dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + strBuilder.append(weekOfYear).append("周 总计"); + } + break; + case "month": + if (Objects.equals(label, "item")) { + strBuilder.append(dateOfMonth).append("日"); + } + break; + case "year": + if (Objects.equals(label, "item")) { + strBuilder.append(month).append("月"); + } + break; + case "any": + break; + default: + } + return strBuilder.toString(); + } + + private String addZeroPrefix(Integer num) { + return (num < 10 && num > 0) ? "0" + num : num.toString(); + } + + /** + * 序号生成: + * 1. 日期越大序号越小,最近的时间排在前面 + * 2. 周总计排在周数据前面 + */ + @SuppressWarnings("unused") + private int colOrderGenerator() { + // 前面的序号已经到3了,最小不能比3小 + int orderNum = 4; + switch (timeType) { + case "day": + orderNum += (24 - hourOfDay); + break; + case "week": + // 算法可以自定义,这里只是从数学上给出一个粗浅的计算方法 + if (Objects.equals(label, "item")) { + orderNum += (((52 - weekOfYear) << 3) + (8 - dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + orderNum += (52 - weekOfYear) << 3; + } + break; + case "month": + orderNum += (31 - dateOfMonth); + break; + case "year": + orderNum += (12 - month); + break; + case "any": + break; + default: + } + return orderNum; + } + + @SuppressWarnings("unused") + private String getGroupName() { + if (!Objects.equals(timeType, "week")) { + return null; + } + return Objects.isNull(weekOfYear) ? null : weekOfYear + "周"; + } + + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticVerticalExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticVerticalExcel.java new file mode 100644 index 0000000..cf55d37 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/excel/EnergyUsageStatisticVerticalExcel.java @@ -0,0 +1,197 @@ +package com.thing.carbon.energyrepory.excel; + +import com.alibaba.fastjson.JSONArray; +import com.thing.carbon.config.dto.CarbonTimeLabelData; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author SiYang + * @date 2024/01/05 10:05 + * @description 用能统计报表导出对象 + */ +@Data +@NoArgsConstructor +@SuppressWarnings("unused") +public class EnergyUsageStatisticVerticalExcel { + private static final String VALUE_WITH_UNIT = "%s(%s)"; + + /** + * 数据值: 能源(品种)、总价、日期等 + */ + @CustomExcel(name = "结构", width = 25) + private String tag; + + @CustomExcelCollection + private List dataList; + + public EnergyUsageStatisticVerticalExcel(String tag) { + this.tag = tag; + } + + public List getDataList() { + if (Objects.isNull(dataList)) { + dataList = new ArrayList<>(); + } + return this.dataList; + } + + @Data + @Accessors(chain = true) + public static class CellData { + @CustomExcel(keyGenerator = "generateKey", columnNameGenerator = "generateTitle", width = 12) + private String value; + + private String thingCode; + + private String thingName; + + private String attrCode; + + private String attrName; + + private String energyName; + + private String unit; + + public static CellData convert(ThingEnergyDictData source) { + return new CellData() + .setThingCode(source.getThingCode()) + .setThingName(source.getThingName()) + .setAttrCode(source.getAttrCode()) + .setAttrName(source.getAttrName()) + .setEnergyName(source.getEnergyVarietyName()) + .setUnit(source.getAttrUnit()); + } + + public String generateKey() { + return thingCode + attrCode; + } + + public String generateTitle() { + return thingName; + } + + } + + public static List convertFromDto(List sourceList, String timeType) { + List res = new ArrayList<>(); + Map excelMap = new TreeMap<>(Comparator.reverseOrder()); + EnergyUsageStatisticVerticalExcel energyInfo = new EnergyUsageStatisticVerticalExcel("能源"); + EnergyUsageStatisticVerticalExcel attrInfo = new EnergyUsageStatisticVerticalExcel("能源"); + EnergyUsageStatisticVerticalExcel totalPriceInfo = new EnergyUsageStatisticVerticalExcel("总量"); + sourceList.forEach(s -> { + Map dataMap = s.getDataMap(); + List dataList = JSONArray.parseArray(JSONArray.toJSONString(dataMap.get("data")), CarbonTimeLabelData.class); + if(!"any".equals(timeType)){ + dataList.forEach( + labelData -> { + // 只需展示基础数据 + if (!Objects.equals(labelData.getLabel(), "item")) { + return; + } + if (labelData.getTs()>System.currentTimeMillis() ) { + return; + } + String timeTag = getTimeTag(labelData, timeType); + excelMap.computeIfAbsent( + timeTag, + k -> new EnergyUsageStatisticVerticalExcel(timeTag)) + .getDataList() + .add(CellData.convert(s).setValue(labelData.getValue()==null?"--":labelData.getValue().toString())); + }); + } + energyInfo.getDataList().add(CellData.convert(s).setValue(String.format(VALUE_WITH_UNIT, s.getEnergyVarietyName(), s.getAttrUnit()))); + attrInfo.getDataList().add(CellData.convert(s).setValue(s.getAttrName())); + String val = dataMap.get("totalValue").toString(); + if(StringUtils.isNotEmpty(val)){ + totalPriceInfo.getDataList().add(CellData.convert(s).setValue(val)); + }else { + totalPriceInfo.getDataList().add(CellData.convert(s).setValue("--")); + } + }); + + res.add(energyInfo); + res.add(attrInfo); + res.add(totalPriceInfo); + excelMap.forEach((k,v) -> res.add(v)); + + return res; + } + + private static String getTimeTag(CarbonTimeLabelData labelData, String timeType) { + LocalDateTime time = DateTimeUtils.parseDateTime(labelData.getTs()); + switch (timeType) { + case "day": + case "any": + return time.format(DateTimeUtils.DATE_TIME_PATTERN); + case "week": + case "month": + return time.format(DateTimeUtils.DATE_PATTERN); + case "year": + return time.format(DateTimeUtils.YEAR_MONTH_PATTERN01); + default: + return ""; + } + } + + public static List calculateMergeRegion(List dataList) { + if (CollectionUtils.isEmpty(dataList)) { + return Collections.emptyList(); + } + List mergeRegionList = new ArrayList<>(); + EnergyUsageStatisticVerticalExcel firstRowData = dataList.get(0); + List firstRowDataList = firstRowData.getDataList(); + Map> thingColRangeMap = new HashMap<>(); + Map> energyColRangeMap = new HashMap<>(); + AtomicInteger colOfThing = new AtomicInteger(0); + AtomicInteger colOfEnergy = new AtomicInteger(0); + firstRowDataList.forEach( + cellData -> { + thingColRangeMap + .computeIfAbsent( + cellData.getThingCode(), + k -> MutablePair.of(colOfThing.get() + 1, colOfThing.get())) + .setValue(colOfThing.incrementAndGet()); + energyColRangeMap + .computeIfAbsent( + cellData.getThingCode() + "_" + cellData.getEnergyName(), + k -> MutablePair.of(colOfEnergy.get() + 1, colOfEnergy.get())) + .setValue(colOfEnergy.incrementAndGet()); + }); + + thingColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(0, 0, pair.getLeft(), pair.getRight())); + }); + + energyColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(1, 1, pair.getLeft(), pair.getRight())); + }); + + mergeRegionList.add(new CellRangeAddress(1, 2, 0, 0)); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/DataSelectService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/DataSelectService.java new file mode 100644 index 0000000..29ccad0 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/DataSelectService.java @@ -0,0 +1,26 @@ +package com.thing.carbon.energyrepory.service; + + +import com.thing.carbon.energyrepory.dto.EnergyTimeLabelData; + +import java.time.LocalDateTime; +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/25 10:56 + * Description: 数据查询服务 + */ +public interface DataSelectService { + + /** + * 查询ck中的物属性数据 + * @param thingCode 物编码 + * @param attrCode 属性编码 + * @param timeRange 时间范围:用于保证返回指定时间段内的全量数据 + * @param attrType 属性类型: am, dd, mm + * @return 标签数据列表 + */ + List getEnergyUsageData( + String thingCode, String attrCode, List timeRange, String attrType); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyEffService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyEffService.java new file mode 100644 index 0000000..7ae8184 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyEffService.java @@ -0,0 +1,22 @@ +package com.thing.carbon.energyrepory.service; + + +import com.thing.carbon.energyrepory.dto.*; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/06 10:16 + * Description: 能效报告服务接口 + */ +public interface EnergyEffService { + + List energyUsageReport(ReportParam param); + + EnergyLoadAgg energyLoadReport(ReportParam param); + + List getEnergyNightUsageAgg(ReportParam param); + + EnergyAlarm getEnergyAlarmAnalysis(ReportParam param); +} \ No newline at end of file diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageCommonService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageCommonService.java new file mode 100644 index 0000000..f9fc794 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageCommonService.java @@ -0,0 +1,31 @@ +package com.thing.carbon.energyrepory.service; + + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/10/18 9:47 + * Description: 能耗数据分析通用服务 + */ +public interface EnergyUsageCommonService { + + List energyVarietyList(CarbonEnergyVarietyReqDTO request); + + List> getThingsEnergyDictList(List thingIds, Long rootId, Long energyVarietyId, String attrType); + + ThingEnergyDictData getThingEnergyDict(Long thingId, Long rootId, Long energyVarietyId, String attrType, Long tenantCode); + + List findThingAttrTsKv(String thingCode, List strings, long startTs, long endTs); + + List findThingsAttrsTsKv(List thingDictList, long startTs, long endTs); + + List getSubOrBrotherThingIds(Long thingId, Long rootId, Long rootThingId); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageFlowService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageFlowService.java new file mode 100644 index 0000000..1a91d82 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageFlowService.java @@ -0,0 +1,16 @@ +package com.thing.carbon.energyrepory.service; + + + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageSummaryDTO; + +import java.util.List; + +public interface EnergyUsageFlowService { + List energyVarietyList(CarbonEnergyVarietyReqDTO request); + + EnergyUsageSummaryDTO getFlowSummary(EnergyUsageReqDTO request); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageOverViewService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageOverViewService.java new file mode 100644 index 0000000..b02bd8d --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageOverViewService.java @@ -0,0 +1,26 @@ +package com.thing.carbon.energyrepory.service; + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.energyrepory.dto.AnalysisThingAttrValueRespDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageSummaryDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.entity.IotThingEntity; + + +import java.util.List; +import java.util.Map; + +public interface EnergyUsageOverViewService { + + Map timeCompareTsKv(EnergyUsageReqDTO request); + + List subOrBrotherSummary(EnergyUsageReqDTO request); + + List brotherList(Long params, Long rootId, Long rootThingId); + + List brotherCompareTsKv(EnergyUsageReqDTO request); + + List energyVarietyList(CarbonEnergyVarietyReqDTO request); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageService.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageService.java new file mode 100644 index 0000000..137e6c5 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/EnergyUsageService.java @@ -0,0 +1,29 @@ +package com.thing.carbon.energyrepory.service; + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.energyrepory.dto.AnalysisDataReqNewDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageStatisticRequest; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +/** + * @author SiYang + * @date 2024/01/02 14:33 + * @description 用能服务接口 + */ +public interface EnergyUsageService { + + List getThingEnergyVarieties(Long rootId, List thingIds, String configType); + + List usageStatisticReport(EnergyUsageStatisticRequest request); + + void usageStatisticReportExport(EnergyUsageStatisticRequest request, HttpServletResponse response); + + void usageStatisticReportExportVertical(EnergyUsageStatisticRequest request, HttpServletResponse response); + + Map>> getRealtimeDataMap(AnalysisDataReqNewDTO request); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/DataSelectServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/DataSelectServiceImpl.java new file mode 100644 index 0000000..c04fb3b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/DataSelectServiceImpl.java @@ -0,0 +1,94 @@ +package com.thing.carbon.energyrepory.service.impl; + + +import com.thing.carbon.energyrepory.dto.EnergyTimeLabelData; +import com.thing.carbon.energyrepory.service.DataSelectService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/12/25 10:56 + * Description: 数据查询服务实现类 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class DataSelectServiceImpl implements DataSelectService { + + private final TsKvService tsKvService; + + @Override + public List getEnergyUsageData(String thingCode, String attrCode, List timeRange, String attrType) { + // 初始化数据,保证返回的数据在指定时间段内是全量的,顶多是某个时间点的data字段为空 + List allDataList = generateEmptyData(timeRange, attrType); + + // 查询ck数据 + long startTs = DateTimeUtils.parse(timeRange.get(0)); + long endTs = DateTimeUtils.parse(timeRange.get(timeRange.size() - 1)); + List thingAttrTsKv = + findThingAttrTsKv(thingCode, Collections.singletonList(attrCode), startTs, endTs); + if (CollectionUtils.isEmpty(thingAttrTsKv)) { + log.warn( + "====>> 查询ck数据为空: thingCode: {}, attrCode: {}, timeRange: {} ~ {}", + thingCode, + attrCode, + startTs, + endTs); + } + Map labelDataMap = + EnergyTimeLabelData.convert(thingAttrTsKv).stream() + .collect( + Collectors.toMap( + EnergyTimeLabelData::getTs, + Function.identity(), + (v1, v2) -> v1)); + + // 将ck中返回的数据代替掉初始数据 + for (int i = 0; i < allDataList.size(); i++) { + EnergyTimeLabelData emptyData = allDataList.get(i); + if (!labelDataMap.containsKey(emptyData.getTs())) { + continue; + } + EnergyTimeLabelData labelData = labelDataMap.get(emptyData.getTs()); + allDataList.set(i, labelData); + } + + return allDataList; + } + + /** + * 按指定时间范围生成全量空数据 + * @param timeRange 时间范围 + * @param attrType 属性类型 + * @return 空数据列表 + */ + private List generateEmptyData( + List timeRange, String attrType) { + return timeRange.stream() + .map( + t -> + new EnergyTimeLabelData( + DateTimeUtils.parse(t), + EnergyTimeLabelData.getByAttrType(attrType), + null)) + .collect(Collectors.toList()); + } + + private List findThingAttrTsKv( + String thingCode, List attrCodes, long startTs, long endTs) { + return tsKvService.findTsKvByCodeAndAttrs(thingCode, attrCodes, startTs, endTs, false); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyEffServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyEffServiceImpl.java new file mode 100644 index 0000000..e37f6be --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyEffServiceImpl.java @@ -0,0 +1,766 @@ +package com.thing.carbon.energyrepory.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.alarm.alarm.dto.AlarmRuleReportDTO; +import com.thing.alarm.alarm.entity.AlarmConfigEntity; +import com.thing.alarm.alarm.entity.AlarmDictEntity; +import com.thing.alarm.alarm.entity.AlarmRuleEntityEntity; +import com.thing.alarm.alarm.mapper.AlarmConfigMapper; +import com.thing.alarm.alarm.mapper.AlarmDictMapper; +import com.thing.alarm.alarm.mapper.AlarmRuleEntityMapper; +import com.thing.alarm.alarm.mapper.AlarmRuleLogMapper; +import com.thing.carbon.config.dto.CarbonThingEnergyDictReqDTO; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.mapper.CarbonEnergyDictRelationMapper; +import com.thing.carbon.config.mapper.CarbonEnergyVarietyMapper; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.carbon.energyrepory.dto.*; +import com.thing.carbon.energyrepory.service.DataSelectService; +import com.thing.carbon.energyrepory.service.EnergyEffService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.enumeration.CalculationSymbol; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.publicorg.publicorglimit.dto.PublicOrgLimitDTO; +import com.thing.publicorg.publicorglimit.service.PublicOrgLimitService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/12/06 10:17 + * Description: 能效报告服务实现 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class EnergyEffServiceImpl implements EnergyEffService { + + private final PublicOrgLimitService publicOrgLimitService; + private final CarbonEnergyDictRelationService carbonEnergyDictRelationService; + private final CarbonEnergyDictRelationMapper carbonEnergyDictRelationDao; + private final CarbonEnergyVarietyMapper carbonEnergyVarietyDao; + private final AlarmConfigMapper alarmConfigDao; + private final AlarmRuleLogMapper alarmRuleLogDao; + private final AlarmRuleEntityMapper alarmRuleEntityDao; + private final AlarmDictMapper alarmDictDao; + private final DataSelectService dataSelectService; + private final ThingManageContextService thingManageContextService; + private final TsKvService tsKvService; + + + private static final int THREAD_POOL_SIZE = 4; + private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(THREAD_POOL_SIZE); + + @Override + public List energyUsageReport(ReportParam param) { + // 查询当前物的用量属性 + UserContext.getRealTenantCode(); + Optional optional = thingManageContextService.findEntityByThingList(param.getThingId(), UserContext.getRealTenantCode(), true); + List thingEnergyDictList = + carbonEnergyDictRelationService.getThingEnergyDictList( + new CarbonThingEnergyDictReqDTO() + .setThingIds(Collections.singletonList(optional.get().getId())) + .setAttrTypes(Collections.singletonList(param.getDateType()))); + + // 特殊逻辑说明: 一个能源品种下有多个用量属性,此时只选择其中一个用量属性进行处理。优先选择编号较小的用量属性。 + Map> energyDictGroup = + thingEnergyDictList.stream() + .collect( + Collectors.groupingBy( + ThingEnergyDictData::getEnergyVarietyId, + Collectors.toList())); + List> futures = new ArrayList<>(); + energyDictGroup.forEach( + (energyVarietyId, energyDictList) -> { + energyDictList.sort( + Comparator.comparing(ThingEnergyDictData::getAttrCode)); + ThingEnergyDictData energyDict = energyDictList.get(0); + Future future = + EXECUTOR.submit(() -> energyUsageAggregate(energyDict, param)); + futures.add(future); + }); + + List result = new ArrayList<>(); + for (Future future : futures) { + EnergyUsageAgg energyUsageAgg = null; + try { + energyUsageAgg = future.get(); + result.add(energyUsageAgg); + } catch (Exception e) { + e.printStackTrace(); + } + } + return result; + } + + @Override + public EnergyLoadAgg energyLoadReport(ReportParam param) { + // 当期时间范围 + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + + // 查询明细数据 + List energyUsageData = + dataSelectService.getEnergyUsageData( + param.getThingCode(), EnergyLoadAgg.ATTR_CODE, timeRange, attributeTypeEnum.name()); + + // 聚合 + List dataList = + energyUsageData.stream() + .map(EnergyTimeLabelData::getData) + .filter(Objects::nonNull) + .toList(); + + if (CollectionUtils.isEmpty(dataList)) { + return new EnergyLoadAgg(); + } + + BigDecimal max = Collections.max(dataList); + double average = dataList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0D); + BigDecimal avg = BigDecimal.valueOf(average).setScale(1, RoundingMode.HALF_UP); + + EnergyLoadAgg report = new EnergyLoadAgg(); + report.setMax(max); + report.setAvg(avg); + report.setDataList(energyUsageData); + + return report; + } + + + private EnergyUsageAgg energyUsageAggregate(ThingEnergyDictData energyDict, ReportParam param) { + // 当期时间范围 + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + + // 查询明细数据 + List energyUsageData = + dataSelectService.getEnergyUsageData( + energyDict.getThingCode(), energyDict.getAttrCode(), timeRange, attributeTypeEnum.name()); + + // 聚合 + List dataList = + energyUsageData.stream() + .map(EnergyTimeLabelData::getData) + .filter(Objects::nonNull) + .toList(); + + if (CollectionUtils.isEmpty(dataList)) { + return new EnergyUsageAgg() + .setTitle(energyDict.getAttrName() + "用量分析") + .setUnit(energyDict.getAttrUnit()) + ; + } + + BigDecimal max = Collections.max(dataList); + BigDecimal min = Collections.min(dataList); + double summary = dataList.stream().mapToDouble(BigDecimal::doubleValue).sum(); + double average = dataList.stream().mapToDouble(BigDecimal::doubleValue).average().orElse(0D); + BigDecimal sum = BigDecimal.valueOf(summary).setScale(1, RoundingMode.HALF_UP); + BigDecimal avg = BigDecimal.valueOf(average).setScale(1, RoundingMode.HALF_UP); + + // 上一期数据的时间范围 + List previousTimeRange = attributeTypeEnum.getPreviousTimeRange(timeRange.get(0)); + long previousStartTs = DateTimeUtils.parse(previousTimeRange.get(0)); + long previousEndTs = DateTimeUtils.parse(previousTimeRange.get(previousTimeRange.size() - 1)); + + // 查询上一期数据总和 + List previousSummary = + findSummary(energyDict.getThingCode(), Collections.singletonList(energyDict.getAttrCode()), previousStartTs, previousEndTs); + + // 计算环比 + BigDecimal previous = null; + BigDecimal chainRatio = null; + if (!CollectionUtils.isEmpty(previousSummary)) { +// previous = +// BigDecimal.valueOf( +// Optional.ofNullable( +// MapUtil.getDouble( +// previousSummary.get(0), "summary")) +// .orElse(0D)) +// .setScale(1, RoundingMode.HALF_UP); + BigDecimal sum1 = BigDecimal.ZERO; + for (TsKvDTO tsKvDTO : previousSummary) { + sum1 = sum1.add(new BigDecimal(tsKvDTO.getVal())); + } + previous = sum1.setScale(1, RoundingMode.HALF_UP); + chainRatio = + previousSummary.isEmpty() + ? null + : sum.subtract(previous).divide(previous, 1, RoundingMode.HALF_UP); + } + EnergyUsageAgg energyUsageAgg = new EnergyUsageAgg() + .setTitle(energyDict.getAttrName() + "用量分析") + .setUnit(energyDict.getAttrUnit()) + .setEnergyVarietyId(energyDict.getEnergyVarietyId()) + .setEnergyVarietyName(energyDict.getEnergyVarietyName()) + .setCurrent(sum) + .setPrevious(previous) + .setChainRatio(chainRatio) + .setMax(max) + .setMin(min) + .setAvg(avg) + .setDataList(energyUsageData); + System.out.println("---------------"); + return energyUsageAgg; + } + + @Override + public List getEnergyNightUsageAgg(ReportParam param){ + List resList = new ArrayList<>(); + //获取夜间用能配置列表信息 + List alarmConfigEntities = alarmConfigDao.selectListByQuery(QueryWrapper.create() + .eq(AlarmConfigEntity::getThingId,param.getThingId()) + .eq(AlarmConfigEntity::getType,"2")); + //没有配置信息就没有夜间用能 + if(CollectionUtil.isEmpty(alarmConfigEntities)){ + // throw new RenException("当前用户缺失用量属性信息"); + return resList; + } + //获取对应的能源品种的属性和名称 + List carbonEnergyDictRelationEntities = + carbonEnergyDictRelationDao.selectListByQuery(QueryWrapper.create() + .eq(CarbonEnergyDictRelationEntity::getAttrType, "base")); +// .eq(CarbonEnergyDictRelationEntity::getTenantCode, UserContext.getRealTenantCode())); + //获取能源品种 + if(CollectionUtil.isEmpty(carbonEnergyDictRelationEntities)){ + throw new SysException("当前用户缺失用量属性信息"); + } + //获取开始时间和结束时间 + String dateType = param.getDateType(); + String dateStr = param.getDateStr(); + Long startTs = 0L; + Long endTs = 0L; + //按照月的时间:dateStr 为 yy-MM + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())){ + startTs = DateTimeUtils.convertYYMMTimeToLong(dateStr); + endTs = DateTimeUtils.getMonthEndTimestamp(startTs); + } + //按照月的时间:dateStr 为 yy-MM + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + startTs = DateTimeUtils.convertYYTimeToLong(dateStr); + endTs = DateTimeUtils.getYearEndTimestamp(startTs); + } + //计算开始小时,结束小时,开始分钟,结束分钟 + AlarmConfigEntity alarmConfigEntity = alarmConfigEntities.get(0); + String beginTime = alarmConfigEntity.getBeginTime(); + String endTime = alarmConfigEntity.getEndTime(); + if(StringUtils.isBlank(beginTime) || StringUtils.isBlank(endTime)){ + throw new SysException("请先设置夜间告警时间"); + } + //因为跨天,所以有这么多参数 + Integer hourStAm = null; + Integer hourEdAm = null; + Integer hourStPm = null; + Integer hourEdPm = null; + List minuteStAmList = Lists.newArrayList(); + List minuteEdPmList = Lists.newArrayList(); + String[] beginSplit = beginTime.split(":"); + String[] endSplit = endTime.split(":"); + Integer hourSt = Integer.parseInt(beginSplit[0]); + Integer hourEd = Integer.parseInt(endSplit[0]); + Integer minSt = Integer.parseInt(beginSplit[1]); + Integer minEd = Integer.parseInt(endSplit[1]); + //处理时间:1.小时相等:(分钟相等且为0) 和 (分钟相等不为0) + if(CompareUtil.compare(hourSt,hourEd) == 0){ + hourStAm = hourSt; + hourEdAm = hourEd; + if(CompareUtil.compare(minSt,0) != 0 && CompareUtil.compare(minEd,0) != 0){ + getMinuteList(0,minSt-1,minuteStAmList); + getMinuteList(minEd+1,60,minuteEdPmList); + } + } + // 2.开始小时小于结束小时:(分钟相等且为0) 和 (分钟相等不为0) + if(CompareUtil.compare(hourSt,hourEd) < 0){ + hourStAm = hourSt; + hourEdAm = hourEd; + if(CompareUtil.compare(minSt,0) != 0 ){ + getMinuteList(0,minSt-1,minuteStAmList); + } + if(CompareUtil.compare(minEd,0) == 0){ + hourEdAm -= 1; + }else { + getMinuteList(minEd+1,60,minuteEdPmList); + } + } + // 3.开始小时大于结束小时:(分钟相等且为0) 和 (分钟相等不为0 && 分钟不相等) + else if (CompareUtil.compare(hourSt, hourEd) > 0) { + hourStAm = hourSt; + hourEdAm = 23; + hourStPm = 0; + hourEdPm = hourEd; + if(CompareUtil.compare(minSt,0) != 0){ + getMinuteList(0,minSt-1,minuteStAmList); + } + if(CompareUtil.compare(minEd,0) == 0){ + hourEdPm -= 1; + }else { + getMinuteList(minEd+1,60,minuteEdPmList); + } + } + //遍历每一个能源品种 + for (CarbonEnergyDictRelationEntity carbonEnergyDictRelationEntity : carbonEnergyDictRelationEntities) { + EnergyNightUsageAgg tempEnergy = new EnergyNightUsageAgg(); + //获取能源品种的基本信息:主要获取能源品种的大名称 + CarbonEnergyVarietyEntity carbonEnergyVarietyEntity = carbonEnergyVarietyDao.selectOneById(carbonEnergyDictRelationEntity.getEnergyVarietyId()); + if(ObjectUtil.isNull(carbonEnergyVarietyEntity)){ + throw new SysException("当前用户缺失能源品种信息"); + } + tempEnergy.setTitle("夜间"+carbonEnergyVarietyEntity.getName()+"用能分析"); + //获取每个时段的夜间用能信息 + //获取能源品种对应的告警的配置信息 + List alarmConfigEntityList = alarmConfigEntities.stream() + .filter(s -> StringUtils.startsWith(carbonEnergyDictRelationEntity.getAttrCode(),s.getAttrCode()) + // && StringUtils.endsWith(carbonEnergyDictRelationEntity.getAttrCode(),dateType) + ).collect(Collectors.toList()); + if(CollectionUtil.isEmpty(alarmConfigEntityList)){ + continue; + } + List codes = alarmConfigEntityList.stream().map(AlarmConfigEntity::getThingCode).distinct().collect(Collectors.toList()); + String key = carbonEnergyDictRelationEntity.getAttrCode()+dateType; + //获取单位 + List dictId = alarmConfigEntityList.stream().map(AlarmConfigEntity::getAttrId).distinct().collect(Collectors.toList()); + + Optional> optionalList = thingManageContextService.findDictRelationAllByIds(dictId); + List iotThingDictEntities = optionalList.orElseGet(Collections::emptyList); + tempEnergy.setUnit(iotThingDictEntities.get(0).getUnit()); + //按照月的时间:dateStr 为 yy-MM ,即统计每一天的 + String dataType = TimeType.DAY.getName(); + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + dataType = TimeType.MONTH.getName(); + } + try { + //超限次数 + String alarmRule = alarmConfigEntityList.get(0).getAlarmRule(); + String alarmVal = alarmConfigEntityList.get(0).getAlarmVal(); + AtomicLong atomicLong = new AtomicLong(0); + //查询的数据 + List> dayList = getDayList(codes, key, startTs, endTs, hourStAm, hourEdAm, minuteStAmList, minuteEdPmList, hourStPm, hourEdPm, dataType); + if(CollectionUtil.isEmpty(dayList)){ + tempEnergy.setTotal(BigDecimal.ZERO); + tempEnergy.setOverrunNum(atomicLong.get()); + resList.add(tempEnergy); + continue; + } + //获取总数:比如总用电量 + BigDecimal bigDecimal = calculateSum(dayList); + tempEnergy.setTotal(bigDecimal); + //计算超限次数和超限数据处理 + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())) { + List> collect = dayList.stream().peek(s -> { + String val = MapUtil.getStr(s, "val"); + s.put("alarm",0); + int i = new BigDecimal(val).compareTo(new BigDecimal(alarmVal)); + compareRule(s, alarmRule, i, atomicLong); + }).collect(Collectors.toList()); + tempEnergy.setDataList(collect); + } + //年的类型计算 + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + //查询每一天的数据 + List> hourDDList = getDayList(codes, carbonEnergyDictRelationEntity.getAttrCode()+"dd", startTs, endTs, hourStAm, hourEdAm, minuteStAmList, minuteEdPmList, hourStPm, hourEdPm, TimeType.DAY.getName()); + //遍历每天的数据 + List> collect = dayList.stream().peek(s -> { + Long ts = MapUtil.getLong(s, "ts"); + s.put("alarm",0); + hourDDList.stream().forEach(h->{ + Long hts = MapUtil.getLong(h, "ts"); + String val = MapUtil.getStr(h, "val"); + long monthBeginTimestamp = DateTimeUtils.getMonthBeginTimestamp(hts); + int i = new BigDecimal(val).compareTo(new BigDecimal(alarmVal)); + compareYyRule(CalculationSymbol.greater.getName().equals(alarmRule) && i > 0 && ts.compareTo(monthBeginTimestamp) == 0, + CalculationSymbol.greaterEqual.getName().equals(alarmRule) && i >= 0 && ts.compareTo(monthBeginTimestamp) == 0, + CalculationSymbol.less.getName().equals(alarmRule) && i < 0 && ts.compareTo(monthBeginTimestamp) == 0, + CalculationSymbol.lessEqual.getName().equals(alarmRule) && i <= 0 && ts.compareTo(monthBeginTimestamp) == 0, s, atomicLong); + }); + }).collect(Collectors.toList()); + tempEnergy.setDataList(collect); + } + tempEnergy.setOverrunNum(atomicLong.get()); + //补全时间 + List timeList = generateTime(startTs, endTs, dateType); + List> dataList = tempEnergy.getDataList(); + if(CollectionUtil.isEmpty(dataList)){ + resList.add(tempEnergy); + continue; + } + for (int i = 0; i < timeList.size(); i++) { + int finalI = i; + boolean b = dataList.stream().anyMatch(stringObjectMap -> ObjectUtil.equals(MapUtil.getLong(stringObjectMap, "ts"), timeList.get(finalI))); + if(!b){ + Map tempMap = new HashMap<>(); + tempMap.put("ts",timeList.get(finalI)); + tempMap.put("thing_code",dataList.get(0).get( "thing_code")); + tempMap.put("attr_key",dataList.get(0).get( "attr_key")); + tempMap.put("alarm",0); + tempMap.put("val",null); + dataList.add(tempMap); + } + } + List> collect = dataList.stream().sorted(Comparator.comparing(s -> MapUtil.getLong(s, "ts"))).collect(Collectors.toList()); + tempEnergy.setDataList(collect); + resList.add(tempEnergy); + }catch (Exception e){ + log.error("查询失败",e); + throw new SysException("夜间用能分析查询失败",e); + } + } + return resList; + } + + private static void compareYyRule(boolean greater, boolean greaterEqual, boolean less, boolean lessEqual,Map s, AtomicLong atomicLong) { + compareGreater(greater, greaterEqual, s, atomicLong); + compareGreater(less, lessEqual, s, atomicLong); + } + + private static void compareGreater(boolean greater, boolean greaterEqual, Map s, AtomicLong atomicLong) { + if (greater) { + if (CompareUtil.compare(MapUtil.getInt(s, "mapper"), 1) != 0) { + s.put("alarm", 1); + } + atomicLong.getAndIncrement(); + } + if (greaterEqual) { + if (CompareUtil.compare(MapUtil.getInt(s, "mapper"), 1) != 0) { + s.put("alarm", 1); + } + atomicLong.getAndIncrement(); + } + } + + private static void compareRule(Map s, String alarmRule, int i, AtomicLong atomicLong) { + compareYyRule(CalculationSymbol.greater.getName().equals(alarmRule) && i > 0, + CalculationSymbol.greaterEqual.getName().equals(alarmRule) && i >= 0, + CalculationSymbol.less.getName().equals(alarmRule) && i < 0, + CalculationSymbol.lessEqual.getName().equals(alarmRule) && i <= 0,s, + atomicLong); + } + + private List generateTime(Long startTime,Long endTime,String dateType){ + List timeList = new ArrayList<>(); + Long tempTime = DateTimeUtils.getMillis(DateTimeUtils.parseDateTime( endTime+1).minusDays(1)); + while (tempTime>=startTime){ + timeList.add(tempTime); + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())){ + tempTime = DateTimeUtils.getMillis(DateTimeUtils.parseDateTime(tempTime).minusDays(1)); + } + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + tempTime = DateTimeUtils.getMillis(DateTimeUtils.parseDateTime(tempTime).minusMonths(1)); + } + } + return timeList; + } + + /** + * 获取每天或者每年的数据 + * @param codes 物编码 + * @param key 物属性 + * @param startTs 开始时间 + * @param endTs 结束时间 + * @param hourStAm 上半程开始小时数 + * @param hourEdAm 上半程结束小时数 + * @param minuteStAmList 不包含的分钟 + * @param minuteEdPmList 不包含的分钟 + * @param hourStPm 下半程开始小时数 + * @param hourEdPm 下半程结束小时数 + * @param dataType 获取类型:dd 月 mm年 + * @return + * @throws InterruptedException + * @throws ExecutionException + */ + private List> getDayList(List codes, String key, Long startTs, Long endTs, + Integer hourStAm, Integer hourEdAm, + List minuteStAmList, List minuteEdPmList, + Integer hourStPm, Integer hourEdPm, + String dataType) throws InterruptedException, ExecutionException { +// //判断是否有下半程查询 +// if(ObjectUtil.isNotNull(hourStPm) && ObjectUtil.isNotNull(hourEdPm)){ +// //先计算上半程数据 +// +// tsKvService.findTsKvByCodesAndAttrs(codes, Lists.newArrayList(key), startTs, endTs, A, hourStAm, hourEdAm, minuteStAmList, null) +// +// +// CompletableFuture>> listAm = dynamicViewTsKvService +// .queryIntervalAggCodeSAndKeys(codes, Lists.newArrayList(key), "sum", startTs, endTs, dataType, hourStAm, hourEdAm, minuteStAmList, null); +// //再计算下半程数据 +// Long startPmTs = DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(startTs).plusDays(1))); +// Long endPmTs = DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(endTs).plusDays(1))); +// CompletableFuture>> listPm = dynamicViewTsKvService +// .queryIntervalAggCodeSAndKeys(codes, Lists.newArrayList(key), "sum", startPmTs, endPmTs, dataType, hourStPm, hourEdPm, null, minuteEdPmList); +// List> mapAm = listAm.get(); +// List> mapPm = listPm.get(); +// //拼接求和:上半程和下半程数据总和 +// if(CollectionUtil.isNotEmpty(mapPm)){ +// getTotal(mapAm,mapPm, dataType); +// } +// return mapAm; +// }else{ +// CompletableFuture>> listAm = dynamicViewTsKvService +// .queryIntervalAggCodeSAndKeys(codes, Lists.newArrayList(key), "sum", startTs, endTs, dataType, hourStAm, hourEdAm, minuteStAmList, minuteEdPmList); +// return listAm.get(); +// } + return null; + } + + @Override + public EnergyAlarm getEnergyAlarmAnalysis(ReportParam param) { + EnergyAlarm resEnergyAlarm = new EnergyAlarm(); + + String dateType = param.getDateType(); + String dateStr = param.getDateStr(); + //获取告警类型列表 + Long startTs = 0L; + Long endTs = 0L; + //按照月的时间:dateStr 为 yy-MM + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())){ + startTs = DateTimeUtils.convertYYMMTimeToLong(dateStr); + endTs = DateTimeUtils.getMonthEndTimestamp(startTs); + } + //按照月的时间:dateStr 为 yy-MM + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + startTs = DateTimeUtils.convertYYTimeToLong(dateStr); + endTs = DateTimeUtils.getYearEndTimestamp(startTs); + } + //查询当前物的基本信息 + Optional> optionalIotThingDictRelationDTOS = thingManageContextService.findDictRelationAllByEntityIdAndCodes(param.getThingId(), null); + if(optionalIotThingDictRelationDTOS.isEmpty()){ + return resEnergyAlarm; + //throw new SysException("当前物没有配置属性信息"); + } + //获取相关告警设置属性信息 + List alarmRuleEntityEntities = alarmRuleEntityDao.selectListByQuery(QueryWrapper.create() + .eq(AlarmRuleEntityEntity::getThingId,param.getThingId()).in(AlarmRuleEntityEntity::getThingAttr + ,optionalIotThingDictRelationDTOS.get().stream().map(IotThingDictRelationDTO::getCode).collect(Collectors.toList()))); + if(CollectionUtil.isEmpty(alarmRuleEntityEntities)){ + return resEnergyAlarm; + //throw new SysException("请在通用告警中先设置告警规则"); + } + //获取所有告警记录 + List alarmReportList = alarmRuleLogDao. + getAlarmReportList(startTs, endTs, param.getThingId(), + alarmRuleEntityEntities.stream().map(AlarmRuleEntityEntity::getThingAttr).collect(Collectors.toList())); + if(CollectionUtil.isEmpty(alarmReportList)){ + return resEnergyAlarm; + //throw new SysException("当前时间段没有相关告警配置属性数据,无法提供告警分析信息"); + } + //告警类型 + // 设备掉线 1 + //超出阈值 0 + List alarmTypes = alarmDictDao.findAllByDictType("alarm_type"); + AtomicLong processeTotal = new AtomicLong(0); + AtomicLong unProcesseTotal = new AtomicLong(0); + List> collect = alarmTypes.stream().map(s -> { + Map tempMap = Maps.newHashMap(); + tempMap.put("ruleType", s.getDictValue()); + tempMap.put("ruleName", s.getDictName()); + //已经处理的次数和未处理的次数---处理状态类型 + // 处理中 1//已处理 2 //待处理 0 + List processedList = alarmReportList.stream().filter(alarmRuleReportDTO -> StringUtils.equals(alarmRuleReportDTO.getStatus(), "2") && StringUtils.equals(alarmRuleReportDTO.getRuleType(), s.getDictValue())).collect(Collectors.toList()); + tempMap.put("processedNum", CollectionUtil.size(processedList)); + processeTotal.getAndAdd(CollectionUtil.size(processedList)); + List unProcessedList = alarmReportList.stream().filter(alarmRuleReportDTO -> !StringUtils.equals(alarmRuleReportDTO.getStatus(), "2") && StringUtils.equals(alarmRuleReportDTO.getRuleType(), s.getDictValue())).collect(Collectors.toList()); + tempMap.put("unProcessedNum", CollectionUtil.size(unProcessedList)); + unProcesseTotal.getAndAdd(CollectionUtil.size(unProcessedList) + CollectionUtil.size(processedList)); + return tempMap; + }).collect(Collectors.toList()); + resEnergyAlarm.setProcessedNum(processeTotal.get()); + resEnergyAlarm.setUnProcessedNum(unProcesseTotal.get()); + resEnergyAlarm.setAlarmAnalysisList(collect); + try { + List keys = Lists.newArrayList(); + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())){ + keys = alarmRuleEntityEntities.stream().map(AlarmRuleEntityEntity::getThingAttr).filter(s -> + Lists.newArrayList("tce_perBuildAreamm","tce_perCapitaBuidmm","tce_perCanteenmm","EEUEmm").contains(s)).collect(Collectors.toList()); + } + //按照月的时间:dateStr 为 yy-MM + if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + keys = alarmRuleEntityEntities.stream().map(AlarmRuleEntityEntity::getThingAttr).filter(s -> + Lists.newArrayList("tce_perBuildAreayy","tce_perCapitaBuidyy","tce_perCanteenyy","EEUEyy").contains(s)).collect(Collectors.toList()); + } + if(CollectionUtil.isNotEmpty(keys)){ + //查询每个属性的最新数据 + List tsKvDTOList = tsKvService.findLatestByCodeAndAttrs(param.getThingCode(), keys, false); + //将bean转为map + List> maps = tsKvDTOList.stream().map(BeanUtil::beanToMap).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(maps)){ + List list = publicOrgLimitService.listAs(Maps.newHashMap(),PublicOrgLimitDTO.class); + for (Map dataMap : maps) { + String attrKey = MapUtil.getStr(dataMap, "attr_key"); + String val = MapUtil.getStr(dataMap, "val"); + getLimitData(dataMap, dateType, attrKey, list, val); + dataMap.put("alarm",0); + } + resEnergyAlarm.setAlarmAnalysisDetails(maps); + } + } + } catch (Exception e) { + log.error("查询失败",e); + throw new SysException("查询失败",e); + } + return resEnergyAlarm; + } + + private static void getLimitData(Map dataMap, String dateType, String attrKey, List list, String val) { + List buildList = Lists.newArrayList(); + if(StringUtils.startsWith(attrKey,"tce_perBuild")){ + buildList = list.stream().filter(s -> StringUtils.equals(s.getIndexName(), "energy_perbulid")).collect(Collectors.toList()); + } + if(StringUtils.startsWith(attrKey,"tce_perCapitaBuid")){ + buildList = list.stream().filter(s -> StringUtils.equals(s.getIndexName(), "energy_perperson")).collect(Collectors.toList()); + } + if(StringUtils.startsWith(attrKey,"EEUE")){ + buildList = list.stream().filter(s -> StringUtils.equals(s.getIndexName(), "energy_utilization_efficiency")).collect(Collectors.toList()); + } + if(StringUtils.startsWith(attrKey,"tce_perCanteen")){ + buildList = list.stream().filter(s -> StringUtils.equals(s.getIndexName(), "energy_percafeteria_person")).collect(Collectors.toList()); + } + + if(CollectionUtil.isNotEmpty(buildList)){ + BigDecimal compareValue = BigDecimal.ZERO; + if(StringUtils.equals(dateType,AttributeTypeEnum.dd.name())){ + BigDecimal monthValue = buildList.get(0).getMonthValue(); + compareValue = compareValue.add(monthValue); + }if(StringUtils.equals(dateType,AttributeTypeEnum.mm.name())){ + BigDecimal yearValue = buildList.get(0).getYearValue(); + compareValue = compareValue.add(yearValue); + } + dataMap.put("limitVal",compareValue); + dataMap.put("unit",buildList.get(0).getUnit()); + if(CompareUtil.compare(compareValue,new BigDecimal(val)) <0 ){ + dataMap.put("alarm",1); + }else{ + dataMap.put("alarm",0); + } + }else{ + dataMap.put("alarm",0); + } + } + + /** + * 获取数据的总和 + * @param dataList + * @return + */ + public BigDecimal calculateSum(List> dataList) { + return dataList.stream() + .map(data -> new BigDecimal(MapUtil.getStr(data,"val","0"))) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + /** + * 计算每条遥测记录的和 + * @param dataAmList 上半程数据 + * @param dataPmList 下半程数据 + */ + private void getTotal(List> dataAmList,List> dataPmList,String dataType){ + if(CollectionUtil.isEmpty(dataAmList) && CollectionUtil.isEmpty(dataPmList) ){ + return; + }else if(CollectionUtil.isEmpty(dataAmList) && CollectionUtil.isNotEmpty(dataPmList)){ + for (int i = 0; i < dataPmList.size(); i++) { + Map dataPmMap = dataPmList.get(i); + Long ts = MapUtil.getLong(dataPmMap, "ts"); + if(StringUtils.equals(dataType,TimeType.DAY.getName())){ + dataPmMap.put("ts",DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(ts).minusDays(1)))); + } + if(StringUtils.equals(dataType,TimeType.MONTH.getName())){ + dataPmMap.put("ts",DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(ts).minusMonths(1)))); + } + } + }else if(CollectionUtil.isNotEmpty(dataAmList) && CollectionUtil.isNotEmpty(dataPmList)){ + if(CollectionUtil.size(dataPmList) > CollectionUtil.size(dataAmList)){ + for (int i = 0; i < dataPmList.size(); i++) { + Map dataPmMap = dataPmList.get(i); + if(dataAmList.size() == i){ + Long ts = MapUtil.getLong(dataPmMap, "ts"); + if(StringUtils.equals(dataType,TimeType.DAY.getName())){ + dataPmMap.put("ts",DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(ts).minusDays(1)))); + } + if(StringUtils.equals(dataType,TimeType.MONTH.getName())){ + dataPmMap.put("ts",DateTimeUtils.parse(DateTimeUtils.getDayStart(DateTimeUtils.parseDateTime(ts).minusMonths(1)))); + } + dataAmList.set(i,dataPmMap); + continue; + } + Map dataMap = dataPmList.get(i); + if(MapUtil.isNotEmpty(dataMap) && MapUtil.isNotEmpty(dataPmMap)){ + String val = MapUtil.getStr(dataMap, "val"); + String pmVal = MapUtil.getStr(dataPmMap, "val"); + val = new BigDecimal(val).add(new BigDecimal(pmVal)).toString(); + dataMap.put("val",val); + } + } + }if(CollectionUtil.size(dataPmList) < CollectionUtil.size(dataAmList)){ + for (int i = 0; i < dataAmList.size(); i++) { + if(dataPmList.size() == i){ + continue; + } + Map dataAmMap = dataAmList.get(i); + Map dataPmMap = dataPmList.get(i); + String val = MapUtil.getStr(dataAmMap, "val"); + String pmVal = MapUtil.getStr(dataPmMap, "val"); + val = new BigDecimal(val).add(new BigDecimal(pmVal)).toString(); + dataAmMap.put("val",val); + } + }else{ + for (int i = 0; i < dataAmList.size(); i++) { + Map dataMap = dataAmList.get(i); + Map dataPmMap = dataPmList.get(i); + String val = MapUtil.getStr(dataMap, "val"); + String pmVal = MapUtil.getStr(dataPmMap, "val"); + val = new BigDecimal(val).add(new BigDecimal(pmVal)).toString(); + dataMap.put("val",val); + } + } + } + } + + /** + * 获取连续时间段 + * @param startMinute + * @param endMinute + * @param timeList + */ + private void getMinuteList(Integer startMinute,Integer endMinute,List timeList){ + while (endMinute >= startMinute){ + timeList.add(startMinute); + startMinute +=1; + } + } + + private List findSummary( + String thingCode, List attrCodes, long startTs, long endTs) { + return tsKvService.findTsKvByCodeAndAttrs(thingCode, attrCodes, startTs, endTs, false); + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageCommonServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageCommonServiceImpl.java new file mode 100644 index 0000000..6974fc5 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageCommonServiceImpl.java @@ -0,0 +1,309 @@ +package com.thing.carbon.energyrepory.service.impl; + +import com.google.common.collect.Maps; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbon.energyrepory.service.EnergyUsageCommonService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.device.source.dto.IotThingSourceDTO; +import com.thing.device.source.service.IotThingSourceService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/10/18 9:50 + * Description: 用能数据分析通用服务实现 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class EnergyUsageCommonServiceImpl implements EnergyUsageCommonService { + + private final IotThingSourceService iotThingSourceService; + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + public final CarbonEnergyDictRelationService carbonEnergyDictRelationService; + public final ThingManageContextService thingManageContextService; + public final TsKvService tsKvService; + private static final ExecutorService EXECUTOR = Executors.newFixedThreadPool(10); + + /** + * 查询当前物及其兄弟物、子物的数据源(物)的属性,再获取这些属性的能源品种 + * 保证这些能源品种能覆盖到足够大的应用范围 + */ + @Override + public List energyVarietyList(CarbonEnergyVarietyReqDTO request) { + List thingIds = getSubOrBrotherThingIds(request.getThingId(), request.getRootId(), request.getRootThingId()); + thingIds.add(request.getThingId()); + List iotThingSources = iotThingSourceService.findAllByFromIdAndThingIdAndConfigType(thingIds,null,null,Collections.singletonList(request.getRootId())); + if (CollectionUtils.isEmpty(iotThingSources)) { + throw new SysException("无匹配数据源"); + } + Set attrCodes = + iotThingSources.stream() + .map(IotThingSourceDTO::getThingAttrCode) + .collect(Collectors.toSet()); + List energyDictList = + carbonEnergyDictRelationService.getByAttrAndVariety(attrCodes,null, null); + List energyVarietyIds = + energyDictList.stream() + .map(CarbonEnergyDictRelationEntity::getEnergyVarietyId) + .distinct() + .collect(Collectors.toList()); + List energyVarietyEntities = + carbonEnergyVarietyService.selectByIds(energyVarietyIds); + return ConvertUtils.sourceToTarget(energyVarietyEntities, CarbonEnergyVarietyDTO.class); + } + + /** + * 多个物自身及其子物,在指定属性类型和能源品种下的用能属性
+ * + * @param thingIds 物Id列表 + * @param energyVarietyId 能源品种Id + * @param attrType 属性类型 + * @return list + */ + public List> getThingsEnergyDictList( + List thingIds, Long rootId, Long energyVarietyId, String attrType) { + + Optional> optionalList = thingManageContextService.findEntityAllById(thingIds); + Map thingMap = optionalList.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingEntityDTO::getId, Function.identity())); + + Long tenantCode = UserContext.getRealTenantCode(); + Map> futures = new HashMap<>(); + for (Long thingId : thingIds) { + Future future = + EXECUTOR.submit(() -> getThingEnergyDict(thingId, rootId, energyVarietyId, attrType, tenantCode)); + futures.put(thingId, future); + } + + List> result = new ArrayList<>(); + futures.forEach( + (thingId, future) -> { + try { + ThingEnergyDictData energyDict = future.get(); + IotThingEntityDTO thingEntity = thingMap.get(thingId); + if (Objects.nonNull(energyDict)) { + result.add(Pair.of(thingEntity, energyDict)); + } + } catch (Exception ignore) { + } + }); + + return result; + } + + /** + * 当前物以及子物,在指定属性类型和能源品种下的用能属性 + * + * @param thingId 物Id + * @param energyVarietyId 能源品种id + * @param attrType 属性类型 + * @return 用能属性列表 + */ + public List getThingEnergyDictList( + Long thingId , Long rootId, Long energyVarietyId, String attrType, Long tenantCode) { + List thingSources = iotThingSourceService.findAllByFromIdAndThingIdAndConfigType(thingId,null,null,rootId, tenantCode); + if (CollectionUtils.isEmpty(thingSources)) { + log.debug( + "========>> 数据源为空, thingId: {}, rootId: {}, tenantCode: {}", + thingId, + rootId, + tenantCode); + return Collections.emptyList(); + } + thingSourceAdapt(thingSources, attrType); + Set attrCodes = + thingSources.stream() + .map(IotThingSourceDTO::getThingAttrCode) + .collect(Collectors.toSet()); + + List energyDictEntities = + carbonEnergyDictRelationService.getByAttrAndVariety( + attrCodes, Collections.singletonList(energyVarietyId), attrType); + + Map attrGroup = + energyDictEntities.stream() + .collect( + Collectors.toMap( + CarbonEnergyDictRelationEntity::getAttrCode, + Function.identity())); + + // 每个物的用能属性如果存在,则生成一条CarbonEnergyDictRelationDTO + List energyDictDtoList = new ArrayList<>(); + for (IotThingSourceDTO thingSource : thingSources) { + String thingAttrCode = thingSource.getThingAttrCode(); + if (!attrGroup.containsKey(thingAttrCode)) { + continue; + } + CarbonEnergyDictRelationEntity entity = attrGroup.get(thingAttrCode); + ThingEnergyDictData energyDict = + new ThingEnergyDictData() + .setThingId(thingSource.getThingId()) + .setThingName(thingSource.getThingName()) + .setThingCode(thingSource.getThingCode()) + .setEnergyVarietyId(entity.getEnergyVarietyId()) + .setAttrCode(entity.getAttrCode()) + .setAttrName(entity.getAttrName()) + .setAttrType(entity.getAttrType()) + ; + energyDictDtoList.add(energyDict); + } + + return energyDictDtoList; + } + + /** + * 该方法用来适配用量分析设置中的“年、日”数据源 + * 用量分析设置中: + * 1. 年属性都是mm结尾的,此处如果要查询yy结尾的用量属性是查询不到的,因此做一层替换 + * 2. 日属性都是am结尾的,此处如果要查询hh结尾的用量属性是查询不到的,因此做一层替换 + */ + private void thingSourceAdapt(List thingSources, String attrType) { + AttributeTypeEnum attrTypeEnum = AttributeTypeEnum.match(attrType); + switch (attrTypeEnum) { + case yy -> doAdapter(thingSources, AttributeTypeEnum.yy); + case hh -> doAdapter(thingSources, AttributeTypeEnum.hh); + default -> { + } + // anything else ? + } + } + + private void doAdapter(List thingSources, AttributeTypeEnum attrTypeEnum) { + // 2023古早版本 + List source = + thingSources.stream() + .filter(s -> s.getThingAttrCode().endsWith(attrTypeEnum.name())) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(source)) { + thingSources.clear(); + thingSources.addAll(source); + return; + } + + // 2024年中版本 + source = + thingSources.stream() + .filter(s -> Objects.nonNull(s.getThingAttrCodeType())) + .filter(s -> s.getThingAttrCodeType().equals(attrTypeEnum.name())) + .peek(s -> s.setThingAttrCode(s.getThingAttrCode() + s.getThingAttrCodeType())) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(source)) { + thingSources.clear(); + thingSources.addAll(source); + return; + } + + // 通用的适配 + source = + thingSources.stream() + .filter( + s -> { + assert attrTypeEnum.previous() != null; + return s.getThingAttrCode() + .endsWith(attrTypeEnum.previous().name()); + }) + .peek( + s -> { + String previousCode = s.getThingAttrCode(); + String nextCode = + previousCode.substring(0, previousCode.length() - 2) + + attrTypeEnum.name(); + s.setThingAttrCode(nextCode); + }) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(source)) { + thingSources.clear(); + thingSources.addAll(source); + } + } + + public ThingEnergyDictData getThingEnergyDict( + Long thingId, Long rootId, Long energyVarietyId, String attrType, Long tenantCode) { + List thingEnergyDictList = + getThingEnergyDictList(thingId, rootId, energyVarietyId, attrType, tenantCode); + if (thingEnergyDictList.isEmpty()) { + return null; + } + thingEnergyDictList.sort(Comparator.comparing(ThingEnergyDictData::getAttrCode)); + ThingEnergyDictData energyDict = thingEnergyDictList.get(0); + Optional dictList = thingManageContextService.findDictRelationByEntityIdAndCode(thingId, energyDict.getAttrCode()); + dictList.ifPresent(iotThingDictRelationDTO -> energyDict.setAttrUnit(iotThingDictRelationDTO.getUnit())); + return energyDict; + } + + /** + * 查询一个物某属性的值 + */ + public List findThingAttrTsKv( + String thingCode, List attrCodes, long startTs, long endTs) { + + return tsKvService.findTsKvByCodeAndAttrs(thingCode, attrCodes, startTs, endTs, true); + } + + /** + * 查询不同物,不同属性的tsKv + * + * @param thingEnergyDictList 物的用能属性列表 + * @param startTs 开始时间 + * @param endTs 结束时间 + * @return tsKv + */ + public List findThingsAttrsTsKv( + List thingEnergyDictList, long startTs, long endTs) { + Map> codeAndKey = + thingEnergyDictList.stream() + .filter(Objects::nonNull) + .collect(Collectors.groupingBy( + ThingEnergyDictData::getThingCode, + Collectors.mapping( + ThingEnergyDictData::getAttrCode, + Collectors.toList() + ) + )); + Map> params = Maps.newHashMap(); + codeAndKey.forEach((k, v) -> params.put(k,Collections.unmodifiableCollection(v))); + return tsKvService.findTsKvByMultiMap(params, startTs, endTs, true); + } + + /** + * 获取子级或者同级的物id + * + * @param thingId 当前物的id + * @return 如果存在子节点,则返回子节点; 否则返回兄弟节点 + */ + public List getSubOrBrotherThingIds(Long thingId, Long rootId, Long rootThingId) { + Optional> optionalList = thingManageContextService.findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(rootId, thingId, rootThingId); + List thingIds = optionalList.orElseGet(Collections::emptyList).stream().map(IotThingRelationDetailDTO::getToId).collect(Collectors.toList()); + if (thingIds.isEmpty()) { + Optional> optionalIotThingRelationDetailDTOS = thingManageContextService.findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(rootId, thingId, rootThingId); + thingIds = optionalIotThingRelationDetailDTOS.orElseGet(Collections::emptyList).stream().map(IotThingRelationDetailDTO::getToId).collect(Collectors.toList()); + } + return thingIds; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageFlowServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageFlowServiceImpl.java new file mode 100644 index 0000000..4e9ef21 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageFlowServiceImpl.java @@ -0,0 +1,290 @@ +package com.thing.carbon.energyrepory.service.impl; + + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageSummaryDTO; +import com.thing.carbon.energyrepory.service.EnergyUsageCommonService; +import com.thing.carbon.energyrepory.service.EnergyUsageFlowService; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.common.core.utils.params.SortTreeNode; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/10/16 13:36 + * Description: 用能概况服务层 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class EnergyUsageFlowServiceImpl implements EnergyUsageFlowService { + + private final EnergyUsageCommonService commonService; + private final ThingManageContextService thingManageContextService; + + @Override + public List energyVarietyList(CarbonEnergyVarietyReqDTO request) { + return commonService.energyVarietyList(request); + } + + /** + * 能流图特殊情况列举 + * 设父物的用量属性为A29,值为100kwh + * 子物1:用量属性A29, 值为40kwh + * 子物2:用量属性A29, 值为50kwh + * 子物3:用量属性A30, 值为40kwh + * 子物按照其用量属性进行汇总: 40 + 50 + 40 = 130kwh + * 损耗: 此例中为 100 - 130 = -30kwh + * 出现负数损耗说明配置人员配错了信息,此例就是配置信息存在错误 + * + * @param request 能耗数据通用请求 + * @return 响应 + */ + @Override + public EnergyUsageSummaryDTO getFlowSummary(EnergyUsageReqDTO request) { + Long thingId = request.getThingIds().get(0); + + // 时间转换 + long startTs = DateTimeUtils.dateToTimestamp(request.getBeginTime()); + long endTs = DateTimeUtils.dateToTimestamp(request.getEndTime()); + + EnergyUsageSummaryDTO fatherSummary; + List subSummaryList; + + Optional> rootDetailAllByRootId = thingManageContextService.findRootDetailSortTreeList(request.getRootId(),request.getRootThingId()); + ThingSortTreeDTO tree = findSubTree(rootDetailAllByRootId.orElseGet(Collections::emptyList), thingId, request.getRootThingId()); + if (Objects.nonNull(tree) && !tree.getChildren().isEmpty()) { + resetDepth(tree); + fatherSummary = fatherInfo(thingId, request.getEnergyVarietyId(), request.getAttrType(), request.getRootId(), startTs, endTs); + if (Objects.isNull(fatherSummary)) { + return null; + } + fatherSummary.setDepth(tree.getDepth()); + fatherSummary.setChildren(tree.getChildren()); + buildSummaryTree(fatherSummary, request, startTs, endTs); + return fatherSummary; + } else { + // 查询父物Id + Optional optional = thingManageContextService.findRootDetailParentNodeByRootIdAndToIdAndRootThingId(request.getRootId(), thingId, null); + Long pid = optional.get().getFromId(); + fatherSummary = fatherInfo(pid, request.getEnergyVarietyId(), request.getAttrType(), request.getRootId(), startTs, endTs); + if (Objects.isNull(fatherSummary)) { + return null; + } + fatherSummary.setDepth(1); + // 查询兄弟物Id + Optional> optionalList = thingManageContextService + .findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(request.getRootId(), thingId, request.getRootThingId()); + List brotherIds = optionalList.orElseGet(Collections::emptyList).stream().map(IotThingRelationDetailDTO::getToId).toList(); + subSummaryList = summary(fatherSummary, brotherIds, request.getEnergyVarietyId(), request.getAttrType(), request.getRootId(), startTs, endTs); + subSummaryList.forEach(son -> son.setDepth(2)); + } + if(!CollectionUtils.isEmpty(subSummaryList)){ + subSummaryList.sort(null); + fatherSummary.setChildrenUsageSummary(subSummaryList); + } + + return fatherSummary; + } + + private ThingSortTreeDTO findSubTree(List treeNodes, Long entityId, Long rootThingId) { + for (ThingSortTreeDTO treeNode : treeNodes) { + if (Objects.equals(treeNode.getEntityId(), entityId)) { + if(Objects.nonNull(rootThingId) && !Objects.equals(treeNode.getRootThingId(), rootThingId)){ + continue; + } + return treeNode; + } + } + for (ThingSortTreeDTO treeNode : treeNodes) { + List children = treeNode.getChildren(); + ThingSortTreeDTO subTree = findSubTree(children, entityId, rootThingId); + if (Objects.nonNull(subTree)) { + return subTree; + } + } + return null; + } + + /** + * 构建能流图(树) 将多余的字段置空,减少对前端的误导 + * @param fatherSummary 父级汇总数据 + * @param request 请求参数 + * @param startTs 开始时间 + * @param endTs 结束时间 + */ + private void buildSummaryTree( + EnergyUsageSummaryDTO fatherSummary, + EnergyUsageReqDTO request, + long startTs, + long endTs) { + if (fatherSummary.getDepth() >= request.getDepth()) { + return; + } + List children = fatherSummary.getChildren(); + if (children.isEmpty() || StringUtils.isBlank(fatherSummary.getTotalValue())) { + return; + } + ThingSortTreeDTO child = children.get(0); + Map childMap = children.stream().collect(Collectors.toMap(ThingSortTreeDTO::getEntityId, Function.identity())); + List childrenIds = children.stream().map(ThingSortTreeDTO::getEntityId).collect(Collectors.toList()); + List subSummaryList = + summary(fatherSummary, childrenIds, request.getEnergyVarietyId(), request.getAttrType(), request.getRootId(), startTs, endTs); + List validSubSummary = + subSummaryList.stream() + .filter(s -> Objects.nonNull(s) && Objects.nonNull(s.getTotalValue())) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(validSubSummary)) { + validSubSummary.sort(null); + validSubSummary.forEach(s -> s.setDepth(child.getDepth())); + fatherSummary.setChildrenUsageSummary(validSubSummary); + } + for (EnergyUsageSummaryDTO subTree : validSubSummary) { + ThingSortTreeDTO currentChild = childMap.get(subTree.getEntityId()); + if (Objects.isNull(currentChild)) { + continue; + } + subTree.setChildren(currentChild.getChildren()); + buildSummaryTree(subTree, request, startTs, endTs); + subTree.setChildren(null); + } + fatherSummary.setChildren(null); + } + + private EnergyUsageSummaryDTO fatherInfo( + Long pid, Long energyVarietyId, String attrType, Long rootId, long startTs, long endTs) { + ThingEnergyDictData energyDict = + commonService.getThingEnergyDict(pid, rootId, energyVarietyId, attrType, UserContext.getTenantCode()); + if (Objects.isNull(energyDict)) { + return null; + } + List tsKvMapList = + commonService.findThingAttrTsKv( + energyDict.getThingCode(), + Collections.singletonList(energyDict.getAttrCode()), + startTs, + endTs); + double total = + tsKvMapList.stream() + .map(map -> Double.parseDouble(map.getVal())) + .reduce(Double::sum) + .orElse(0D); + + EnergyUsageSummaryDTO summary = new EnergyUsageSummaryDTO() + .setAttrName(energyDict.getAttrName()) + .setAttrCode(energyDict.getAttrCode()) + .setAttrUnit(energyDict.getAttrUnit()) + .setTotalValue(String.valueOf(total)) + ; + summary.setEntityId(pid); + summary.setEntityName(energyDict.getThingName()); + summary.setEntityCode(energyDict.getThingCode()); + return summary; + } + + /** + * 汇总数据 + * + * @param fatherSummary 父物汇总数据 + * @param subIds 子物id + * @param energyVarietyId 能源品种id + * @param attrType 属性类型 + * @param startTs 开始时间 + * @param endTs 结束时间 + * @return 汇总数据 + */ + private List summary( + EnergyUsageSummaryDTO fatherSummary, + List subIds, + Long energyVarietyId, + String attrType, + Long rootId, + long startTs, + long endTs) { + // 1. 子物信息和数据 + List> thingsEnergyDictList = + commonService.getThingsEnergyDictList(subIds, rootId, energyVarietyId, attrType); + List subEnergyDictList = + thingsEnergyDictList.stream().map(Pair::getRight).collect(Collectors.toList()); + List subTsKvMapList = + commonService.findThingsAttrsTsKv(subEnergyDictList, startTs, endTs); + Map thingTotalValMap = + subTsKvMapList.stream() + .collect( + Collectors.groupingBy( + map -> map.getThingCode() + ":" + map.getAttrKey(), + Collectors.summingDouble(item -> Double.parseDouble(item.getVal())))); + double subTotal = thingTotalValMap.values().stream().reduce(Double::sum).orElse(0D); + + // 2. 计算损耗 + double total = Double.parseDouble(fatherSummary.getTotalValue()); + double loss = total - subTotal; + + // 3. 初始化汇总数据 + List summaryData = + subEnergyDictList.stream() + .map(EnergyUsageSummaryDTO::convertFromThingEnergyDict) + .collect(Collectors.toList()); + + // 4. 计算占比 + for (EnergyUsageSummaryDTO item : summaryData) { + if (Objects.isNull(item)) { + continue; + } + String thingAttr = item.getEntityCode() + ":" + item.getAttrCode(); + Double totalValue = thingTotalValMap.get(thingAttr); + if (Objects.isNull(totalValue) || subTotal == 0) { + continue; + } + double percent = totalValue / total; + item.setTotalValue(String.format("%.2f", totalValue)); + item.setPercent(String.format("%.2f", percent * 100) + "%"); + } + + // 5. 损耗数据加入到汇总数据列表 + if (loss > 0 && subTotal != 0) { + double lossPercent = loss / total; + EnergyUsageSummaryDTO lossData = + + new EnergyUsageSummaryDTO() + .setAttrName(fatherSummary.getAttrName()) + .setAttrCode(fatherSummary.getAttrCode()) + .setAttrUnit(fatherSummary.getAttrUnit()) + .setTotalValue(String.format("%.2f", loss)) + .setPercent(String.format("%.2f", lossPercent * 100) + "%"); + lossData.setEntityName(fatherSummary.getEntityName() + EnergyUsageSummaryDTO.LOSS); + summaryData.add(lossData); + } + return summaryData.stream().filter(Objects::nonNull).collect(Collectors.toList()); + } + + /** 重新设定树的深度 */ + private void resetDepth(ThingSortTreeDTO tree) { + int baseDepth = tree.getDepth(); + TreeUtils.recursiveOperation( + tree, + nd -> { + SortTreeNode node = ((SortTreeNode) nd); + node.setDepth(node.getDepth() - baseDepth + 1); + }); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageOverViewServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageOverViewServiceImpl.java new file mode 100644 index 0000000..7bee219 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageOverViewServiceImpl.java @@ -0,0 +1,355 @@ +package com.thing.carbon.energyrepory.service.impl; + +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.dto.CarbonEnergyVarietyReqDTO; +import com.thing.carbon.energyrepory.dto.AnalysisThingAttrValueRespDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageReqDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageSummaryDTO; +import com.thing.carbon.energyrepory.service.EnergyUsageCommonService; +import com.thing.carbon.energyrepory.service.EnergyUsageOverViewService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Author: SiYang + * Date: 2023/10/16 13:36 + * Description: 用能概况服务层 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class EnergyUsageOverViewServiceImpl implements EnergyUsageOverViewService { + + private final EnergyUsageCommonService commonService; + private final ThingManageContextService thingManageContextService; + + @Override + public List energyVarietyList(CarbonEnergyVarietyReqDTO request) { + return commonService.energyVarietyList(request); + } + + /** + * 查询物属性时比数据
+ * 混淆情况讨论:
+ * 时间 9月 10月
+ * 用量属性1 A29(有数据) A29(无数据)
+ * 用量属性2 A30(无数据) A30(有数据)
+ * 问题:采用哪个用量属性?
+ * 答: 按attrCode排序,选择排在前面的即可,放在此例则为A29
+ * + * @param request 参数 + * @return map + */ + @Override + public Map timeCompareTsKv(EnergyUsageReqDTO request) { + Long thingId = request.getThingIds().get(0); + String attrType = request.getAttrType(); + String beginTime = request.getBeginTime(); + + // 时间转换 + long startTimestamp = DateTimeUtils.dateToTimestamp(beginTime); + long endTimestamp = DateTimeUtils.dateToTimestamp(request.getEndTime()); + + AttributeTypeEnum attrTypeEnum = AttributeTypeEnum.match(attrType); + // 获取当前时间阶段的范围 + List currentTimestampList = + attrTypeEnum.getTimestampList(DateTimeUtils.parseDateTime(beginTime)); + // 获取上一个同级时间阶段的范围 + List previousTimestampList = + attrTypeEnum.getPreviousTimestampList(DateTimeUtils.parseDateTime(beginTime)); + Long previousStartTime = previousTimestampList.get(0); + Long previousEndTime = previousTimestampList.get(previousTimestampList.size() - 1); + + // 查询物的用量属性 + ThingEnergyDictData energyDict = commonService.getThingEnergyDict( + thingId, request.getRootId(), request.getEnergyVarietyId(), request.getAttrType(), UserContext.getTenantCode()); + + Map resultMap = new HashMap<>(); + if (Objects.isNull(energyDict)) { + putEmptyDataInTimeGroup( + resultMap, thingId, currentTimestampList, previousTimestampList); + return resultMap; + } + + // 查询当前物信息: 由于energyDict可能是子数据源的信息,因此这里需要查询当前物本身的信息作为响应结果的物信息 + IotThingEntityInfoDTO iotThingViewDTO = thingManageContextService.findEntityById(thingId).orElseThrow(() -> new SysException("当前物实体不存在")); + // 初始化响应数据 + AnalysisThingAttrValueRespDTO current = + AnalysisThingAttrValueRespDTO.convertFromThingEnergyDict(energyDict); + current.setThingAttrCode(energyDict.getAttrCode()); + current.setThingCode(iotThingViewDTO.getCode()); + current.setThingName(iotThingViewDTO.getName()); + AnalysisThingAttrValueRespDTO previous = new AnalysisThingAttrValueRespDTO(); + BeanUtils.copyProperties(current, previous); + + // 查询物的用能属性对应的值 + List currentTsKvMapList = + commonService.findThingAttrTsKv( + energyDict.getThingCode(), + Collections.singletonList(energyDict.getAttrCode()), + startTimestamp, + endTimestamp); + List previousTsKvMapList = + commonService.findThingAttrTsKv( + energyDict.getThingCode(), + Collections.singletonList(energyDict.getAttrCode()), + previousStartTime, + previousEndTime); + + // 填充数据 + fillTsKv(current, currentTimestampList, currentTsKvMapList); + fillTsKv(previous, previousTimestampList, previousTsKvMapList); + + resultMap.put("current", current); + resultMap.put("previous", previous); + + return resultMap; + } + + private void putEmptyDataInTimeGroup( + Map resultMap, + Long thingId, + List currentTimestampList, + List previousTimestampList) { + IotThingEntityInfoDTO iotThingViewDTO = thingManageContextService.findEntityById(thingId).orElseThrow(() -> new SysException("当前物实体不存在")); + AnalysisThingAttrValueRespDTO current = + new AnalysisThingAttrValueRespDTO() + .setThingId(thingId) + .setThingName(iotThingViewDTO.getName()) + .setThingCode(iotThingViewDTO.getCode()) + ; + AnalysisThingAttrValueRespDTO previous = new AnalysisThingAttrValueRespDTO(); + BeanUtils.copyProperties(current, previous); + fillTsKv(current, currentTimestampList, Collections.emptyList()); + fillTsKv(previous, previousTimestampList, Collections.emptyList()); + resultMap.put("current", current); + resultMap.put("previous", previous); + } + + /** + * 查询给定物的子节点或兄弟节点属性汇总值并计算占比 + */ + @Override + public List subOrBrotherSummary(EnergyUsageReqDTO request) { + Long thingId = request.getThingIds().get(0); + + // 时间转换 + long startTimestamp = DateTimeUtils.dateToTimestamp(request.getBeginTime()); + long endTimestamp = DateTimeUtils.dateToTimestamp(request.getEndTime()); + + List thingIds = commonService.getSubOrBrotherThingIds(thingId, request.getRootId(), request.getRootThingId()); + if (thingIds.isEmpty()) { + return Collections.emptyList(); + } + + List> thingDictPairList = + commonService.getThingsEnergyDictList( + thingIds, request.getRootId(), request.getEnergyVarietyId(), request.getAttrType()); + if (thingDictPairList.isEmpty()) { + return Collections.emptyList(); + } + List thingDictList = thingDictPairList.stream().map(Pair::getRight).collect(Collectors.toList()); + List tsKvMapList = commonService.findThingsAttrsTsKv(thingDictList, startTimestamp, endTimestamp); + + // 初始化汇总数据 + List summaryData = + thingDictList.stream() + .map(EnergyUsageSummaryDTO::convertFromThingEnergyDict) + .collect(Collectors.toList()); + + // 汇总 + Map thingTotalValMap = + tsKvMapList.stream() + .collect( + Collectors.groupingBy( + map -> map.getThingCode() + ":" + map.getAttrKey(), + Collectors.summingDouble(item -> Double.parseDouble(item.getVal())))); + Double total = thingTotalValMap.values().stream().reduce(Double::sum).orElse(null); + + // 基于汇总值计算占比 + for (EnergyUsageSummaryDTO item : summaryData) { + String thingAttr = item.getEntityCode() + ":" + item.getAttrCode(); + Double totalValue = thingTotalValMap.get(thingAttr); + if (Objects.isNull(totalValue) || Objects.isNull(total)) { + continue; + } + double percent = totalValue / total; + item.setTotalValue(String.format("%.2f", totalValue)); + item.setPercent(String.format("%.2f", percent * 100) + "%"); + } + + summaryData.sort( + (a, b) -> { + double current = Double.parseDouble(Optional.ofNullable(a.getTotalValue()).orElse("0")); + double otherValue = Double.parseDouble(Optional.ofNullable(b.getTotalValue()).orElse("0")); + return current - otherValue > 0 ? -1 : current == otherValue ? 0 : 1; + }); + + // 将子数据源的物信息,修改为父数据源的物信息 + changeThingInfoForSummary(summaryData, thingDictPairList); + + return summaryData; + } + + @Override + public List brotherList(Long thingId, Long rootId, Long rootThingId) { + + List relationDetailDTOS = + thingManageContextService.findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(rootId, thingId, rootThingId).orElseGet(Collections::emptyList); + Optional> optionalList = thingManageContextService.findEntityAllById(relationDetailDTOS.stream().map(IotThingRelationDetailDTO::getToId).collect(Collectors.toList())); + return optionalList.orElseGet(Collections::emptyList); + } + + @Override + public List brotherCompareTsKv(EnergyUsageReqDTO request) { + // 时间转换 + long startTimestamp = DateTimeUtils.dateToTimestamp(request.getBeginTime()); + long endTimestamp = DateTimeUtils.dateToTimestamp(request.getEndTime()); + + // 查询物的用能属性 + List> thingDictPairList = + commonService.getThingsEnergyDictList( + request.getThingIds(), request.getRootId(), request.getEnergyVarietyId(), request.getAttrType()); + if (thingDictPairList.isEmpty()) { + return Collections.emptyList(); + } + + List thingDictList = thingDictPairList.stream().map(Pair::getRight).collect(Collectors.toList()); + List tsKvMapList = commonService.findThingsAttrsTsKv(thingDictList, startTimestamp, endTimestamp); + + // 初始化响应对象 + List thingAttrValList = + thingDictList.stream() + .map(AnalysisThingAttrValueRespDTO::convertFromThingEnergyDict) + .collect(Collectors.toList()); + + Map> tsKvGroup = + tsKvMapList.stream() + .collect( + Collectors.groupingBy( + map -> map.getThingCode() + ":" + map.getAttrKey())); + AttributeTypeEnum attrTypeEnum = AttributeTypeEnum.match(request.getAttrType()); + // 获取当前时间阶段的范围 + List timeList = + attrTypeEnum.getTimestampList(DateTimeUtils.parseDateTime(request.getBeginTime())); + + // 填充tsKv + for (AnalysisThingAttrValueRespDTO thingAttr : thingAttrValList) { + String thingAttrKey = thingAttr.getThingCode() + ":" + thingAttr.getThingAttrCode(); + List tsKvs = tsKvGroup.get(thingAttrKey); + fillTsKv(thingAttr, timeList, tsKvs); + } + + changeThingInfoForBrotherCompare(thingAttrValList, thingDictPairList); + + return thingAttrValList; + } + + /** + * 修改汇总数据中的物信息,将子物信息设置为父物信息 + * + * @param summaryData 物的用能属性列表 + * @param thingDictPairList 物及其(子)数据源的pair列表 + */ + private void changeThingInfoForSummary( + List summaryData, + List> thingDictPairList) { + summaryData.forEach( + s -> { + Pair pair = + thingDictPairList.stream() + .filter( + p -> { + ThingEnergyDictData dict = p.getRight(); + return Objects.equals( + s.getEntityCode(), + dict.getThingCode()) + && Objects.equals( + s.getAttrCode(), + dict.getAttrCode()); + }) + .findFirst() + .orElse(null); + if (Objects.isNull(pair)) { + return; + } + IotThingEntityDTO thing = pair.getLeft(); + s.setEntityId(thing.getId()); + s.setEntityCode(thing.getCode()); + s.setEntityName(thing.getName()); + }); + } + + private void changeThingInfoForBrotherCompare( + List analysisThingAttrList, + List> thingDictPairList) { + analysisThingAttrList.forEach( + s -> { + Pair pair = + thingDictPairList.stream() + .filter( + p -> { + ThingEnergyDictData dict = p.getRight(); + return Objects.equals( + s.getThingCode(), + dict.getThingCode()) + && Objects.equals( + s.getThingAttrCode(), + dict.getAttrCode()); + }) + .findFirst() + .orElse(null); + if (Objects.isNull(pair)) { + return; + } + IotThingEntityDTO thing = pair.getLeft(); + s.setThingId(thing.getId()); + s.setThingCode(thing.getCode()); + s.setThingName(thing.getName()); + }); + } + + /** + * 填充属性在指定时间范围内的值,如果值不存在则填充null,保证每个时间点的坑位都被占据了 + * + * @param thingAttr 物属性关系 + * @param tsList 时间范围 + * @param tsKvMapList tsKv + */ + private void fillTsKv( + AnalysisThingAttrValueRespDTO thingAttr, + List tsList, + List tsKvMapList) { + + Map attrValues = thingAttr.initAttrValues(); + List nonNullMap = + Optional.ofNullable(tsKvMapList).orElse(new ArrayList<>()); + Map tsKvMap = + nonNullMap.stream() + .collect( + Collectors.toMap( + TsKvDTO::getTs, + TsKvDTO::getVal, + (v1, v2) -> v2)); + for (Long ts : tsList) { + attrValues.put(ts, tsKvMap.get(ts)); + } + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageServiceImpl.java new file mode 100644 index 0000000..b5d3699 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/service/impl/EnergyUsageServiceImpl.java @@ -0,0 +1,661 @@ +package com.thing.carbon.energyrepory.service.impl; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.dto.CarbonEnergyVarietyDTO; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbon.energyrepory.dto.AnalysisDataReqNewDTO; +import com.thing.carbon.energyrepory.dto.EnergyUsageLabelData; +import com.thing.carbon.energyrepory.dto.EnergyUsageStatisticRequest; +import com.thing.carbon.energyrepory.excel.EnergyUsageStatisticExcel; +import com.thing.carbon.energyrepory.excel.EnergyUsageStatisticVerticalExcel; +import com.thing.carbon.energyrepory.service.EnergyUsageService; +import com.thing.carbon.energyrepory.thread.EnergyUsageExecutor; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.utils.export.ExcelEntityGenerator; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.data.tskv.TsKvEntity; +import com.thing.common.tskv.service.TsKvService; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.ThingEnergyDictData; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; +import com.thing.device.menu.mapper.IotThingMenuConfigMapper; +import com.thing.device.source.dto.IotThingSourceDTO; +import com.thing.device.source.mapper.IotThingSourceMapper; +import com.thing.device.source.service.IotThingSourceService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class EnergyUsageServiceImpl implements EnergyUsageService { + + private final EnergyUsageExecutor energyUsageExecutor; + private final IotThingSourceService iotThingSourceService; + private final CarbonEnergyVarietyService carbonEnergyVarietyService; + private final CarbonEnergyDictRelationService carbonEnergyDictRelationService; + private final TsKvService tsKvService; + private final ThingManageContextService thingManageContextService; + private final IotThingRelationRootService thingRelationRootService; + @Autowired + private IotThingMenuConfigMapper iotThingMenuConfigMapper; + + @Autowired + private IotThingSourceMapper iotThingSourceMapper; + + @Override + public List getThingEnergyVarieties(Long rootId, List thingIds, String configType) { + List thingSources = getThingSourceList(rootId, thingIds, configType); + if (CollectionUtils.isEmpty(thingSources)) { + return Collections.emptyList(); + } + Set attrCodeSet = + thingSources.stream() + .map(IotThingSourceDTO::getThingAttrCode) + .collect(Collectors.toSet()); + return carbonEnergyDictRelationService.findEnergyVarietyByCodes(attrCodeSet); + } + + @Override + public List usageStatisticReport(EnergyUsageStatisticRequest request) { + List energyVarietyIds = request.getEnergyVarietyIds(); + if (CollectionUtils.isEmpty(energyVarietyIds)) { + return Collections.emptyList(); + } + if (request.isAnyTime()) { + request.setAttrType("hh"); + } + // 查询出本次报表所需使用的能源和用量属性 + List varietyIds = energyVarietyIds.stream().map(Long::parseLong).collect(Collectors.toList()); + Map varietyMap = + carbonEnergyVarietyService.selectByIds(varietyIds).stream() + .collect( + Collectors.toMap( + CarbonEnergyVarietyEntity::getId, Function.identity())); + + List energyDictList = + carbonEnergyDictRelationService.getByAttrAndVariety(null, varietyIds, request.getAttrType()); + if (CollectionUtils.isEmpty(energyDictList)) { + return Collections.emptyList(); + } + Map energyDictMap = + energyDictList.stream() + .collect( + Collectors.toMap( + CarbonEnergyDictRelationEntity::getAttrCode, + CarbonEnergyDictRelationEntity::getEnergyVarietyId)); + Set targetAttrSet = energyDictMap.keySet(); + + // 查询数据源,按目标属性过滤,并按物id分组 + List thingIds = request.getThingIds().stream().map(Long::parseLong).collect(Collectors.toList()); + + Optional> optionalList = thingManageContextService.findEntityAllById(thingIds); + + Map thingMap = optionalList.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingEntityDTO::getId, Function.identity())); + List thingSourceList = + getThingSourceList(request.getRootId(), thingIds, request.getConfigType(), targetAttrSet); + + Map> sourceGroup; + Map energyDictMapNew = new HashMap<>(); + if (request.isAnyTime()) { + for (IotThingSourceDTO iotThingSourceEntity : thingSourceList) { + iotThingSourceEntity.setThingAttrCode(iotThingSourceEntity.getThingAttrCode().substring(0, iotThingSourceEntity.getThingAttrCode().length() - 2) + "am"); + iotThingSourceEntity.setThingAttrName(iotThingSourceEntity.getThingAttrName().replace("时", "刻")); + } + for (Map.Entry entry : energyDictMap.entrySet()) { + energyDictMapNew.put(entry.getKey().substring(0, entry.getKey().length() - 2) + "am", entry.getValue()); + } + request.setAttrType("am"); + sourceGroup = + thingSourceList.stream() + .collect(Collectors.groupingBy(IotThingSourceDTO::getFromId)); + } else { + for (Map.Entry entry : energyDictMap.entrySet()) { + energyDictMapNew.put(entry.getKey(), entry.getValue()); + } + sourceGroup = + thingSourceList.stream() + .filter(s -> targetAttrSet.contains(s.getThingAttrCode())) + .collect(Collectors.groupingBy(IotThingSourceDTO::getFromId)); + } + + // 一个物可能有多个子数据源,且都是当前指定的用量属性, 对每个物进行异步生成一批用能属性数据 + List result = + energyUsageExecutor.loopMapToExecute( + sourceGroup, handleUsageStatisticReport(energyDictMapNew, varietyMap, thingMap, request)); + + // 按深度优先对物排序 + return thingRelationRootService.sortInDiffRootThingIds( + request.getRootId(), + request.getRootThingIds(), + new HashSet<>(thingIds), + result, + res -> res.stream().collect(Collectors.groupingBy(ThingEnergyDictData::getThingId))); + } + + @Override + public void usageStatisticReportExport(EnergyUsageStatisticRequest request, HttpServletResponse response) { + // 报表原始数据 + List originalDataList = usageStatisticReport(request); + + // 数据类型转换 + List excelDataList = EnergyUsageStatisticExcel.convert(originalDataList, request.getTimeType()); + + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + + // 计算合并区域 + List regions = EnergyUsageStatisticExcel.calculateMergeRegion(excelDataList, request.isWeekTime()); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "能耗统计报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + + + + // 导出 + ExcelUtils.downLoadExcel("能耗统计报表.xls", response, workbook); + } + @Override + public void usageStatisticReportExportVertical(EnergyUsageStatisticRequest request, HttpServletResponse response) { + // 报表原始数据 + List originalDataList = usageStatisticReport(request); + // 数据类型转换 + List excelDataList = EnergyUsageStatisticVerticalExcel.convertFromDto(originalDataList, request.getTimeType()); + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + + // 计算合并区域 + List regions = EnergyUsageStatisticVerticalExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "能耗统计报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + + // 导出 + ExcelUtils.downLoadExcel("能耗统计报表.xls", response, workbook); + } + + @Override + public Map>> getRealtimeDataMap(AnalysisDataReqNewDTO dto) { + Long currentTime = System.currentTimeMillis(); + Map>> result = new HashMap<>(); + List thingAttrCodeList = dto.getThingAttrCodeList(); + EnergyUsageStatisticRequest request = dto.getRequest(); + //根目录id + Long rootId = null; + if (request!=null&& request.getRootId()!=null){ + rootId = request.getRootId(); + } + if (rootId==null){ + //取类型的root配置 + List configTypes = new ArrayList<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + configTypes.add(dto.getRequest().getConfigType()); + List configList = iotThingMenuConfigMapper.selectListExt( + QueryWrapper.create() + .in(IotThingMenuConfigEntity::getConfigType, configTypes) + .eq(IotThingMenuConfigEntity::getTenantCode, tenantCode)); + if (CollectionUtil.isEmpty(configList)){ + return null; + } + IotThingMenuConfigEntity entity = configList.get(0); + List menuConfigs = new ArrayList<>(); + if (org.apache.commons.lang3.StringUtils.isNotBlank(entity.getConfigType())){ + List newMenuConfigs = JSON.parseArray(entity.getMenuConfig(), IotThingMenuConfigDTO.MenuConfig.class); + if (CollectionUtil.isNotEmpty(newMenuConfigs)){ + for (IotThingMenuConfigDTO.MenuConfig menuConfig:newMenuConfigs){ +// if (menuConfig.getIsSelected()){ + menuConfigs.add(menuConfig); +// } + } + } + } + if (CollectionUtil.isEmpty(menuConfigs)){ + return null; + } + menuConfigs = menuConfigs.stream().sorted(Comparator.comparing(IotThingMenuConfigDTO.MenuConfig::getSort)).collect(Collectors.toList()); + //获取物对应的rootId + List rootIds = iotThingSourceMapper.getRootIdsByThingIdAndConfigTypeAndTenantCode(Long.parseLong(request.getThingIds().get(0)),request.getConfigType(),tenantCode); + for (IotThingMenuConfigDTO.MenuConfig menuConfig:menuConfigs){ + if (rootIds.contains(Long.parseLong(menuConfig.getId()))){ + rootId = Long.parseLong(menuConfig.getId()); + request.setRootId(Long.parseLong(menuConfig.getId())); + dto.setRequest(request); + break; + } + } + } + List list = usageStatisticReport(dto.getRequest()); + if (!CollectionUtils.isEmpty(list)) { + List newList = list.stream().filter(item -> thingAttrCodeList.contains(item.getAttrCode())).collect(Collectors.toList()); + for (ThingEnergyDictData dictData : newList) { + Map> map1 = new HashMap<>(); + List list1 = new ArrayList<>(); + if (dictData.getDataMap() != null) { + List valueList = (List) dictData.getDataMap().get("data"); + for (EnergyUsageLabelData energyUsageLabelData : valueList) { + Long valTs = DateTimeUtils.parse(energyUsageLabelData.getDateTime()); + if (valTs<=currentTime && energyUsageLabelData.getValue()!=null){ + AnalysisDataRespDTO dataRespDTO = new AnalysisDataRespDTO(); + dataRespDTO.setThingId(dictData.getThingId()); + dataRespDTO.setAttrName(dictData.getAttrName()); + dataRespDTO.setThingAttrUnit(dictData.getAttrUnit()); + dataRespDTO.setTs(valTs); + dataRespDTO.setVal(String.valueOf(energyUsageLabelData.getValue())); + list1.add(dataRespDTO); + } + } + map1.put(dictData.getAttrCode(), list1); + } + result.put(rootId+"_"+dictData.getThingId().toString(), map1); + } + } + return result; + } + + private List getThingSourceList(Long rootId, List thingIds, String configType, Collection attrCodes){ + return iotThingSourceService.findAllByFromIdAndThingIdAndConfigTypeAndAttrCode(null, thingIds, configType,rootId, attrCodes); + } + + private List getThingSourceList(Long rootId, List thingIds, String configType) { + return getThingSourceList(rootId, thingIds, configType, null); + } + + private BiFunction, List> + handleUsageStatisticReport( + Map energyDictMap, + Map varietyMap, + Map thingMap, + EnergyUsageStatisticRequest request) { + return (thingId, sourceList) -> { + // thingId: (fromId) 父数据源Id + IotThingEntityDTO thing = thingMap.get(thingId); + // 数据的时间范围 + Pair timeRange = getTimeRange(request.getBeginTime(), request.getEndTime()); + + // 整理好携带属性信息的map,组装 vo 时使用 + Map codeInfoMap = groupSourceByAttr(sourceList); + + // 超过十天的任意时间的数据特殊处理: + // 1. 小于10天数据量极小,全量查询无压力; + // 2. 以三四天的数据量分多次查询反而会有更多IO消耗,直观感觉至少要五天才不亏,这里选择10天 + if (request.isAnyTime() && moreThanTenDays(timeRange)) { + return handleAnyTimeReport( + thing, sourceList, codeInfoMap, energyDictMap, varietyMap, timeRange); + } + + // 查询tskv + List tsKvMapList = findTsKv(sourceList, timeRange, null); + if (CollectionUtils.isEmpty(tsKvMapList)) { + return Collections.emptyList(); + } + + // 数据转换: tskv -> labelData 再按attrCode分组,每个 attrCode 应当生成一条 vo 数据(ThingEnergyDictData) + Map> attrDataGroup = + EnergyUsageLabelData.convertFromTsKvMap(tsKvMapList).stream() + .collect(Collectors.groupingBy(EnergyUsageLabelData::getAttrCode)); + + // 生成本次需要构建的全量数据对应的时间 + AttributeTypeEnum attrTypeEnum = + AttributeTypeEnum.match(request.getAttrType()); + + List timeList = generateAllDateTime(timeRange, request, attrTypeEnum); + + // vo 数据列表定义 + List energyDictDataList = new ArrayList<>(); + // 循环生成 vo 数据 + attrDataGroup.forEach( + (code, dataList) -> energyDictDataList.add( + generateThingEnergyDictData( + thing, + code, + dataList, + timeList, + energyDictMap, + varietyMap, + codeInfoMap, + request.isWeekTime()))); + + // 生成合计行 + generateTotalRow(energyDictDataList, request.isWeekTime()); + + return energyDictDataList; + }; + } + + private List generateAllDateTime( + Pair timeRange, + EnergyUsageStatisticRequest request, + AttributeTypeEnum attrTypeEnum) { + if (request.isWeekTime()) { + List dateTimeList = new ArrayList<>(); + LocalDateTime begin = DateTimeUtils.parseDateTime(timeRange.getLeft()); + begin = begin.withHour(0).withMinute(0).withSecond(0).withNano(0); + LocalDateTime end = DateTimeUtils.parseDateTime(timeRange.getRight()); + while (begin.isBefore(end) || begin.isEqual(end)) { + dateTimeList.add(begin); + begin = begin.plusDays(1); + } + return dateTimeList; + } else { + return attrTypeEnum.getTimeRange(DateTimeUtils.parseDateTime(request.getBeginTime())); + } + } + + private ThingEnergyDictData generateThingEnergyDictData( + IotThingEntityDTO thing, + String attrCode, + List labelDataList, + List timeList, + Map energyDictMap, + Map varietyMap, + Map codeInfoMap, + boolean isWeekTime) { + // 初始化用量属性数据 + Long energyVarietyId = energyDictMap.get(attrCode); + CarbonEnergyVarietyEntity energyVariety = varietyMap.get(energyVarietyId); + IotThingSourceDTO attrInfo = codeInfoMap.get(attrCode); + ThingEnergyDictData result = initThingEnergyDict(thing, attrInfo, energyVariety); + Map dataMap = result.initDataMap(); + + // 按数据的时间分组,相同时间的数据进行汇总,如果当前时间没有数据,则生成一条没有value的数据项 + Map> timeDataMap = + labelDataList.stream() + .collect(Collectors.groupingBy(d -> d.getDateTime().toString())); + List allDataList = new ArrayList<>(); + for (LocalDateTime dateTime : timeList) { + List timeDataList = timeDataMap.get(dateTime.toString()); + if (CollectionUtils.isEmpty(timeDataList)) { + allDataList.add(EnergyUsageLabelData.generateNonValueData(dateTime, attrCode)); + continue; + } + // 同一时刻的子数据源数据汇总为父物当前时刻的数据 + allDataList.add(EnergyUsageLabelData.sameTimeAgg(timeDataList)); + } + dataMap.put("data", allDataList); + dataMap.put("totalValue", EnergyUsageLabelData.summaryValue(allDataList)); + if (isWeekTime) { + allDataList.addAll(EnergyUsageLabelData.generateWeekTotal(allDataList)); + } + return result; + } + + private Map groupSourceByAttr( + List sourceList) { + return sourceList.stream() + .collect( + Collectors.toMap( + IotThingSourceDTO::getThingAttrCode, + Function.identity(), + (v1, v2) -> v1)); + } + + private Pair getTimeRange(String beginTime, String endTime) { + Pair timeRange; + if (StringUtils.isBlank(beginTime) || StringUtils.isBlank(endTime)) { + timeRange = DateTimeUtils.getTimestampRangeForWeeks(3); + } else { + timeRange = + Pair.of( + DateTimeUtils.dateToTimestamp(beginTime), + DateTimeUtils.dateToTimestamp(endTime)); + } + return timeRange; + } + + /** + * 由于任意时间的属性粒度是am, 查询时间区间过大会有大量数据,因此需要优化处理 + * 为了避免逻辑过于复杂,超过天的(囊括完整天的情况),统一按 dd + am 来汇总 + */ + private List handleAnyTimeReport( + IotThingEntityDTO thing, + List sourceList, + Map codeInfoMap, + Map energyDictMap, + Map varietyMap, + Pair timeRange) { + // 查询任意时间的tskv + List tsKvList = findAnyTimeTsKv(sourceList, timeRange); + + // tskv按属性分组汇总 + Map attrValueMap = + tsKvList.stream() + .peek( + tskv -> { + // 查询时将属性分开了,聚合时再统一成am + String attrKey = tskv.getAttrKey(); + String amCode = changeAttrSuffix("am").apply(attrKey); + tskv.setAttrKey(amCode); + }) + .filter(tskv -> Objects.nonNull(tskv.getVal())) + .collect( + Collectors.groupingBy( + TsKvEntity::getAttrKey, + Collectors.reducing( + BigDecimal.ZERO, + map -> new BigDecimal(map.getVal()), + BigDecimal::add))); + + List result = new ArrayList<>(); + attrValueMap.forEach( + (attrCode, totalValue) -> { + Long varietyId = energyDictMap.get(attrCode); + ThingEnergyDictData data = + initThingEnergyDict( + thing, codeInfoMap.get(attrCode), varietyMap.get(varietyId)); + Map dataMap = data.initDataMap(); + dataMap.put("totalValue", totalValue); + result.add(data); + }); + + return result; + } + + private List findAnyTimeTsKv(List sourceList, Pair timeRange) { + LocalDateTime startDateTime = DateTimeUtils.parseDateTime(timeRange.getLeft()); + LocalDateTime endDateTime = DateTimeUtils.parseDateTime(timeRange.getRight()); + + // 判断这两个时间是否是凌晨 00:00 + boolean startIsBeginOfDay = isBeginOfDay(startDateTime); + boolean endIsBeginOfDay = isBeginOfDay(endDateTime); + + List tskv = new ArrayList<>(); + if (startIsBeginOfDay && endIsBeginOfDay) { + tskv.addAll(findTsKv(sourceList, timeRange, changeAttrSuffix("dd"))); + } else if (startIsBeginOfDay) { + // 结束时间不是当天凌晨 + long endTs = DateTimeUtils.parse(DateTimeUtils.getDayStart(endDateTime)); + tskv.addAll(findTsKv(sourceList, Pair.of(timeRange.getLeft(), endTs), changeAttrSuffix("dd"))); + tskv.addAll(findTsKv(sourceList, Pair.of(endTs, timeRange.getRight()))); + } else if (endIsBeginOfDay) { + // 开始时间不是当天凌晨 + long startTs = DateTimeUtils.parse(DateTimeUtils.getNextDayStart(startDateTime)); + tskv.addAll(findTsKv(sourceList, Pair.of(startTs, timeRange.getRight()), changeAttrSuffix("dd"))); + tskv.addAll(findTsKv(sourceList, Pair.of(timeRange.getLeft(), startTs))); + } else { + // 开始和结束时间都不是当天凌晨 + long startTs = DateTimeUtils.parse(DateTimeUtils.getNextDayStart(startDateTime)); + long endTs = DateTimeUtils.parse(DateTimeUtils.getDayStart(endDateTime)); + tskv.addAll(findTsKv(sourceList, Pair.of(startTs, endTs), changeAttrSuffix("dd"))); + tskv.addAll(findTsKv(sourceList, Pair.of(timeRange.getLeft(), startTs))); + tskv.addAll(findTsKv(sourceList, Pair.of(endTs, timeRange.getRight()))); + } + return tskv; + } + + private Function changeAttrSuffix(String suffix) { + return attrCode -> attrCode.substring(attrCode.length() - 2) + suffix; + } + + private boolean isBeginOfDay(LocalDateTime time) { + LocalDateTime dayStart = DateTimeUtils.getDayStart(time); + return time.isEqual(dayStart); + } + + + private boolean moreThanTenDays(Pair timeRange) { + long twoDay = 10 * 24 * 60 * 60 * 1000L; + return timeRange.getRight() - timeRange.getLeft() > twoDay; + } + + private List findTsKv( + List sourceList, Pair timeRange) { + return findTsKv(sourceList, timeRange, null); + } + + private List findTsKv( + List sourceList, + Pair timeRange, + Function changeAttrSuffix) { + List thingCodes = + sourceList.stream() + .map(IotThingSourceDTO::getThingCode) + .collect(Collectors.toList()); + List attrCodes = sourceList.stream() + .map(IotThingSourceDTO::getThingAttrCode) + .toList(); + return findTsKv(thingCodes, attrCodes, timeRange.getLeft(), timeRange.getRight()); + } + + private List findTsKv( + List thingCodes, List attrCode, long startTs, long endTs) { + try { + return new ArrayList<>( + tsKvService + .findTsKvByCodesAndAttrs(thingCodes, attrCode, startTs, endTs, null)); + } catch (Exception e) { + log.error("能耗统计报表:查询tsKvService异常:", e); + throw new SysException("能耗统计报表数据查询异常,请联系管理员"); + } + } + + private ThingEnergyDictData initThingEnergyDict( + IotThingEntityDTO thing, IotThingSourceDTO attrInfo, CarbonEnergyVarietyEntity energyVariety) { + return new ThingEnergyDictData() + .setThingId(thing.getId()) + .setThingName(thing.getName()) + .setThingCode(thing.getCode()) + .setAttrName(attrInfo.getThingAttrName()) + .setAttrCode(attrInfo.getThingAttrCode()) + .setAttrUnit(attrInfo.getThingAttrUnit()) + .setAttrType(attrInfo.getThingAttrType()) + .setEnergyVarietyId(energyVariety.getId()) + .setEnergyVarietyName(energyVariety.getName()) + .setEnergyVarietyCode(energyVariety.getCode()) + ; + } + + /** + * 基于基本数据列表生成能源品种的合计数据列表 + * + * @param baseDataList 基本数据列表 + * @param isWeekTime 是否按周查询 + */ + @SuppressWarnings("Duplicates") + private void generateTotalRow(List baseDataList, boolean isWeekTime) { + Map> thingAttrGroup = + baseDataList.stream() + .collect( + Collectors.groupingBy( + item -> item.getThingId() + "_" + item.getEnergyVarietyId())); + thingAttrGroup.forEach( + (thingEnergy, dataList) -> { + // 只有一条数据的话就不进行合计 + if (dataList.size() <= 1) { + return; + } + // 开始合计,先copy出来基本信息,初始化数据map + ThingEnergyDictData summaryData = dataList.get(0).copy(); + Map summaryDataMap = summaryData.initDataMap(); + + List> dataMapList = + dataList.stream() + .map(ThingEnergyDictData::getDataMap) + .toList(); + // 总量 + BigDecimal totalValue = + dataMapList.stream() + .map(map -> (BigDecimal) map.get("totalValue")) + .filter(Objects::nonNull) + .reduce(BigDecimal::add) + .orElse(BigDecimal.ZERO); + + // 把不同的物属性的数据列表进行合并,方便后续的统一分组计算 + List> diffAttrTimeLabelDataList = + dataMapList.stream() + .map(map -> JSONArray.parseArray(JSONArray.toJSONString(map.get("data")), EnergyUsageLabelData.class)) + .toList(); + List mergeList = + diffAttrTimeLabelDataList.stream() + .flatMap(Collection::parallelStream) + .collect(Collectors.toList()); + + + // 设置信息 + summaryData.setAttrName("合计"); + summaryData.setAttrCode(null); + summaryDataMap.put("totalValue", totalValue); + summaryDataMap.put("data", summaryTimeLabelData(mergeList, isWeekTime)); + + baseDataList.add(summaryData); + }); + } + + /** + * 计算合计行数据, 如果是周数据的话需要特殊处理,其他时间类型只需按时间戳统计即可 + */ + private List summaryTimeLabelData(List dataList, boolean isWeekTime) { + Map> dataMap = + dataList.stream() + .collect( + Collectors.groupingBy( + e -> + isWeekTime + ? e.getLabel() + "_" + e.getWeekOfYear() + "_" + e.getDateOfWeek() + : e.getDateTime().toString())); + List result = new ArrayList<>(); + dataMap.forEach( + (k, v) -> { + EnergyUsageLabelData baseDataInfo = v.get(0).copy(); + BigDecimal summaryValue = EnergyUsageLabelData.summaryValue(v); + baseDataInfo.setValue(summaryValue); + }); + + return result; + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/thread/EnergyUsageExecutor.java b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/thread/EnergyUsageExecutor.java new file mode 100644 index 0000000..89a6e63 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/energyrepory/thread/EnergyUsageExecutor.java @@ -0,0 +1,25 @@ +package com.thing.carbon.energyrepory.thread; + +import com.thing.common.util.thread.AbstractListeningExecutor; +import org.springframework.stereotype.Component; + +/** + * @author SiYang + * @date 2024/01/02 16:17 + * @description 能耗任务执行器 + */ +@Component +public class EnergyUsageExecutor extends AbstractListeningExecutor { + + @Override + public String getTaskName() { + return "energyUsageTask"; + } + + @Override + protected int getThreadPollSize() { + return 10; + } + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/EnergyBalanceController.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/EnergyBalanceController.java new file mode 100644 index 0000000..922220f --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/EnergyBalanceController.java @@ -0,0 +1,87 @@ +package com.thing.carbon.peakvalley.controller; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.peakvalley.dto.EnergyBalanceReq; +import com.thing.carbon.peakvalley.dto.PeakValleyExportParam; +import com.thing.carbon.peakvalley.service.EnergyBalanceService; +import com.thing.carbon.peakvalley.service.PeakValleyService; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.utils.export.ExcelEntityGenerator; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.poi.ss.usermodel.Workbook; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("v2/balance") +@Tag(name="AAA能源平衡表") +@RequiredArgsConstructor +public class EnergyBalanceController { + + private final EnergyBalanceService energyBalanceService; + private final PeakValleyService peakValleyService; + + @GetMapping("queryEnergyList") + @Operation(summary="获取所有能源品种") + private Result> queryEnergyList(){ + return new Result>().ok(peakValleyService.queryEnergyList()); + } + + + @PostMapping("peakValleyExport") + @Operation(summary="能源平衡表查询") + public Result> balanceReport(@RequestBody PeakValleyExportParam param){ + List reqs = energyBalanceService.balanceReport(param); + return new Result>().ok(reqs); + } + + + + @PostMapping("export") + @Operation(summary="能源平衡表导出") + public void balanceReport(@RequestBody PeakValleyExportParam param, HttpServletResponse response){ + List reqs = energyBalanceService.balanceReport(param); + + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(reqs)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(reqs)); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "能源平衡表"), excelExportEntities, dataCollection); + // 导出 + ExcelUtils.downLoadExcel("能源平衡表.xls", response, workbook); + + } + + + + + + + + + + + + + + + + + + + + + + + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/PeakValleyController.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/PeakValleyController.java new file mode 100644 index 0000000..1cb55dc --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/controller/PeakValleyController.java @@ -0,0 +1,184 @@ +package com.thing.carbon.peakvalley.controller; + +import cn.afterturn.easypoi.excel.ExcelExportUtil; +import cn.afterturn.easypoi.excel.entity.ExportParams; +import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity; +import cn.hutool.core.util.ObjectUtil; +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.carbon.peakvalley.dto.*; +import com.thing.carbon.peakvalley.excel.PeakValleyDosageVerticalExcel; +import com.thing.carbon.peakvalley.excel.PeakValleyExcel; +import com.thing.carbon.peakvalley.excel.PeakValleyPriceVerticalExcel; +import com.thing.carbon.peakvalley.excel.PricePeakValleyExcel; +import com.thing.carbon.peakvalley.service.PeakValleyService; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.utils.export.ExcelEntityGenerator; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.poi.ss.usermodel.Workbook; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * 尖峰谷平分析报表查询 + * + * @author xc + * @since 3.0 2023-09-21 + */ +@RestController +@RequestMapping("v2/peakvalley") +@Tag(name="尖峰谷平分析报表查询") +@RequiredArgsConstructor +public class PeakValleyController { + + private final PeakValleyService peakValleyService; + + @PostMapping("analysis") + @Operation(summary="尖峰谷平分析页面,尖峰谷平,用量,电费占比及信息") + public Result analysis(@RequestBody PeakValleyParam param){ + PeakValleyAnalysisReq bean = peakValleyService.analysis(param); + return new Result().ok(bean); + } + + @PostMapping("peakReport") + @Operation(summary="能效报告页面,尖峰谷平,用量,电费占比及信息") + public Result> peakReport(@RequestBody PeakValleyParam param){ + List bean = peakValleyService.peakReprot(param); + return new Result>().ok(bean); + } + + @PostMapping("costReport") + @Operation(summary="能效报告页面,费用分析报表") + public Result costReport(@RequestBody PeakValleyParam param){ + CostStatisticsReq bean = peakValleyService.costReport(param); + return new Result().ok(bean); + } + + @GetMapping("energy") + @Operation(summary="获取当前企业配置了尖峰谷平的能源类型基本信息,没有获取到,获取默认租户1001的") + public Result> energy(){ + List bean = peakValleyService.energy(); + return new Result>().ok(bean); + } + + @PostMapping("peakValleyReportByKey") + @Operation(summary="变压器尖峰谷平分析报表单一个key查询") + public Result> peakValleyReportByKey(@RequestBody PeakValleyParam param){ + List result = peakValleyService.peakValleyReportByKey(param); + return new Result>().ok(result); + } + + @PostMapping("transformerReport") + @Operation(summary="变压器分析,电量,电费分析查询") + public Result>> transformerReport(@RequestBody PeakValleyParam param){ + List> resultList = peakValleyService.transformerReport(param); + return new Result>>().ok(resultList); + + } + + @PostMapping("peakValleyReport") + @Operation(summary="尖峰谷平分析报表,变压器 key查询") + public Result>> peakValleyReport(@RequestBody PeakValleyParam param){ + Map> result = peakValleyService.peakValleyReport(param); + return new Result>>().ok(result); + } + + @PostMapping("peakValleyExport") + @Operation(summary ="尖峰谷平分析报表keys查询导出") + public void peakValleyExport(@RequestBody PeakValleyExportParam param, HttpServletResponse response){ + List exportInfO = new ArrayList<>(); + param.getThingParams().forEach(temp->{ + Map> result = peakValleyService.peakValleyReport(PeakValleyParam.builder(param,temp)); + if(ObjectUtil.isNotEmpty(result)){ + result.keySet().forEach(info->{ + if(ObjectUtil.isNotEmpty(result.get(info))){ + exportInfO.addAll(result.get(info)); + } + }); + } + }); + if("1".equals(param.getType())){ + // 数据类型转换 + List excelDataList = PeakValleyExcel.createBeans(exportInfO, param.getDateType()); + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + // 计算合并区域 + List regions = PeakValleyExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "峰平谷尖用能报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + // 导出 + ExcelUtils.downLoadExcel("峰平谷尖用能报表.xls", response, workbook); + }else { + // 数据类型转换 + List excelDataList = PeakValleyDosageVerticalExcel.convertFromDto(exportInfO, param.getDateType()); + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + // 计算合并区域 + List regions = PeakValleyDosageVerticalExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "峰平谷尖用能报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + // 导出 + ExcelUtils.downLoadExcel("峰平谷尖用能报表.xls", response, workbook); + } + + + } + + @PostMapping("price/peakValleyExport") + @Operation(summary = "尖峰谷平成本报表keys查询导出") + public void pricePeakValleyExport(@RequestBody PeakValleyExportParam param, HttpServletResponse response){ + List exportInfO = new ArrayList<>(); + + param.getThingParams().forEach(temp->{ + Map> result = peakValleyService.peakValleyReport(PeakValleyParam.builder(param,temp)); + if(ObjectUtil.isNotEmpty(result)){ + result.keySet().forEach(info->{ + if(ObjectUtil.isNotEmpty(result.get(info))){ + exportInfO.addAll(result.get(info)); + } + }); + } + }); + if("1".equals(param.getType())){ + // 数据类型转换 + List excelDataList = PricePeakValleyExcel.createBeans(exportInfO, param.getDateType()); + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + // 计算合并区域 + List regions = PricePeakValleyExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "峰平谷尖成本报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + // 导出 + ExcelUtils.downLoadExcel("峰平谷尖成本报表.xls", response, workbook); + }else { + // 数据类型转换 + List excelDataList = PeakValleyPriceVerticalExcel.convertFromDto(exportInfO, param.getDateType()); + // 生成表头 & 生成数据集合 + List excelExportEntities = ExcelEntityGenerator.generateExportEntity(new ArrayList<>(excelDataList)); + List> dataCollection = ExcelEntityGenerator.generateDataCollection(new ArrayList<>(excelDataList)); + // 计算合并区域 + List regions = PeakValleyPriceVerticalExcel.calculateMergeRegion(excelDataList); + Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams(null, "峰平谷尖成本报表"), excelExportEntities, dataCollection); + regions.forEach(workbook.getSheetAt(0)::addMergedRegion); + // 导出 + ExcelUtils.downLoadExcel("峰平谷尖成本报表.xls", response, workbook); + } + + + + } + + + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrParam.java new file mode 100644 index 0000000..dbb48d2 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrParam.java @@ -0,0 +1,14 @@ +package com.thing.carbon.peakvalley.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class AttrParam { + @Schema(description = "能源属性编码") + private String attrCode; + @Schema(description = "能源属性名称") + private String attrName; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrPriceInfo.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrPriceInfo.java new file mode 100644 index 0000000..18d86b5 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/AttrPriceInfo.java @@ -0,0 +1,32 @@ +package com.thing.carbon.peakvalley.dto; + + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; + +/** + * 能源价格信息 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AttrPriceInfo { + + /** + * 属性编码 + */ + private String attrCode; + /** + * 属性名称 + */ + private String attrName; + + /** + * 价格 + */ + private BigDecimal price; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostReq.java new file mode 100644 index 0000000..5e24f22 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostReq.java @@ -0,0 +1,22 @@ +package com.thing.carbon.peakvalley.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CostReq { + + @Schema(description = "能源品种编码") + private String baseCode; + @Schema(description = "能源品种名称") + private String baseName; + @Schema(description = "费用") + private BigDecimal cost; + @Schema(description = "时间") + private Long ts; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostStatisticsReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostStatisticsReq.java new file mode 100644 index 0000000..29bb97d --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/CostStatisticsReq.java @@ -0,0 +1,38 @@ +package com.thing.carbon.peakvalley.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "费用报告,返回结果主体") +public class CostStatisticsReq { + + @Schema (name ="总费用") + private BigDecimal sumCost; + @Schema (name ="总最高日费用") + private BigDecimal maxCost; + @Schema (name ="总最低日费用") + private BigDecimal minCost; + @Schema (name ="总平均费用") + private BigDecimal avgCost; + + + @Schema (name ="各类能源平均日费用集合") + private List avgCosts; + @Schema (name ="各类能源,日总费用 同一时间ts,会存在多笔数据,属性编码不同") + private List> energyCostList = new ArrayList<>(); + @Schema (name ="所有能源总费用") + private List allCostList; + @Schema (name ="环比") + private BigDecimal mom; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyBalanceReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyBalanceReq.java new file mode 100644 index 0000000..0c4d497 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyBalanceReq.java @@ -0,0 +1,37 @@ +package com.thing.carbon.peakvalley.dto; + + +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +@Data +@NoArgsConstructor +public class EnergyBalanceReq { + + @Schema(description = "序号") + @CustomExcel(name = "序号") + private Integer number; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物名称") + @CustomExcel(name = "结构", width = 25,orderNum = 1) + private String thingName; + @Schema(description = "属性结果集") + @CustomExcelCollection + private List dosageReqs; + @Schema(description = "排序,业务用,前端忽略") + private Integer sort; + + + public EnergyBalanceReq(String thingCode, String thingName, Integer sort) { + this.thingCode = thingCode; + this.thingName = thingName; + this.sort = sort; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyReq.java new file mode 100644 index 0000000..bea1fb4 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/EnergyReq.java @@ -0,0 +1,15 @@ +package com.thing.carbon.peakvalley.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class EnergyReq { + + @Schema(description = "能源品种名称 电,水,天然气,蒸汽...") + private String name; + @Schema(description = "能源品种编码 依靠当前系统定义,如电-A29...") + private String code; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyAnalysisReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyAnalysisReq.java new file mode 100644 index 0000000..2cd7c40 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyAnalysisReq.java @@ -0,0 +1,40 @@ +package com.thing.carbon.peakvalley.dto; + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +@Schema( name= "峰平谷尖分析响应结果") +public class PeakValleyAnalysisReq { + + + @Schema(description = "能源品种编码") + private String baseCode; + + @Schema(description = "能源品种名称") + private String baseName; + @Schema(description = "能源品种单位") + private String baseUint; + + @Schema(description = "总用量") + private BigDecimal sumDosage = BigDecimal.ZERO; + + @Schema(description = "总费用/总价") + private BigDecimal sumPrice = BigDecimal.ZERO; + + @Schema(description = "统计分析详情") + private List priceStatisticsInfos; + + @Schema(description = "原始数据无需关注") + private List peakValleyDosageReqs; + + @Schema(description = "峰平谷尖用量详情") + private List> peakValleyList; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyExportParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyExportParam.java new file mode 100644 index 0000000..9f33596 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyExportParam.java @@ -0,0 +1,75 @@ +package com.thing.carbon.peakvalley.dto; + + +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; + +@Data +@Schema( name= "报表入参") +public class PeakValleyExportParam { + + @Schema(description = "选择的物集合") + private List thingParams; + @Schema(description = "选择的能源品种集合") + private List attrParams; + + public static final DateTimeFormatter DATE_TIME_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Schema(description = "日期类型,am=天,dd=月,mm=年") + private String dateType; + + @Schema(description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy") + private String dateStr; + + @Schema(description = "能源品种编码集合,英文逗号分隔") + private String keys; + private String type; + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + + + @SuppressWarnings("Duplicates") + public LocalDateTime getBeginTime() { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am, hh -> { + // yyyy-MM-dd + return DateTimeUtils.parseDateTime(dateStr + " 00:00:00"); + } + case dd -> { + // yyyy-MM + return DateTimeUtils.parseDateTime(dateStr + "-01 00:00:00"); + } + case mm -> { + // yyyy + return DateTimeUtils.parseDateTime(dateStr + "-01-01 00:00:00"); + } + default -> { + } + } + return null; + } + + public static final HashMap dataTypeMap = new HashMap() {{ + put("dd", "mm"); + put("mm", "yy"); + }}; + + + + + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyParam.java new file mode 100644 index 0000000..c12b88c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyParam.java @@ -0,0 +1,87 @@ +package com.thing.carbon.peakvalley.dto; + + +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +@Data +@Schema( name= "峰平谷尖分析,入参") +public class PeakValleyParam { + public static final DateTimeFormatter DATE_TIME_PATTERN = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + + @Schema(description = "日期类型,am=天,dd=月,mm=年,week = 周, any = 任意时间") + private String dateType; + + @Schema(description = "日期,天=yyyy-MM-dd,月=yyyy-MM,年=yyyy") + private String dateStr; + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "物主键:必要参数") + private Long thingId; + + @Schema(description = "能源品种编码,电A29 气C6,能效报告时不传") + private String key; + + @Schema(description = "能源品种编码集合,英文逗号分隔") + private String keys; + + @Schema(description = "能源类型名称") + private String baseName; + + @Schema(description = "物名称") + private String thingName; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + @SuppressWarnings("Duplicates") + public LocalDateTime getBeginTime() { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am, hh ,week -> { + // yyyy-MM-dd + return DateTimeUtils.parseDateTime(dateStr + " 00:00:00"); + } + case dd-> { + // yyyy-MM + return DateTimeUtils.parseDateTime(dateStr + "-01 00:00:00"); + } + case mm -> { + // yyyy + return DateTimeUtils.parseDateTime(dateStr + "-01-01 00:00:00"); + } + default -> { + } + } + return null; + } + + + + public static PeakValleyParam builder(PeakValleyExportParam param,ThingParam thingParam){ + PeakValleyParam valleyParam = new PeakValleyParam(); + valleyParam.setDateType(param.getDateType()); + valleyParam.setKeys(param.getKeys()); + valleyParam.setDateStr(param.getDateStr()); + valleyParam.setThingCode(thingParam.getThingCode()); + valleyParam.setThingName(thingParam.getThingName()); + valleyParam.setThingId(thingParam.getThingId()); + valleyParam.setStartTime(param.getStartTime()); + valleyParam.setEndTime(param.getEndTime()); + return valleyParam; + } + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceInfo.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceInfo.java new file mode 100644 index 0000000..b35695e --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceInfo.java @@ -0,0 +1,58 @@ +package com.thing.carbon.peakvalley.dto; + + +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 能源价格信息 + */ +@Data +public class PeakValleyPriceInfo { + /** + * 是否存在峰谷平价格配置 + */ + private Boolean isHasPeakValley; + + /** + * 能源品种编码 + */ + private String baseCode; + /** + * 能源品种名称 + */ + private String baseName; + + /** + * 属性单位 + */ + private String baseUnit; + + /** + * 原属性价格 + */ + private BigDecimal basePrice; + + /** + * 谷价 + */ + private AttrPriceInfo valleyPriceBean; + + /** + * 尖价 + */ + private AttrPriceInfo rushPriceBean; + + /** + * 峰价 + */ + private AttrPriceInfo peakPriceBean; + + /** + * 平价 + */ + private AttrPriceInfo normalPriceBean; + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceStatisticsInfo.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceStatisticsInfo.java new file mode 100644 index 0000000..cb9f003 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyPriceStatisticsInfo.java @@ -0,0 +1,53 @@ +package com.thing.carbon.peakvalley.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 能源价格信息 + */ +@Data +public class PeakValleyPriceStatisticsInfo { + + @Schema(description = "能源类型名称") + private String baseName; + + @Schema(description = "属性名称") + private String attrName; + + @Schema(description = "属性编码") + private String attrCode; + + @Schema(description = "类型总用量") + private BigDecimal allDosage= BigDecimal.ZERO; + + @Schema(description = "类型用量占比") + private BigDecimal allDosageRatio; + + @Schema(description = "单价") + private BigDecimal unitPrice; + + @Schema(description = "类型总费用") + private BigDecimal allTotalPrice; + + @Schema(description = "类型费用占比") + private BigDecimal priceRatio; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "用量分析描述") + private String desc; + + private Integer sort=0; + + + private Boolean flag= true; + + @Schema(description = "用量标准占比") + private BigDecimal standardRatio; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyReportReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyReportReq.java new file mode 100644 index 0000000..cf68e9c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/PeakValleyReportReq.java @@ -0,0 +1,45 @@ +package com.thing.carbon.peakvalley.dto; + + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +@Data +public class PeakValleyReportReq { + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物名称") + private String thingName; + @Schema(description = "能源类型编码") + private String baseCode; + @Schema(description = "能源类型名称") + private String baseName; + @Schema(description = "能源类型单位") + private String baseUnit; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "属性编码") + private String attrCode; + @Schema(description = "类型总用量") + private BigDecimal allDosage; + @Schema(description = "类型总费用") + private BigDecimal totalPrice; + @Schema(description = "类型费用占比") + private BigDecimal totalPriceRatio; + @Schema(description = "用量标准占比") + private BigDecimal standardRatio; + @Schema(description = "用量实际占比") + private BigDecimal realityRatio; + @Schema(description = "单价") + private BigDecimal uintPrice; + @Schema(description = "用量详情") + private List peakValleyDosageReqs; + + private Integer sort=0; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingAttrDosageReq.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingAttrDosageReq.java new file mode 100644 index 0000000..a969293 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingAttrDosageReq.java @@ -0,0 +1,56 @@ +package com.thing.carbon.peakvalley.dto; + + +import com.thing.common.core.annotation.CustomExcel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class ThingAttrDosageReq { + + + + @Schema(description = "属性编码") + private String attrCode; + + @Schema(description = "属性名称") + private String attrName; + + @Schema(description = "属性单位") + private String uint; + + @Schema(description = "用量 属性值") + @CustomExcel(columnNameGenerator = "getTitle", width = 12, orderGenerator = "getOrder", groupGenerator = "getGroupName") + private BigDecimal dosage; + + + public ThingAttrDosageReq(String attrCode,String attrName,String uint, BigDecimal dosage) { + this.attrCode = attrCode; + this.attrName = attrName; + this.uint = uint; + this.dosage = dosage; + } + + + + + + + private String getTitle(){ + return this.attrName+"("+this.uint+")"; + + } + + private String getGroupName(){ + return "生产消耗"; + } + + private int getOrder(){ + int orderNum = 1; + return ++orderNum; + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingParam.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingParam.java new file mode 100644 index 0000000..d8d7268 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/dto/ThingParam.java @@ -0,0 +1,21 @@ +package com.thing.carbon.peakvalley.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + + +@Data +@Schema( name= "物的基本信息") +public class ThingParam { + + @Schema(description = "物主键:必要参数") + private Long thingId; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物名称") + private String thingName; + @Schema(description = "排序,只需要第一个点击的物传-1 其他的序号按物关系序号传入即可") + private Integer sort; +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageExcel.java new file mode 100644 index 0000000..455805c --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageExcel.java @@ -0,0 +1,160 @@ +package com.thing.carbon.peakvalley.excel; + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PeakValleyDosageExcel { + private static final Map weekMap = new HashMap<>(); + static { + weekMap.put(1, "周一"); + weekMap.put(2, "周二"); + weekMap.put(3, "周三"); + weekMap.put(4, "周四"); + weekMap.put(5, "周五"); + weekMap.put(6, "周六"); + weekMap.put(7, "周日"); + } + private String dateType; + + private Long ts; + + @Getter + private BigDecimal val; + + @CustomExcel(columnNameGenerator = "getTitle", width = 12, orderGenerator = "getOrder", groupGenerator = "getGroupName") + private String valExport="--"; + + private Integer hh; + + private Integer dd; + + @Schema(description = "年份") + private Integer year; + @Schema(description = "月份") + private Integer month; + @Schema(description = "当年第几周") + private Integer weekOfYear; + @Schema(description = "当月第几天") + private Integer dateOfMonth; + @Schema(description = "当周周几") + private Integer dateOfWeek; + @Schema(description = "当天几点") + private Integer hourOfDay; + @Schema(description = "数据标签 weekTotal周总计,item用量") + private String label; + + public static PeakValleyDosageExcel crateBean(PeakValleyDosageReq dosageReq, String dateType){ + PeakValleyDosageExcel excel = ConvertUtils.convertWithTypeAdapt(dosageReq,PeakValleyDosageExcel.class); + if(dosageReq.getTs()!=null){ + LocalDateTime dateTime = DateTimeUtils.parseDateTime(dosageReq.getTs()); + int hh = dateTime.getHour(); + int dd = dateTime.getDayOfMonth(); + excel.setTs(dosageReq.getTs()); + excel.setHh(hh); + excel.setDd(dd); + } + excel.setVal(dosageReq.getDosage()); + excel.setValExport(Objects.nonNull(excel.getVal()) ? excel.getVal().toString() : "--"); + excel.setDateType(dateType); + return excel; + } + + + private String getTitle(){ + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + return hh+"时"; + case dd: + return dd+"日"; + case week: + StringBuilder stringBuilder = new StringBuilder(); + if (Objects.equals(label, "item")) { + stringBuilder + .append(addZeroPrefix(month)) + .append("-") + .append(addZeroPrefix(dateOfMonth)) + .append(" ") + .append(weekMap.get(dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + stringBuilder.append(weekOfYear).append("周 总计"); + } + return stringBuilder.toString(); + case mm: + return month+"月"; + default: + } + return null; + + } + + + private String getGroupName(){ + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + return year+"年"+month+"月"+dd+"日"; + case week: + return "第"+weekOfYear+"周"; + case dd: + return year+"年"+month+"月"; + case mm: + return year+"年"; + default: + } + return null; + } + + + + private int getOrder(){ + int orderNum = 6; + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + orderNum+=(24-hh); + break; + case week: + // 算法可以自定义,这里只是从数学上给出一个粗浅的计算方法 + if (Objects.equals(label, "item")) { + orderNum += (((52 - weekOfYear) << 3) + (8 - dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + orderNum += (52 - weekOfYear) << 3; + } + break; + case dd: + orderNum+=(31-dd); + break; + case mm: + orderNum+=(12-month); + break; + default: + } + return orderNum; + } + + private String addZeroPrefix(Integer num) { + return (num < 10 && num > 0) ? "0" + num : num.toString(); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageVerticalExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageVerticalExcel.java new file mode 100644 index 0000000..52ac99b --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyDosageVerticalExcel.java @@ -0,0 +1,192 @@ +package com.thing.carbon.peakvalley.excel; + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.carbon.peakvalley.dto.PeakValleyReportReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +@Data +@NoArgsConstructor +@SuppressWarnings("unused") +public class PeakValleyDosageVerticalExcel { + private static final String VALUE_WITH_UNIT = "%s(%s)"; + + /** + * 数据值: 能源(品种)、总价、日期等 + */ + @CustomExcel(name = "结构", width = 25) + private String tag; + + @CustomExcelCollection + private List dataList; + + public PeakValleyDosageVerticalExcel(String tag) { + this.tag = tag; + } + + public List getDataList() { + if (Objects.isNull(dataList)) { + dataList = new ArrayList<>(); + } + return this.dataList; + } + + @Data + @Accessors(chain = true) + public static class CellData { + @CustomExcel(keyGenerator = "generateKey", columnNameGenerator = "generateTitle", width = 12) + private String value; + + private String thingCode; + + private String thingName; + + private String attrCode; + + private String attrName; + + private String energyName; + + private String unit; + + public static CellData convert(PeakValleyReportReq source) { + return new CellData() + .setThingCode(source.getThingCode()) + .setThingName(source.getThingName()) + .setAttrCode(source.getAttrCode()) + .setAttrName(source.getAttrName()) + .setEnergyName(source.getBaseName()) + .setUnit(source.getBaseUnit()); + } + + public String generateKey() { + return thingCode + attrCode; + } + + public String generateTitle() { + return thingName; + } + + } + + public static List convertFromDto(List sourceList, String timeType) { + List res = new ArrayList<>(); + Map excelMap = new TreeMap<>(Comparator.reverseOrder()); + PeakValleyDosageVerticalExcel energyInfo = new PeakValleyDosageVerticalExcel("能源"); + PeakValleyDosageVerticalExcel attrInfo = new PeakValleyDosageVerticalExcel("能源"); + PeakValleyDosageVerticalExcel totalPriceInfo = new PeakValleyDosageVerticalExcel("总用量"); + + sourceList.forEach(s -> { + List dataList =s.getPeakValleyDosageReqs(); + if(!"any".equals(timeType)){ + dataList.forEach( + labelData -> { + // 只需展示基础数据 + if (!Objects.equals(labelData.getLabel(), "item")) { + return; + } + if (labelData.getTs()>System.currentTimeMillis() ) { + return; + } + String timeTag = getTimeTag(labelData, timeType); + excelMap.computeIfAbsent( + timeTag, + k -> new PeakValleyDosageVerticalExcel(timeTag)) + .getDataList() + .add(CellData.convert(s).setValue(labelData.getDosage()==null?"--":labelData.getDosage().toString())); + }); + } + energyInfo.getDataList().add(CellData.convert(s).setValue(String.format(VALUE_WITH_UNIT, s.getBaseName(), s.getBaseUnit()))); + attrInfo.getDataList().add(CellData.convert(s).setValue(s.getAttrName())); + String val = s.getAllDosage().toString(); + if(StringUtils.isNotEmpty(val)){ + totalPriceInfo.getDataList().add(CellData.convert(s).setValue(val)); + }else { + totalPriceInfo.getDataList().add(CellData.convert(s).setValue("--")); + } + }); + + res.add(energyInfo); + res.add(attrInfo); + res.add(totalPriceInfo); + excelMap.forEach((k,v) -> res.add(v)); + + return res; + } + + private static String getTimeTag(PeakValleyDosageReq labelData, String timeType) { + LocalDateTime time = DateTimeUtils.parseDateTime(labelData.getTs()); + switch (timeType) { + case "am": + case "any": + return time.format(DateTimeUtils.DATE_TIME_PATTERN); + case "week": + case "dd": + return time.format(DateTimeUtils.DATE_PATTERN); + case "mm": + return time.format(DateTimeUtils.YEAR_MONTH_PATTERN01); + default: + return ""; + } + } + + public static List calculateMergeRegion(List dataList) { + if (CollectionUtils.isEmpty(dataList)) { + return Collections.emptyList(); + } + List mergeRegionList = new ArrayList<>(); + PeakValleyDosageVerticalExcel firstRowData = dataList.get(0); + List firstRowDataList = firstRowData.getDataList(); + Map> thingColRangeMap = new HashMap<>(); + Map> energyColRangeMap = new HashMap<>(); + AtomicInteger colOfThing = new AtomicInteger(0); + AtomicInteger colOfEnergy = new AtomicInteger(0); + firstRowDataList.forEach( + cellData -> { + thingColRangeMap + .computeIfAbsent( + cellData.getThingCode(), + k -> MutablePair.of(colOfThing.get() + 1, colOfThing.get())) + .setValue(colOfThing.incrementAndGet()); + energyColRangeMap + .computeIfAbsent( + cellData.getThingCode() + "_" + cellData.getEnergyName(), + k -> MutablePair.of(colOfEnergy.get() + 1, colOfEnergy.get())) + .setValue(colOfEnergy.incrementAndGet()); + }); + + thingColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(0, 0, pair.getLeft(), pair.getRight())); + }); + + energyColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(1, 1, pair.getLeft(), pair.getRight())); + }); + + mergeRegionList.add(new CellRangeAddress(1, 2, 0, 0)); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyExcel.java new file mode 100644 index 0000000..ca1bc40 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyExcel.java @@ -0,0 +1,112 @@ +package com.thing.carbon.peakvalley.excel; + +import com.thing.carbon.peakvalley.dto.PeakValleyReportReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.util.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PeakValleyExcel { + + + @CustomExcel(name = "结构", width = 25) + private String thingName; + @CustomExcel(name = "能源品种",orderNum=1) + private String baseName; + @CustomExcel(name = "类型",orderNum=2) + private String attrName; + @CustomExcel(name = "总量",orderNum=3) + private String allDosage; + @CustomExcel(name = "标准占比",orderNum=4) + private String standardRatio; + @CustomExcel(name = "实际占比",orderNum=5) + private String realityRatio; + @CustomExcelCollection + private List peakValleyDosageReqs; + + + + + public static List createBeans(List reqs, String dataType){ + List result = new ArrayList<>(); + reqs.forEach(temp->{ + PeakValleyExcel excelBean = new PeakValleyExcel(); + excelBean.setThingName(temp.getThingName()); + excelBean.setBaseName(temp.getBaseName()+"("+temp.getBaseUnit()+")"); + excelBean.setAllDosage(Objects.nonNull(temp.getAllDosage()) ? temp.getAllDosage().toString() : "--"); + excelBean.setRealityRatio(Objects.nonNull(temp.getRealityRatio()) ? temp.getRealityRatio()+"%" : "--"); + excelBean.setStandardRatio(Objects.nonNull(temp.getStandardRatio()) ? temp.getStandardRatio()+"%" : "--"); + excelBean.setAttrName(temp.getAttrName()); + + List dosageExcels = new ArrayList<>(); + if(temp.getPeakValleyDosageReqs()!=null){ + temp.getPeakValleyDosageReqs().forEach(info->{ + //特殊处理,只要当前时间之前的数据 + if(info.getTs()==null||info.getTs()<=System.currentTimeMillis()){ + PeakValleyDosageExcel dosageExcel = PeakValleyDosageExcel.crateBean(info,dataType); + dosageExcels.add(dosageExcel); + } + }); + } + excelBean.setPeakValleyDosageReqs(dosageExcels); + result.add(excelBean); + }); + return result; + } + + @SuppressWarnings({"unchecked"}) + public static List calculateMergeRegion(List excelDataList) { + List mergeRegionList = new ArrayList<>(); + Map> thingCountMap = new HashMap<>(); + Map> thingEnergyCountMap = new HashMap<>(); + int beginRowOfThing =1 ; + int beginRowOfEnergy =1; + for (PeakValleyExcel currentData : excelDataList) { + if (!thingCountMap.containsKey(currentData.getThingName())) { + thingCountMap.put( + currentData.getThingName(), + MutablePair.of(++beginRowOfThing, beginRowOfThing)); + } else { + Pair thingCountPair = + thingCountMap.get(currentData.getThingName()); + thingCountPair.setValue(++beginRowOfThing); + } + + String energyKey = currentData.getThingName() + "_" + currentData.getBaseName(); + if (!thingEnergyCountMap.containsKey(energyKey)) { + thingEnergyCountMap.put( + energyKey, MutablePair.of(++beginRowOfEnergy, beginRowOfEnergy)); + } else { + Pair energyCountPair = thingEnergyCountMap.get(energyKey); + energyCountPair.setValue(++beginRowOfEnergy); + } + } + thingCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 0, 0)); + }); + thingEnergyCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 1, 1)); + }); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyPriceVerticalExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyPriceVerticalExcel.java new file mode 100644 index 0000000..ba201f9 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PeakValleyPriceVerticalExcel.java @@ -0,0 +1,195 @@ +package com.thing.carbon.peakvalley.excel; + + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.carbon.peakvalley.dto.PeakValleyReportReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +@Data +@NoArgsConstructor +@SuppressWarnings("unused") +public class PeakValleyPriceVerticalExcel { + private static final String VALUE_WITH_UNIT = "%s(%s)"; + + /** + * 数据值: 能源(品种)、总价、日期等 + */ + @CustomExcel(name = "结构", width = 25) + private String tag; + + @CustomExcelCollection + private List dataList; + + public PeakValleyPriceVerticalExcel(String tag) { + this.tag = tag; + } + + public List getDataList() { + if (Objects.isNull(dataList)) { + dataList = new ArrayList<>(); + } + return this.dataList; + } + + @Data + @Accessors(chain = true) + public static class CellData { + @CustomExcel(keyGenerator = "generateKey", columnNameGenerator = "generateTitle", width = 12) + private String value; + + private String thingCode; + + private String thingName; + + private String attrCode; + + private String attrName; + + private String energyName; + + private String unit; + + public static CellData convert(PeakValleyReportReq source) { + return new CellData() + .setThingCode(source.getThingCode()) + .setThingName(source.getThingName()) + .setAttrCode(source.getAttrCode()) + .setAttrName(source.getAttrName()) + .setEnergyName(source.getBaseName()) + .setUnit(source.getBaseUnit()); + } + + public String generateKey() { + return thingCode + attrCode; + } + + public String generateTitle() { + return thingName; + } + + } + + public static List convertFromDto(List sourceList, String timeType) { + List res = new ArrayList<>(); + Map excelMap = new TreeMap<>(Comparator.reverseOrder()); + PeakValleyPriceVerticalExcel energyInfo = new PeakValleyPriceVerticalExcel("能源"); + PeakValleyPriceVerticalExcel attrInfo = new PeakValleyPriceVerticalExcel("能源"); + PeakValleyPriceVerticalExcel priceInfo = new PeakValleyPriceVerticalExcel("单价"); + PeakValleyPriceVerticalExcel totalPriceInfo = new PeakValleyPriceVerticalExcel("总价"); + sourceList.forEach(s -> { + List dataList =s.getPeakValleyDosageReqs(); + if(!"any".equals(timeType)){ + dataList.forEach( + labelData -> { + // 只需展示基础数据 + if (!Objects.equals(labelData.getLabel(), "item")) { + return; + } + if (labelData.getTs()>System.currentTimeMillis() ) { + return; + } + String timeTag = getTimeTag(labelData, timeType); + excelMap.computeIfAbsent( + timeTag, + k -> new PeakValleyPriceVerticalExcel(timeTag)) + .getDataList() + .add(CellData.convert(s).setValue(labelData.getTotalPrice()==null?"--":labelData.getTotalPrice().toString())); + }); + } + energyInfo.getDataList().add(CellData.convert(s).setValue(String.format(VALUE_WITH_UNIT, s.getBaseName(), s.getBaseUnit()))); + attrInfo.getDataList().add(CellData.convert(s).setValue(s.getAttrName())); + priceInfo.getDataList().add(CellData.convert(s).setValue(s.getUintPrice()==null?"--":s.getUintPrice().toString())); + String val = s.getAllDosage().toString(); + if(StringUtils.isNotEmpty(val)){ + totalPriceInfo.getDataList().add(CellData.convert(s).setValue(val)); + }else { + totalPriceInfo.getDataList().add(CellData.convert(s).setValue("--")); + } + }); + + res.add(energyInfo); + res.add(attrInfo); + res.add(totalPriceInfo); + res.add(priceInfo); + excelMap.forEach((k,v) -> res.add(v)); + + return res; + } + + private static String getTimeTag(PeakValleyDosageReq labelData, String timeType) { + LocalDateTime time = DateTimeUtils.parseDateTime(labelData.getTs()); + switch (timeType) { + case "am": + case "any": + return time.format(DateTimeUtils.DATE_TIME_PATTERN); + case "week": + case "dd": + return time.format(DateTimeUtils.DATE_PATTERN); + case "mm": + return time.format(DateTimeUtils.YEAR_MONTH_PATTERN01); + default: + return ""; + } + } + + public static List calculateMergeRegion(List dataList) { + if (CollectionUtils.isEmpty(dataList)) { + return Collections.emptyList(); + } + List mergeRegionList = new ArrayList<>(); + PeakValleyPriceVerticalExcel firstRowData = dataList.get(0); + List firstRowDataList = firstRowData.getDataList(); + Map> thingColRangeMap = new HashMap<>(); + Map> energyColRangeMap = new HashMap<>(); + AtomicInteger colOfThing = new AtomicInteger(0); + AtomicInteger colOfEnergy = new AtomicInteger(0); + firstRowDataList.forEach( + cellData -> { + thingColRangeMap + .computeIfAbsent( + cellData.getThingCode(), + k -> MutablePair.of(colOfThing.get() + 1, colOfThing.get())) + .setValue(colOfThing.incrementAndGet()); + energyColRangeMap + .computeIfAbsent( + cellData.getThingCode() + "_" + cellData.getEnergyName(), + k -> MutablePair.of(colOfEnergy.get() + 1, colOfEnergy.get())) + .setValue(colOfEnergy.incrementAndGet()); + }); + + thingColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(0, 0, pair.getLeft(), pair.getRight())); + }); + + energyColRangeMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add(new CellRangeAddress(1, 1, pair.getLeft(), pair.getRight())); + }); + + mergeRegionList.add(new CellRangeAddress(1, 2, 0, 0)); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyDosageExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyDosageExcel.java new file mode 100644 index 0000000..11e78ed --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyDosageExcel.java @@ -0,0 +1,160 @@ +package com.thing.carbon.peakvalley.excel; + + +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PricePeakValleyDosageExcel { + private static final Map weekMap = new HashMap<>(); + static { + weekMap.put(1, "周一"); + weekMap.put(2, "周二"); + weekMap.put(3, "周三"); + weekMap.put(4, "周四"); + weekMap.put(5, "周五"); + weekMap.put(6, "周六"); + weekMap.put(7, "周日"); + } + private String dateType; + + private Long ts; + + @Getter + private BigDecimal val; + + @CustomExcel(columnNameGenerator = "getTitle", width = 12, orderGenerator = "getOrder", groupGenerator = "getGroupName") + private String valExport="--"; + + private Integer hh; + + private Integer dd; + + @Schema(description = "年份") + private Integer year; + @Schema(description = "月份") + private Integer month; + @Schema(description = "当年第几周") + private Integer weekOfYear; + @Schema(description = "当月第几天") + private Integer dateOfMonth; + @Schema(description = "当周周几") + private Integer dateOfWeek; + @Schema(description = "当天几点") + private Integer hourOfDay; + @Schema(description = "数据标签 weekTotal周总计,item用量") + private String label; + + public static PricePeakValleyDosageExcel crateBean(PeakValleyDosageReq dosageReq, String dateType){ + PricePeakValleyDosageExcel excel = ConvertUtils.convertWithTypeAdapt(dosageReq, PricePeakValleyDosageExcel.class); + if(dosageReq.getTs()!=null){ + LocalDateTime dateTime = DateTimeUtils.parseDateTime(dosageReq.getTs()); + int hh = dateTime.getHour(); + int dd = dateTime.getDayOfMonth(); + excel.setTs(dosageReq.getTs()); + excel.setHh(hh); + excel.setDd(dd); + } + excel.setVal(dosageReq.getTotalPrice()); + excel.setValExport(Objects.nonNull(excel.getVal()) ? excel.getVal().toString() : "--"); + excel.setDateType(dateType); + return excel; + } + + + private String getTitle(){ + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + return hh+"时"; + case dd: + return dd+"日"; + case week: + StringBuilder stringBuilder = new StringBuilder(); + if (Objects.equals(label, "item")) { + stringBuilder + .append(addZeroPrefix(month)) + .append("-") + .append(addZeroPrefix(dateOfMonth)) + .append(" ") + .append(weekMap.get(dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + stringBuilder.append(weekOfYear).append("周 总计"); + } + return stringBuilder.toString(); + case mm: + return month+"月"; + default: + } + return null; + + } + + + private String getGroupName(){ + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + return year+"年"+month+"月"+dd+"日"; + case week: + return "第"+weekOfYear+"周"; + case dd: + return year+"年"+month+"月"; + case mm: + return year+"年"; + default: + } + return null; + } + + + + private int getOrder(){ + int orderNum = 6; + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(dateType); + switch (attributeTypeEnum) { + case am: + case hh: + orderNum+=(24-hh); + break; + case week: + // 算法可以自定义,这里只是从数学上给出一个粗浅的计算方法 + if (Objects.equals(label, "item")) { + orderNum += (((52 - weekOfYear) << 3) + (8 - dateOfWeek)); + } else if (Objects.equals(label, "weekTotal")) { + orderNum += (52 - weekOfYear) << 3; + } + break; + case dd: + orderNum+=(31-dd); + break; + case mm: + orderNum+=(12-month); + break; + default: + } + return orderNum; + } + + private String addZeroPrefix(Integer num) { + return (num < 10 && num > 0) ? "0" + num : num.toString(); + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyExcel.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyExcel.java new file mode 100644 index 0000000..c45af10 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/excel/PricePeakValleyExcel.java @@ -0,0 +1,113 @@ +package com.thing.carbon.peakvalley.excel; + +import com.thing.carbon.peakvalley.dto.PeakValleyReportReq; +import com.thing.common.core.annotation.CustomExcel; +import com.thing.common.core.annotation.CustomExcelCollection; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.apache.commons.lang3.tuple.MutablePair; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.poi.ss.util.CellRangeAddress; + +import java.math.BigDecimal; +import java.util.*; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class PricePeakValleyExcel { + + + @CustomExcel(name = "结构", width = 25) + private String thingName; + @CustomExcel(name = "能源品种",orderNum=1) + private String baseName; + @CustomExcel(name = "类型",orderNum=2) + private String attrName; + @CustomExcel(name = "总量",orderNum=3) + private String allDosage; + @CustomExcel(name = "单价",orderNum=4) + private String uintPrice; + @CustomExcel(name = "总价",orderNum=5) + private String totalPrice; + @CustomExcelCollection + private List peakValleyDosageReqs; + + + + + public static List createBeans(List reqs, String dataType){ + List result = new ArrayList<>(); + reqs.forEach(temp->{ + PricePeakValleyExcel excelBean = new PricePeakValleyExcel(); + excelBean.setThingName(temp.getThingName()); + excelBean.setBaseName(temp.getBaseName()+"("+temp.getBaseUnit()+")"); + excelBean.setAllDosage(Objects.nonNull(temp.getAllDosage()) ? temp.getAllDosage().toString() : "--"); + excelBean.setUintPrice(Objects.nonNull(temp.getUintPrice()) ? temp.getUintPrice()+"" : "--"); + excelBean.setTotalPrice(Objects.nonNull(temp.getTotalPrice()) ? temp.getTotalPrice()+"" : "--"); + excelBean.setAttrName(temp.getAttrName()); + List dosageExcels = new ArrayList<>(); + if(temp.getPeakValleyDosageReqs()!=null){ + temp.getPeakValleyDosageReqs().forEach(info->{ + //特殊处理,只要当前时间之前的数据 + if(info.getTs()==null||info.getTs()<=System.currentTimeMillis()){ + PricePeakValleyDosageExcel dosageExcel = PricePeakValleyDosageExcel.crateBean(info,dataType); + dosageExcels.add(dosageExcel); + } + }); + } + excelBean.setPeakValleyDosageReqs(dosageExcels); + result.add(excelBean); + }); + return result; + } + + @SuppressWarnings({"unchecked"}) + public static List calculateMergeRegion(List excelDataList) { + List mergeRegionList = new ArrayList<>(); + Map> thingCountMap = new HashMap<>(); + Map> thingEnergyCountMap = new HashMap<>(); + int beginRowOfThing =1 ; + int beginRowOfEnergy =1; + for (PricePeakValleyExcel currentData : excelDataList) { + if (!thingCountMap.containsKey(currentData.getThingName())) { + thingCountMap.put( + currentData.getThingName(), + MutablePair.of(++beginRowOfThing, beginRowOfThing)); + } else { + Pair thingCountPair = + thingCountMap.get(currentData.getThingName()); + thingCountPair.setValue(++beginRowOfThing); + } + + String energyKey = currentData.getThingName() + "_" + currentData.getBaseName(); + if (!thingEnergyCountMap.containsKey(energyKey)) { + thingEnergyCountMap.put( + energyKey, MutablePair.of(++beginRowOfEnergy, beginRowOfEnergy)); + } else { + Pair energyCountPair = thingEnergyCountMap.get(energyKey); + energyCountPair.setValue(++beginRowOfEnergy); + } + } + thingCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 0, 0)); + }); + thingEnergyCountMap.forEach( + (k, pair) -> { + if (Objects.equals(pair.getLeft(), pair.getRight())) { + return; + } + mergeRegionList.add( + new CellRangeAddress(pair.getLeft(), pair.getRight(), 1, 1)); + }); + + return mergeRegionList; + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/AnalysisService.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/AnalysisService.java new file mode 100644 index 0000000..cd212bd --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/AnalysisService.java @@ -0,0 +1,31 @@ +package com.thing.carbon.peakvalley.service; + + +import com.thing.carbon.peakvalley.dto.PeakValleyPriceStatisticsInfo; + +import java.math.BigDecimal; + +/** + * Author: SiYang + * Date: 2023/12/08 10:34 + * Description: 尖峰谷平分析服务接口 + */ +public interface AnalysisService { + + /** + * 生成尖峰谷平分析结论 + * + * @param thingId 物id + * @param attrCode 属性编码 + * @param attrUnit 属性单位 + * @param usagePercent 用量占比 + * @param allUsage 尖峰谷平类型总用量 + * @return 分析结论 + */ + String generateAnalysisResult( + Long thingId, + String attrCode, + String attrUnit, + BigDecimal usagePercent, + BigDecimal allUsage, PeakValleyPriceStatisticsInfo statisticsInfo ); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/EnergyBalanceService.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/EnergyBalanceService.java new file mode 100644 index 0000000..997a91e --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/EnergyBalanceService.java @@ -0,0 +1,14 @@ +package com.thing.carbon.peakvalley.service; + + + +import com.thing.carbon.peakvalley.dto.EnergyBalanceReq; +import com.thing.carbon.peakvalley.dto.PeakValleyExportParam; + +import java.util.List; + +public interface EnergyBalanceService { + + + List balanceReport(PeakValleyExportParam param); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/PeakValleyService.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/PeakValleyService.java new file mode 100644 index 0000000..62d0078 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/PeakValleyService.java @@ -0,0 +1,29 @@ +package com.thing.carbon.peakvalley.service; + + + +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.carbon.peakvalley.dto.*; + +import java.util.List; +import java.util.Map; + +public interface PeakValleyService { + PeakValleyAnalysisReq analysis(PeakValleyParam param); + + List peakReprot(PeakValleyParam param); + + CostStatisticsReq costReport(PeakValleyParam param); + + List energy(); + + Map> peakValleyReport(PeakValleyParam param); + + List> transformerReport(PeakValleyParam param); + + List peakValleyReportByKey(PeakValleyParam param); + + + List queryEnergyList(); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/AnalysisServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/AnalysisServiceImpl.java new file mode 100644 index 0000000..0ace6de --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/AnalysisServiceImpl.java @@ -0,0 +1,176 @@ +package com.thing.carbon.peakvalley.service.impl; + +import com.thing.alarm.alarm.dto.AlarmConfigDTO; +import com.thing.alarm.alarm.service.AlarmConfigService; +import com.thing.carbon.peakvalley.dto.PeakValleyPriceStatisticsInfo; +import com.thing.carbon.peakvalley.service.AnalysisService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.enumeration.EnergyVarietyEnum; +import com.thing.common.core.enumeration.PeakValleyEnum; +import com.thing.common.core.enumeration.SignEnum; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * Author: SiYang + * Date: 2023/12/08 10:48 + * Description: 尖峰谷平分析服务实现 + */ +@Service +@RequiredArgsConstructor +public class AnalysisServiceImpl implements AnalysisService { + + private final AlarmConfigService alarmConfigService; + + @Override + public String generateAnalysisResult( + Long thingId, + String attrCode, + String attrUnit, + BigDecimal usagePercent, + BigDecimal allUsage, PeakValleyPriceStatisticsInfo statisticsInfo ) { + String noSuffixAttrCode = AttributeTypeEnum.removeSuffix(attrCode); + List deviceConfigList = alarmConfigService.findByThingId(thingId); + AlarmConfigDTO config = + deviceConfigList.stream() + .filter(c -> Objects.equals(c.getAttrCode(), noSuffixAttrCode)) + .findFirst() + .orElse(null); + + PeakValleyEnum peakValleyEnum = PeakValleyEnum.match(attrCode); + EnergyVarietyEnum energyVarietyEnum = EnergyVarietyEnum.getByPrefix(getAttrPrefix(attrCode)); + + StringBuilder description = + generateBaseDescription( + energyVarietyEnum, peakValleyEnum, usagePercent, allUsage, attrUnit); + + if (Objects.isNull(config)) { + return generateNonConfigDescription(description, energyVarietyEnum, peakValleyEnum).toString(); + } + statisticsInfo.setStandardRatio(new BigDecimal(config.getFineVal())); + if (isAlarm(usagePercent, config)) { + return generateAlarmDescription(description, config, energyVarietyEnum, peakValleyEnum).toString(); + } + + if (isFine(usagePercent, config)) { + return generateFineDescription(description, config).toString(); + } + return generateNormalDescription(description).toString(); + } + + private boolean isAlarm(BigDecimal usagePercent, AlarmConfigDTO config) { + SignEnum alarmSign = SignEnum.match(config.getAlarmRule()); + String alarmValStr = config.getAlarmVal(); + BigDecimal alarmVal = BigDecimal.valueOf(Double.parseDouble(alarmValStr)); + return alarmSign.compare(usagePercent, alarmVal); + } + + private boolean isFine(BigDecimal usagePercent, AlarmConfigDTO config) { + SignEnum fineSign = SignEnum.match(config.getFineRule()); + String fineValStr = config.getFineVal(); + BigDecimal fineVal = BigDecimal.valueOf(Double.parseDouble(fineValStr)); + return fineSign.compare(usagePercent, fineVal); + } + + private String getAttrPrefix(String attrCode) { + return String.valueOf(attrCode.charAt(0)); + } + + /** + * 生成基础描述信息 + * + * @param energyVarietyEnum 能源品种枚举 + * @param peakValleyEnum 尖峰谷平枚举 + * @param usagePercent 用量占比 + * @param allUsage 总用量 + * @param attrUnit 属性单位 + * @return 基础描述 + */ + private StringBuilder generateBaseDescription( + EnergyVarietyEnum energyVarietyEnum, + PeakValleyEnum peakValleyEnum, + BigDecimal usagePercent, + BigDecimal allUsage, + String attrUnit) { + StringBuilder description = new StringBuilder(); + description + .append(peakValleyEnum.getName()) + .append("段") + .append(energyVarietyEnum.getName()) + .append("量占总") + .append(energyVarietyEnum.getName()) + .append("量的比值为") + .append(usagePercent) + .append("%, 用") + .append(energyVarietyEnum.getName()) + .append("总量为") + .append(allUsage) + .append(attrUnit) + .append(", "); + return description; + } + + private StringBuilder generateNonConfigDescription( + StringBuilder description, + EnergyVarietyEnum energyVarietyEnum, + PeakValleyEnum peakValleyEnum) { + + description + .append("未设置") + .append(peakValleyEnum.getName()) + .append("段") + .append(energyVarietyEnum.getName()) + .append("量指标。"); + + return description; + } + + private StringBuilder generateAlarmDescription( + StringBuilder description, + AlarmConfigDTO config, + EnergyVarietyEnum energyVarietyEnum, + PeakValleyEnum peakValleyEnum) { + SignEnum alarmSign = SignEnum.match(config.getAlarmRule()); + + description + .append(alarmSign.getName()) + .append("指标值") + .append(config.getAlarmVal()) + .append("%") + .append("应") + .append(getOption(alarmSign)) + .append(peakValleyEnum.getName()) + .append("段") + .append(energyVarietyEnum.getName()) + .append("量。"); + + return description; + } + + private StringBuilder generateFineDescription(StringBuilder description, AlarmConfigDTO config) { + description.append("优于先进值").append(config.getFineVal()).append("%, 请继续保持。"); + return description; + } + + private StringBuilder generateNormalDescription(StringBuilder description) { + description.append("处于正常范围内。"); + return description; + } + + private String getOption(SignEnum alarmSign) { + switch (alarmSign) { + case GT: + case GE: + return "减少"; + case LT: + case LE: + return "增加"; + default: + return ""; + } + } +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/EnergyBalanceServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/EnergyBalanceServiceImpl.java new file mode 100644 index 0000000..afa3d03 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/EnergyBalanceServiceImpl.java @@ -0,0 +1,133 @@ +package com.thing.carbon.peakvalley.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.carbon.peakvalley.dto.EnergyBalanceReq; +import com.thing.carbon.peakvalley.dto.PeakValleyExportParam; +import com.thing.carbon.peakvalley.dto.ThingAttrDosageReq; +import com.thing.carbon.peakvalley.service.EnergyBalanceService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.*; + +@Service +public class EnergyBalanceServiceImpl implements EnergyBalanceService { + + @Resource + private TsKvService tsKvService; + @Resource + private ThingManageContextService thingManageContextService; + + @Override + public List balanceReport(PeakValleyExportParam param) { + List result = new ArrayList<>(); + Map allMap = new HashMap<>(); + Map hJMap = new HashMap<>(); + Map cYMap; + + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + long startTs = DateTimeUtils.parse(timeRange.get(0)); + long endTs = DateTimeUtils.parse(timeRange.get(timeRange.size() - 1)); + List attrCodes = new ArrayList<>(); + if (ObjectUtil.isEmpty(param.getAttrParams())) { + return null; + } + Map attrNameMap = new HashMap<>(); + Map attrUintMap = new HashMap<>(); + //初始化属性入参 + param.getAttrParams().forEach(temp -> { + attrCodes.add(temp.getAttrCode() + PeakValleyExportParam.dataTypeMap.get(param.getDateType())); + attrNameMap.put(temp.getAttrCode() + PeakValleyExportParam.dataTypeMap.get(param.getDateType()), temp.getAttrName()); + Optional optional = thingManageContextService.findDictByCode(temp.getAttrCode()); + if (optional.isPresent()) { + IotThingDictDTO dict = optional.get(); + attrUintMap.put(temp.getAttrCode() + PeakValleyExportParam.dataTypeMap.get(param.getDateType()), dict.getUnit()); + } else { + Optional optional1 = thingManageContextService.findDictByCode(temp.getAttrCode() + PeakValleyExportParam.dataTypeMap.get(param.getDateType())); + if (optional1.isPresent()) { + IotThingDictDTO dict = optional1.get(); + attrUintMap.put(temp.getAttrCode() + PeakValleyExportParam.dataTypeMap.get(param.getDateType()), dict.getUnit()); + } + } + }); + param.getThingParams().forEach(temp -> { + + List infoList = tsKvService.findTsKvByCodeAndAttrs(temp.getThingCode(), attrCodes, startTs, endTs, true); + + EnergyBalanceReq info = new EnergyBalanceReq(temp.getThingCode(), temp.getThingName(), temp.getSort()); + Map valMap = new HashMap<>(); + infoList.forEach(map -> valMap.put(map.getAttrKey(), new BigDecimal(map.getVal()))); + List dosageReqs = new ArrayList<>(); + attrCodes.forEach(a -> { + ThingAttrDosageReq dosageReq = new ThingAttrDosageReq(a, attrNameMap.get(a), attrUintMap.get(a), ObjectUtil.isNotEmpty(valMap.get(a)) ? valMap.get(a) : BigDecimal.ZERO); + dosageReqs.add(dosageReq); + if (temp.getSort() == -1) { + allMap.put(dosageReq.getAttrCode(), dosageReq.getDosage()); + } else { + //如果存在,累加 + if (ObjectUtil.isNotEmpty(hJMap.get(dosageReq.getAttrCode()))) { + BigDecimal dosage = hJMap.get(dosageReq.getAttrCode()); + hJMap.put(dosageReq.getAttrCode(), dosage.add(dosageReq.getDosage())); + } else { + hJMap.put(dosageReq.getAttrCode(), dosageReq.getDosage()); + } + } + info.setDosageReqs(dosageReqs); + }); + result.add(info); + }); + if (result.isEmpty()) { + return null; + } + cYMap = calculateDifference(allMap, hJMap); + //result 添加和值和差值这两条数据 + EnergyBalanceReq hJBean = new EnergyBalanceReq("HJ", "合计", 9998); + EnergyBalanceReq cYBean = new EnergyBalanceReq("CY", "差异", 9999); + List hJBeans = new ArrayList<>(); + List cYBeans = new ArrayList<>(); + for (String temp : attrCodes) { + ThingAttrDosageReq hj = new ThingAttrDosageReq(temp, attrNameMap.get(temp), attrUintMap.get(temp), ObjectUtil.isNotEmpty(hJMap.get(temp)) ? hJMap.get(temp) : BigDecimal.ZERO); + hJBeans.add(hj); + ThingAttrDosageReq cy = new ThingAttrDosageReq(temp, attrNameMap.get(temp), attrUintMap.get(temp), ObjectUtil.isNotEmpty(cYMap.get(temp)) ? cYMap.get(temp) : BigDecimal.ZERO); + cYBeans.add(cy); + hJBean.setDosageReqs(hJBeans); + cYBean.setDosageReqs(cYBeans); + } + result.add(hJBean); + result.add(cYBean); + result.sort(Comparator.comparingInt(EnergyBalanceReq::getSort)); + int size = result.size(); + for (int i = 0; i < size; i++) { + EnergyBalanceReq obj = result.get(i); + obj.setNumber(i + 1); + } + return result; + } + + + private Map calculateDifference(Map allMap, Map hJMap) { + Map diffMap = new HashMap<>(); + for (String key : allMap.keySet()) { + BigDecimal allMapValue = allMap.get(key); + if (hJMap.containsKey(key)) { + BigDecimal hJMapValue = hJMap.get(key); + BigDecimal diffValue = allMapValue.subtract(hJMapValue); + diffMap.put(key, diffValue); + } else { + diffMap.put(key, allMapValue); + } + } + return diffMap; + } + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/PeakValleyServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/PeakValleyServiceImpl.java new file mode 100644 index 0000000..b3a565a --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/peakvalley/service/impl/PeakValleyServiceImpl.java @@ -0,0 +1,826 @@ +package com.thing.carbon.peakvalley.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.carbon.config.entity.CarbonEnergyDictRelationEntity; +import com.thing.carbon.config.entity.CarbonEnergyPriceInfo; +import com.thing.carbon.config.entity.CarbonEnergyVarietyEntity; +import com.thing.carbon.config.mapper.CarbonEnergyDictRelationMapper; +import com.thing.carbon.config.mapper.CarbonEnergyPriceMapper; +import com.thing.carbon.config.mapper.CarbonPeakConfigMapper; +import com.thing.carbon.config.service.CarbonEnergyVarietyService; +import com.thing.carbon.energyrepory.dto.EnergyTimeLabelData; +import com.thing.carbon.energyrepory.dto.PeakValleyDosageReq; +import com.thing.carbon.peakvalley.dto.*; +import com.thing.carbon.peakvalley.service.AnalysisService; +import com.thing.carbon.peakvalley.service.PeakValleyService; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.time.temporal.WeekFields; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; + + +@Service +public class PeakValleyServiceImpl implements PeakValleyService { + + @Resource + private CarbonPeakConfigMapper carbonPeakConfigDao; + @Resource + private CarbonEnergyPriceMapper carbonEnergyPriceDao; + @Resource + private CarbonEnergyDictRelationMapper carbonEnergyDictRelationDao; + @Resource + private AnalysisService analysisService; + @Resource + private ThingManageContextService thingManageContextService; + @Resource + private TsKvService tsKvService; + @Resource + private CarbonEnergyVarietyService carbonEnergyVarietyService; + + @Override + public PeakValleyAnalysisReq analysis(PeakValleyParam param) { + PeakValleyAnalysisReq result = new PeakValleyAnalysisReq(); + List priceStatisticsInfos = new ArrayList<>(); + //判断当前企业 当前能源有无尖峰谷平配置,没有不进行查询 + if(!this.isHasConfig(param.getKey())){ + return null; + } + long startTs; + long endTs; + if(param.getDateType().equals("any")||param.getDateType().equals("week")){ + if(param.getStartTime()!=null){ + startTs = DateTimeUtils.dateToTimestamp(param.getStartTime()); + endTs = DateTimeUtils.dateToTimestamp(param.getEndTime()); + }else { + endTs = param.getBeginTime().atZone(ZoneOffset.UTC).toInstant().toEpochMilli(); + startTs = param.getBeginTime().minusDays(30).atZone(ZoneOffset.UTC).toInstant().toEpochMilli(); + } + }else { + if(param.getStartTime()!=null){ + startTs = DateTimeUtils.dateToTimestamp(param.getStartTime()); + endTs = DateTimeUtils.dateToTimestamp(param.getEndTime()); + }else { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + startTs = DateTimeUtils.parse(timeRange.get(0)); + endTs = DateTimeUtils.parse(timeRange.get(timeRange.size() - 1)); + } + } + if(param.getDateType().equals("am")||param.getDateType().equals("any")){ + param.setDateType("hh"); + }else if (param.getDateType().equals("week")){ + param.setDateType("dd"); + } + List attrList = new ArrayList<>(); + attrList.add(param.getKey()+"_peak"+param.getDateType()); + attrList.add(param.getKey()+"_rush"+param.getDateType()); + attrList.add(param.getKey()+"_valley"+param.getDateType()); + attrList.add(param.getKey()+"_normal"+param.getDateType()); + List tempList = queryList(param.getKey(),param.getThingCode(),attrList,startTs,endTs); + if(ObjectUtil.isEmpty(tempList)){ + return null; + } + tempList.sort(Comparator.comparing(PeakValleyDosageReq::getTs)); + result.setPeakValleyDosageReqs(tempList); + //按类型的将峰谷平数据集合 + Map> groupedByAttrCode = tempList.stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getAttrCode)); + PeakValleyDosageReq firstInfo = tempList.get(0); + result.setBaseCode(firstInfo.getBaseCode()); + result.setBaseName(firstInfo.getBaseName()); + result.setBaseUint(firstInfo.getUint()); + groupedByAttrCode.keySet().forEach(temp->{ + List reqs = groupedByAttrCode.get(temp); + PeakValleyPriceStatisticsInfo statisticsInfo = new PeakValleyPriceStatisticsInfo(); + PeakValleyDosageReq firstData = reqs.get(0); + // 计算 totalPrice 总和 + BigDecimal totalPriceSum = reqs.stream() + .map(PeakValleyDosageReq::getTotalPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + // 计算 dosage 总和 + BigDecimal dosageSum = reqs.stream() + .map(PeakValleyDosageReq::getDosage) + .reduce(BigDecimal.ZERO, BigDecimal::add); + statisticsInfo.setAllDosage(dosageSum.setScale(1, BigDecimal.ROUND_HALF_UP)); + statisticsInfo.setAllTotalPrice(totalPriceSum.setScale(1, BigDecimal.ROUND_HALF_UP)); + statisticsInfo.setBaseName(firstData.getBaseName()); + statisticsInfo.setAttrName(firstData.getAttrName()); + statisticsInfo.setAttrCode(firstData.getAttrCode()); + statisticsInfo.setUnitPrice(firstData.getUintPrice()); + statisticsInfo.setUnit(firstData.getUint()); + statisticsInfo.setSort(firstData.getSort()); + priceStatisticsInfos.add(statisticsInfo); + result.setSumPrice(result.getSumPrice().add(statisticsInfo.getAllTotalPrice())); + result.setSumDosage(result.getSumDosage().add(statisticsInfo.getAllDosage())); + }); + //计算占比 + priceStatisticsInfos.forEach(temp->{ + try { + temp.setAllDosageRatio(temp.getAllDosage().divide(result.getSumDosage(),8, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) + .setScale(1, BigDecimal.ROUND_HALF_UP)); + } catch (Exception e) { + temp.setAllDosageRatio(BigDecimal.ZERO); + } + try { + temp.setPriceRatio(temp.getAllTotalPrice().divide(result.getSumPrice(),8, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) + .setScale(1, BigDecimal.ROUND_HALF_UP)); + } catch (Exception e) { + temp.setPriceRatio(BigDecimal.ZERO); + } + //占比对应的告警描述 + temp.setDesc(analysisService.generateAnalysisResult(param.getThingId(), + temp.getAttrCode(),temp.getUnit(), + temp.getAllDosageRatio(),temp.getAllDosage(),temp)); + }); + //总量的集合 + PeakValleyPriceStatisticsInfo statisticsInfo = new PeakValleyPriceStatisticsInfo(); + statisticsInfo.setAttrName(result.getBaseName()+"总用量"); + statisticsInfo.setBaseName(result.getBaseName()); + statisticsInfo.setAttrCode(result.getBaseCode()); + statisticsInfo.setAllDosageRatio(BigDecimal.valueOf(100)); + statisticsInfo.setAllDosage(result.getSumDosage().setScale(1, BigDecimal.ROUND_HALF_UP)); + statisticsInfo.setAllTotalPrice(result.getSumPrice().setScale(1, BigDecimal.ROUND_HALF_UP)); + statisticsInfo.setSort(99); + statisticsInfo.setFlag(false); + statisticsInfo.setUnit(result.getBaseUint()); + priceStatisticsInfos.add(statisticsInfo); + priceStatisticsInfos.sort(Comparator.comparing(PeakValleyPriceStatisticsInfo::getSort).reversed()); + result.setPriceStatisticsInfos(priceStatisticsInfos); + + //格式化结果集合中的 用量,价格,单价,只保留一位小数 + result.getPeakValleyDosageReqs().forEach(temp->{ + temp.setDosage(temp.getDosage().setScale(1, BigDecimal.ROUND_HALF_UP)); + temp.setUintPrice(temp.getUintPrice().setScale(1, BigDecimal.ROUND_HALF_UP)); + temp.setTotalPrice(temp.getTotalPrice().setScale(1, BigDecimal.ROUND_HALF_UP)); + }); + Map> groupList = tempList.stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getAttrCode)); + List< List> peakValleyList = new ArrayList<>(); + groupList.keySet().forEach(bean->{ + peakValleyList.add(groupList.get(bean)); + }); + result.setPeakValleyList(peakValleyList); + return result; + } + + + @Override + public List peakReprot(PeakValleyParam param) { + List result = new ArrayList<>(); + List keys = this.queryEnergyKeyList(); + keys.forEach(key->{ + param.setKey(key); + PeakValleyAnalysisReq tempInfo = this.analysis(param); + if(ObjectUtil.isNotEmpty(tempInfo)){ + result.add(tempInfo); + } + }); + return result; + } + + @Override + public CostStatisticsReq costReport(PeakValleyParam param) { + CostStatisticsReq result = new CostStatisticsReq(); + List avgCosts = new ArrayList<>(); + List energyCostList = new ArrayList<>(); + List allCostList = new ArrayList<>(); + List keys = this.queryEnergyKeyList(); + keys.forEach(key->{ + param.setKey(key); + List tempInfo = this.costAnalysis(param,true); + if(ObjectUtil.isNotEmpty(tempInfo)){ + BigDecimal avgCost = tempInfo.stream() + .map(CostReq::getCost) + .reduce(BigDecimal.ZERO, BigDecimal::add); + CostReq firstBean = tempInfo.get(0); + CostReq avgBean = new CostReq(); + avgBean.setBaseCode(firstBean.getBaseCode()); + avgBean.setBaseName(firstBean.getBaseName()); + avgBean.setCost(avgCost); + avgCosts.add(avgBean); + energyCostList.addAll(tempInfo); + } + }); + if(ObjectUtil.isEmpty(energyCostList)){ + return null; + } + allCostList(energyCostList, allCostList); + energyCostList.sort(Comparator.comparing(CostReq::getTs).reversed()); + allCostList.sort(Comparator.comparing(CostReq::getTs).reversed()); + result.setSumCost(allCostList.stream() + .map(CostReq::getCost) + .reduce(BigDecimal.ZERO, BigDecimal::add)); + result.setMinCost(allCostList.stream() + .map(CostReq::getCost) + .min(Comparator.naturalOrder()) + .orElse(BigDecimal.ZERO)); + result.setMaxCost(allCostList.stream() + .map(CostReq::getCost) + .max(Comparator.naturalOrder()) + .orElse(BigDecimal.ZERO)); + result.setAvgCost(allCostList.stream() + .map(CostReq::getCost) + .reduce(BigDecimal.ZERO, BigDecimal::add) + .divide(BigDecimal.valueOf(allCostList.size()), 1, RoundingMode.HALF_UP)); + result.setAllCostList(allCostList); + + Map> groupedByAttrCode = energyCostList.stream() + .collect(Collectors.groupingBy(CostReq::getBaseCode)); + List> arrayList = new ArrayList<>(); + groupedByAttrCode.keySet().forEach(temp->{ + arrayList.add(groupedByAttrCode.get(temp)); + }); + result.setEnergyCostList(arrayList); + result.setAvgCosts(avgCosts); + //上个月/上一年的总费用 + result.setMom(this.calculateMoM(result.getSumCost(),this.getPreviousAllCost(param))); + formatReq(result); + + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + List energyTimeLabelData = this.generateEmptyData(timeRange, param.getDateType()); + + List costReqs = new ArrayList<>(); + + Map> collect = allCostList.stream() + .collect(Collectors.groupingBy(CostReq::getTs)); + + energyTimeLabelData.forEach(temp->{ + CostReq req = new CostReq(); + if(ObjectUtil.isNotEmpty(collect.get(temp.getTs()))){ + req = collect.get(temp.getTs()).get(0); + }else { + req= new CostReq(); + req.setTs(temp.getTs()); + req.setBaseCode(""); + req.setBaseName("总费用"); + } + costReqs.add(req); + + }); + result.setAllCostList(costReqs); + return result; + } + + + @Override + public List energy() { + List reqs = carbonPeakConfigDao.energy(UserContext.getTenantCode()); + if(ObjectUtil.isEmpty(reqs)){ + reqs = carbonPeakConfigDao.energy(1001L); + } + return reqs; + } + + + + @Override + public Map> peakValleyReport(PeakValleyParam param) { + Map> result = new HashMap<>(); + List keys = List.of(param.getKeys().split(",")); + keys.forEach(temp->{ + param.setKey(temp); + List info = this.peakValleyReportByKey(param); + result.put(temp,info); + }); + return result; + } + + + @Override + public List peakValleyReportByKey(PeakValleyParam param){ + String dataType = param.getDateType(); + List result = new ArrayList<>(); + PeakValleyAnalysisReq peakValleyAnalysisReq = this.analysis(param); + if(ObjectUtil.isEmpty(peakValleyAnalysisReq)){ + return null; + } + List timeRange; + if(dataType.equals("week")||dataType.equals("any")){ + if(param.getStartTime()!=null){ + timeRange= AttributeTypeEnum.getTimeRange(DateTimeUtils.parseDateTime(param.getStartTime()),(DateTimeUtils.parseDateTime(param.getEndTime()))); + }else { + param.setDateType("week"); + timeRange= AttributeTypeEnum.getTimeRange(param.getBeginTime().minusDays(30),param.getBeginTime()); + } + }else { + if(param.getStartTime()!=null){ + timeRange= AttributeTypeEnum.getTimeRange(DateTimeUtils.parseDateTime(param.getStartTime()),(DateTimeUtils.parseDateTime(param.getEndTime()))); + }else { + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + } + } + List energyTimeLabelData = this.generateEmptyData(timeRange, param.getDateType()); + + Map> groupList = peakValleyAnalysisReq.getPeakValleyDosageReqs().stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getAttrCode)); + + peakValleyAnalysisReq.getPriceStatisticsInfos().forEach(temp->{ + PeakValleyReportReq req = new PeakValleyReportReq(); + req.setThingCode(param.getThingCode()); + req.setThingName(param.getThingName()); + req.setBaseCode(param.getKey()); + req.setBaseName(temp.getBaseName()); + req.setAttrCode(temp.getAttrCode()); + req.setAttrName(temp.getAttrName()); + req.setAllDosage(temp.getAllDosage()); + req.setRealityRatio(temp.getAllDosageRatio()); + req.setStandardRatio(temp.getStandardRatio()); + req.setTotalPrice(temp.getAllTotalPrice()); + req.setTotalPriceRatio(temp.getPriceRatio()); + req.setSort(temp.getSort()); + req.setBaseUnit(temp.getUnit()); + req.setUintPrice(temp.getUnitPrice()); + if(!dataType.equals("any")){ + List dosageReqs = groupList.get(req.getAttrCode()); + if(ObjectUtil.isNotEmpty(dosageReqs)){ + Map> tsDosageList = dosageReqs.stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getTs)); + energyTimeLabelData.forEach(ts->{ + if(ObjectUtil.isEmpty(tsDosageList.get(ts.getTs()))){ + PeakValleyDosageReq pvBean = new PeakValleyDosageReq(); + pvBean.setTs(ts.getTs()); + pvBean.setBaseCode(req.getBaseCode()); + pvBean.setBaseName(req.getBaseName()); + pvBean.setAttrCode(req.getAttrCode()); + pvBean.setAttrName(req.getAttrName()); + dosageReqs.add(pvBean); + } + }); + dosageReqs.sort(Comparator.comparingLong(PeakValleyDosageReq::getTs).reversed()); + req.setPeakValleyDosageReqs(dosageReqs); + } + if(req.getSort()==99){ + List peakValleyDosageReqs = this.total(peakValleyAnalysisReq.getPeakValleyDosageReqs(),energyTimeLabelData); + peakValleyDosageReqs.sort(Comparator.comparingLong(PeakValleyDosageReq::getTs).reversed()); + req.setPeakValleyDosageReqs(peakValleyDosageReqs); + } + } + result.add(req); + result.sort(Comparator.comparing(PeakValleyReportReq::getSort)); + }); + //处理打标 + result.forEach(info->{ + info.getPeakValleyDosageReqs().forEach(t->{ + LocalDateTime time = DateTimeUtils.parseDateTime(t.getTs()); + //日期打标 + t.setYear(time.getYear()); + t.setMonth(time.getMonthValue()); + t.setWeekOfYear(time.get(WeekFields.ISO.weekOfYear())); + t.setDateOfMonth(time.getDayOfMonth()); + t.setDateOfWeek(time.getDayOfWeek().getValue()); + t.setHourOfDay(time.getHour()); + t.setLabel("item"); + }); + if(dataType.equals("week")){ + //按tWeekOfYear分组,获取每周得总计数据 + Map> weekDataMap = + info.getPeakValleyDosageReqs().stream() + .filter(item -> Objects.equals(item.getLabel(), "item")) + .collect(Collectors.groupingBy(PeakValleyDosageReq::getWeekOfYear)); + info.getPeakValleyDosageReqs().addAll(summary(weekDataMap)); + System.out.println(info.getPeakValleyDosageReqs().size()); + } + }); + return result; + } + + private Collection summary(Map> weekDataMap) { + List resultList = new ArrayList<>(); + weekDataMap.keySet().forEach(temp->{ + List reqs = weekDataMap.get(temp); + PeakValleyDosageReq dosageReq = reqs.stream().findFirst().get(); + if(ObjectUtil.isNotEmpty(reqs)){ + PeakValleyDosageReq info = new PeakValleyDosageReq(); + //用量 + BigDecimal dosage = reqs.stream() + .map(PeakValleyDosageReq::getDosage) + .reduce(BigDecimal.ZERO, (acc, val) -> val != null ? acc.add(val) : acc); + //总价 + BigDecimal totalPrice = reqs.stream() + .map(PeakValleyDosageReq::getTotalPrice) + .reduce(BigDecimal.ZERO, (acc, val) -> val != null ? acc.add(val) : acc); + info.setTs(System.currentTimeMillis()); + info.setDosage(dosage); + info.setTotalPrice(totalPrice); + info.setLabel("weekTotal"); + info.setYear(dosageReq.getYear()); + info.setWeekOfYear(dosageReq.getWeekOfYear()); + resultList.add(info); + } + + }); + return resultList; + } + + + @Override + public List> transformerReport(PeakValleyParam param) { + PeakValleyAnalysisReq req = this.analysis(param); + if(ObjectUtil.isNotEmpty(req)){ + return req.getPeakValleyList(); + } + return null; + } + + + private List total(List dosageReqs,List energyTimeLabelData){ + PeakValleyDosageReq dosageReq = dosageReqs.stream().findFirst().get(); + List resultList = new ArrayList<>(); + Map> groupList = dosageReqs.stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getTs)); + energyTimeLabelData.forEach(temp->{ + List infoList = groupList.get(temp.getTs()); + PeakValleyDosageReq req = new PeakValleyDosageReq(); + req.setUint(dosageReq.getUint()); + req.setTs(temp.getTs()); + req.setBaseCode(dosageReq.getBaseCode()); + req.setBaseName(dosageReq.getBaseName()); + req.setAttrCode(dosageReq.getBaseName()); + req.setAttrName(dosageReq.getBaseCode()); + if(ObjectUtil.isNotEmpty(infoList)){ + //用量 + BigDecimal dosage = infoList.stream() + .map(PeakValleyDosageReq::getDosage) + .reduce(BigDecimal.ZERO, BigDecimal::add); + //总价 + BigDecimal totalPrice = infoList.stream() + .map(PeakValleyDosageReq::getTotalPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + req.setDosage(dosage); + req.setTotalPrice(totalPrice); + } + resultList.add(req); + }); + return resultList; + } + + /** + * 费用分析,查询费用 + * @param param + * @return + */ + private List costAnalysis(PeakValleyParam param,Boolean flag){ + List result = new ArrayList<>(); + + AttributeTypeEnum attributeTypeEnum = AttributeTypeEnum.match(param.getDateType()); + List timeRange = new ArrayList<>(); + if(!flag){ + if(param.getDateType().equals("dd")){ + timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime().minusMonths(1)); + }else if (param.getDateType().equals("mm")){ + timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime().minusYears(1)); + } + }else { + timeRange = attributeTypeEnum.getTimeRange(param.getBeginTime()); + } + long startTs = DateTimeUtils.parse(timeRange.get(0)); + long endTs = DateTimeUtils.parse(timeRange.get(timeRange.size() - 1)); + try { + //判断是否存在峰尖谷平,来初始化attrList + List attrList = new ArrayList<>(); + Date currentDate = new Date(); + List priceInfos = carbonEnergyPriceDao.getPeakValleyPrice(param.getKey(), UserContext.getTenantCode(), currentDate); + if (priceInfos != null && !priceInfos.isEmpty()) { + if (!priceInfos.stream() + .map(CarbonEnergyPriceInfo::getBaseCode) + .anyMatch(param.getKey()::equals)) { + attrList.add(param.getKey()+"_peak"+param.getDateType()); + attrList.add(param.getKey()+"_rush"+param.getDateType()); + attrList.add(param.getKey()+"_valley"+param.getDateType()); + attrList.add(param.getKey()+"_normal"+param.getDateType()); + } else { + attrList.add(param.getKey()+param.getDateType()); + } + }else { + return null; + } + List tempList = queryList(param.getKey(),param.getThingCode(),attrList,startTs,endTs); + if(ObjectUtil.isEmpty(tempList)){ + return null; + } + Map> groupedByAttrCode = tempList.stream() + .collect(Collectors.groupingBy(PeakValleyDosageReq::getTs)); + groupedByAttrCode.keySet().forEach(ts->{ + CostReq req = new CostReq(); + req.setTs(ts); + List reqs = groupedByAttrCode.get(ts); + BigDecimal cost = reqs.stream() + .map(PeakValleyDosageReq::getTotalPrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); + req.setCost(cost); + PeakValleyDosageReq firstReq = reqs.stream().findFirst().orElse(null); + req.setBaseCode(firstReq.getBaseCode()); + req.setBaseName(firstReq.getBaseName()); + result.add(req); + }); + } catch (Exception e) { + // 异常处理 + e.printStackTrace(); + } + return result; + } + + + + + + /** + * 查询tskv数据,并补全单价,总价等信息 + * @param key + * @param thingCode + * @param attrCode + * @param startTime + * @param endTime + * @return + */ + private List queryList(String key, String thingCode, List attrCode, Long startTime, Long endTime){ + + List infoList = tsKvService.findTsKvByCodeAndAttrs(thingCode,attrCode,startTime,endTime,false); + List dosageReqs = infoList.stream() + .map(m -> new PeakValleyDosageReq( + m.getThingCode(), + m.getAttrKey(), + new BigDecimal(m.getVal()), + m.getTs())) + .collect(Collectors.toList()); + + PeakValleyPriceInfo priceInfo = this.getPeakValleyPrice(key); + dosageReqs.forEach(temp->{ + temp.setUint(priceInfo.getBaseUnit()); + if(ObjectUtil.isEmpty(temp.getUint())){ + Optional optional = thingManageContextService.findDictByCode(temp.getAttrCode()); + if(optional.isPresent()){ + IotThingDictDTO dict = optional.get(); + temp.setUint(dict.getUnit()); + } + } + //key取能源品种 + CarbonEnergyVarietyEntity entity = carbonEnergyVarietyService.selectByCode(key); + priceInfo.setBaseCode(entity.getCode()); + priceInfo.setBaseName(entity.getName()); + temp.setBaseCode(priceInfo.getBaseCode()); + temp.setBaseName(priceInfo.getBaseName()); + if(temp.getAttrCode().contains("peak")){ + temp.setAttrName(priceInfo.getPeakPriceBean()==null?"峰用"+priceInfo.getBaseName():priceInfo.getPeakPriceBean().getAttrName()); + temp.setUintPrice(priceInfo.getPeakPriceBean()==null?BigDecimal.ZERO:priceInfo.getPeakPriceBean().getPrice()); + temp.setSort(1); + }else if(temp.getAttrCode().contains("rush")){ + temp.setAttrName(priceInfo.getRushPriceBean()==null?"尖用"+priceInfo.getBaseName():priceInfo.getRushPriceBean().getAttrName()); + temp.setUintPrice(priceInfo.getRushPriceBean()==null?BigDecimal.ZERO:priceInfo.getRushPriceBean().getPrice()); + temp.setSort(2); + }else if(temp.getAttrCode().contains("valley")){ + temp.setAttrName(priceInfo.getValleyPriceBean()==null?"谷用"+priceInfo.getBaseName():priceInfo.getValleyPriceBean().getAttrName()); + temp.setUintPrice(priceInfo.getValleyPriceBean()==null?BigDecimal.ZERO:priceInfo.getValleyPriceBean().getPrice()); + temp.setSort(3); + }else if(temp.getAttrCode().contains("normal")){ + temp.setAttrName(priceInfo.getNormalPriceBean()==null?"平用"+priceInfo.getBaseName():priceInfo.getNormalPriceBean().getAttrName()); + temp.setUintPrice(priceInfo.getNormalPriceBean()==null?BigDecimal.ZERO:priceInfo.getNormalPriceBean().getPrice()); + temp.setSort(4); + }else { + temp.setAttrName(priceInfo.getBaseName()); + temp.setUintPrice(priceInfo.getBasePrice()); + } + temp.setTotalPrice(temp.getDosage().multiply(temp.getUintPrice())); + }); + return dosageReqs; + } + + + + + /** + * 当前企业,当前能源类型是否存在尖峰谷平配置,不存在就不计算 + * @param key + * @return + */ + private boolean isHasConfig(String key){ + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", UserContext.getTenantCode()); + wrapper.like("base_attr_code",key); + return carbonPeakConfigDao.selectCountByQuery(wrapper) != 0; + } + + /** + * 根据指定的key获取峰平谷尖价格信息 + * + * @param key 唯一标识 + * @return 返回包含峰平谷尖价格信息的PeakValleyPriceInfo对象,如果没有相关信息则返回空对象 + */ + private PeakValleyPriceInfo getPeakValleyPrice(String key) { + Date currentDate = new Date(); + PeakValleyPriceInfo info = new PeakValleyPriceInfo(); + info.setIsHasPeakValley(false); + try { + List priceInfos = carbonEnergyPriceDao.getPeakValleyPrice(key, UserContext.getTenantCode(), currentDate); + if (priceInfos != null && !priceInfos.isEmpty()) { + CarbonEnergyPriceInfo basePriceInfo = priceInfos.get(0); + info.setBaseCode(basePriceInfo.getBaseCode()); + info.setBaseName(basePriceInfo.getBaseName()); + info.setBaseUnit(basePriceInfo.getBaseUint()); + if (priceInfos.size() > 1) { + info.setIsHasPeakValley(true); + priceInfos.stream() + .filter(temp -> temp.getAttrCode().contains(info.getBaseCode())) + .findFirst() + .ifPresent(temp -> { + info.setBasePrice(temp.getPrice()); + }); + setAttrPrice(info::setPeakPriceBean, priceInfos, "peak"); + setAttrPrice(info::setRushPriceBean, priceInfos, "rush"); + setAttrPrice(info::setValleyPriceBean, priceInfos, "valley"); + setAttrPrice(info::setNormalPriceBean, priceInfos, "normal"); + } else { + info.setBasePrice(basePriceInfo.getPrice()); + } + } + } catch (Exception e) { + // 异常处理 + e.printStackTrace(); + } + + return info; + } + + /** + * 标签属性价格 + * @param setter + * @param priceInfos + * @param attrCode + */ + private void setAttrPrice(Consumer setter, List priceInfos, String attrCode) { + priceInfos.stream() + .filter(temp -> temp.getAttrCode().contains(attrCode)) + .findFirst() + .ifPresent(temp -> setter.accept(new AttrPriceInfo(temp.getAttrCode(), temp.getAttrName(), temp.getPrice()))); + } + + /** + * 获取能源品种编码集合 + * @return + */ + + public List queryEnergyKeyList(){ + //获取对应的能源品种的属性和名称 + List energys = + carbonEnergyDictRelationDao.selectListByQuery(QueryWrapper.create() + .eq(CarbonEnergyDictRelationEntity::getAttrType, "base")); + //.eq(CarbonEnergyDictRelationEntity::getTenantCode, UserContext.getTe nantCode())); + //获取能源品种 + if(CollectionUtil.isEmpty(energys)){ + throw new SysException("未配置用量属性信息"); + } + return energys.stream() + .map(CarbonEnergyDictRelationEntity::getAttrCode) + .collect(Collectors.toList()); + } + + + + /** + * 获取能源品种编码集合 + * @return + */ + @Override + public List queryEnergyList(){ + //获取对应的能源品种的属性和名称 + List energys = + carbonEnergyDictRelationDao.selectListByQuery(QueryWrapper.create() + .eq(CarbonEnergyDictRelationEntity::getAttrType, "base")); + //获取能源品种 + if(CollectionUtil.isEmpty(energys)){ + throw new SysException("未配置用量属性信息"); + } + return energys; + } + + + + + /** + * 上一个周期的总费用 + * @param param + * @return + */ + private BigDecimal getPreviousAllCost(PeakValleyParam param){ + List energyCostList = new ArrayList<>(); + List allCostList = new ArrayList<>(); + List keys = this.queryEnergyKeyList(); + keys.forEach(key->{ + param.setKey(key); + List tempInfo = this.costAnalysis(param,false); + if(ObjectUtil.isNotEmpty(tempInfo)){ + energyCostList.addAll(tempInfo); + } + }); + allCostList(energyCostList, allCostList); + return allCostList.stream() + .map(CostReq::getCost) + .reduce(BigDecimal.ZERO, BigDecimal::add); + } + + /** + * 相同时间的,总价 + * @param energyCostList + * @param allCostList + */ + private void allCostList(List energyCostList, List allCostList) { + Map> collect = energyCostList.stream() + .collect(Collectors.groupingBy(CostReq::getTs)); + collect.keySet().forEach(ts->{ + CostReq req = new CostReq(); + req.setTs(ts); + List reqs = collect.get(ts); + BigDecimal cost = reqs.stream() + .map(CostReq::getCost) + .reduce(BigDecimal.ZERO, BigDecimal::add); + req.setCost(cost); + req.setBaseCode(""); + req.setBaseName("总费用"); + allCostList.add(req); + }); + } + + + /** + * 计算 a 与b的同比 + * @param a 当前月份 + * @param b 上一月 + * @return + */ + public BigDecimal calculateMoM(BigDecimal a, BigDecimal b) { + if (a.equals(BigDecimal.ZERO)) { + return BigDecimal.ZERO; + } + + BigDecimal difference = b.subtract(a); + try { + return difference.divide(a, 3, BigDecimal.ROUND_HALF_UP).multiply(BigDecimal.valueOf(100)).setScale(1,RoundingMode.HALF_UP); + } catch (Exception e) { + return BigDecimal.ZERO; + } + } + + + + /** + * 格式化能效统计 用能费用的响应结果 + * @param result + */ + private void formatReq(CostStatisticsReq result ){ + result.setMinCost(result.getMinCost().setScale(1,RoundingMode.HALF_UP)); + result.setSumCost(result.getSumCost().setScale(1,RoundingMode.HALF_UP)); + result.setMaxCost(result.getMaxCost().setScale(1,RoundingMode.HALF_UP)); + result.getAvgCosts().forEach(temp->{ + temp.setCost(temp.getCost().setScale(1,RoundingMode.HALF_UP)); + }); + result.getAllCostList().forEach(temp->{ + temp.setCost(temp.getCost().setScale(1,RoundingMode.HALF_UP)); + }); + result.getEnergyCostList().forEach(temp->{ + temp.forEach(info->{ + info.setCost(info.getCost().setScale(1,RoundingMode.HALF_UP)); + }); + }); + } + + + + /** + * 按指定时间范围生成全量空数据 + * @param timeRange 时间范围 + * @param attrType 属性类型 + * @return 空数据列表 + */ + private List generateEmptyData( + List timeRange, String attrType) { + return timeRange.stream() + .map( + t -> + new EnergyTimeLabelData( + DateTimeUtils.parse(t), + EnergyTimeLabelData.getByAttrType(attrType), + null)) + .collect(Collectors.toList()); + } + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/controller/AppletController.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/controller/AppletController.java new file mode 100644 index 0000000..222eed8 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/controller/AppletController.java @@ -0,0 +1,57 @@ +package com.thing.carbon.xiaochengxu.controller; + + +import com.thing.carbon.xiaochengxu.dto.DataResultDTO; +import com.thing.carbon.xiaochengxu.dto.ThingInfoDTO; +import com.thing.carbon.xiaochengxu.service.AppletService; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.web.response.Result; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +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; + + +import java.util.Map; + + +/** + * @author xzw + */ +@RestController +@RequestMapping("applet") +@Tag(name="小程序接口") +public class AppletController { + @Autowired + private AppletService appletService; + + + @GetMapping("getCompanyThingCode") + @Operation(summary="获取机构总的物code") + @LogOperation("获取机构总的物code") + public Result getCompanyThingCode(){ + ThingInfoDTO result = appletService.getCompanyThingCode(); + return new Result().ok(result); + } + + @GetMapping("getEachData") + @Operation(summary="首页总数据(各种数据)") + @LogOperation("首页总数据(各种数据)") + @Parameters({ + @Parameter(name = "thingCode", description = "物code"), + @Parameter(name = "date", description = "日时间,默认传当天时间 格式2024-01-04") + }) + public Result getEachData( @RequestParam Map params){ + DataResultDTO result = appletService.getEachData(params); + return new Result().ok(result); + } + + + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/AttibuteValueDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/AttibuteValueDTO.java new file mode 100644 index 0000000..f527ef7 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/AttibuteValueDTO.java @@ -0,0 +1,25 @@ +package com.thing.carbon.xiaochengxu.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; + + +/** + * @author xzw + */ +@Data +@Schema( name= "动态属性值") +public class AttibuteValueDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "名称") + private String name; + + @Schema(description = "值") + private BigDecimal value; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/DataResultDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/DataResultDTO.java new file mode 100644 index 0000000..3b64158 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/DataResultDTO.java @@ -0,0 +1,59 @@ +package com.thing.carbon.xiaochengxu.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + + +/** + * @author xzw + */ +@Data +@Schema( name= "数据") +public class DataResultDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "年能耗总量") + private BigDecimal yearConsumption; + + @Schema(description = "月能耗总量") + private BigDecimal monthConsumption; + + @Schema(description = "日能耗总量") + private BigDecimal dayConsumption; + + @Schema(description = "年碳排总量") + private BigDecimal yearCarbon; + + @Schema(description = "月碳排总量") + private BigDecimal monthCarbon; + + @Schema(description = "日碳排总量") + private BigDecimal dayCarbon; + +// @Schema(description = "日用电") +// private BigDecimal dayElectric; +// +// @Schema(description = "日用水") +// private BigDecimal dayWater; +// +// @Schema(description = "日天然气") +// private BigDecimal dayGas; +// +// @Schema(description = "日蒸汽") +// private BigDecimal daySteam; + + @Schema(description = "在线数量") + private Integer onlineCount; + + @Schema(description = "离线数量") + private Integer offlineCount; + + @Schema(description = "动态属性数据") + private List attitudeValues; + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/ThingInfoDTO.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/ThingInfoDTO.java new file mode 100644 index 0000000..b676490 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/dto/ThingInfoDTO.java @@ -0,0 +1,28 @@ +package com.thing.carbon.xiaochengxu.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serializable; + + +/** + * @author xzw + */ +@Data +@Schema( name= "物属性") +public class ThingInfoDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + + @Schema(description = "物实体编码") + private String code; + + @Schema(description = "名称") + private String name; + + +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/AppletService.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/AppletService.java new file mode 100644 index 0000000..76f5a06 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/AppletService.java @@ -0,0 +1,18 @@ +package com.thing.carbon.xiaochengxu.service; + + + +import com.thing.carbon.xiaochengxu.dto.DataResultDTO; +import com.thing.carbon.xiaochengxu.dto.ThingInfoDTO; + +import java.util.Map; + +/** + * @author xzw + */ +public interface AppletService { + + DataResultDTO getEachData(Map params); + + ThingInfoDTO getCompanyThingCode(); +} diff --git a/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/impl/AppletServiceImpl.java b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/impl/AppletServiceImpl.java new file mode 100644 index 0000000..da1db19 --- /dev/null +++ b/modules/report-analysis/src/main/java/com/thing/carbon/xiaochengxu/service/impl/AppletServiceImpl.java @@ -0,0 +1,167 @@ +package com.thing.carbon.xiaochengxu.service.impl; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.carbon.config.dto.CarbonEnergyDictRelationDTO; +import com.thing.carbon.config.service.CarbonEnergyDictRelationService; +import com.thing.carbon.xiaochengxu.dto.AttibuteValueDTO; +import com.thing.carbon.xiaochengxu.dto.DataResultDTO; +import com.thing.carbon.xiaochengxu.dto.ThingInfoDTO; +import com.thing.carbon.xiaochengxu.service.AppletService; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.TemplateMark; +import com.thing.common.core.enumeration.ThingStatus; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Service +@Slf4j +public class AppletServiceImpl implements AppletService { + + @Resource + private SysTenantService sysTenantService; + + @Resource + private TsKvService tsKvService; + + @Resource + private CarbonEnergyDictRelationService carbonEnergyDictRelationService; + + @Resource + private ThingManageContextService thingManageContextService; + + @Override + public DataResultDTO getEachData(Map params) { + DataResultDTO result = new DataResultDTO(); + Map thingParams = new HashMap<>(); + Long tenantCode = UserContext.getRealTenantCode(); + Long companyId = UserContext.getRealCompanyId(); + thingParams.put("tenantCode", tenantCode); + thingParams.put("companyId", companyId); + thingParams.put("enableStatus", "1"); + Optional> optionalList = thingManageContextService.findViewAllEntity(null, null, null, null, tenantCode, null, null, null, "1", TemplateMark.NO.getValue()); + long online = optionalList.orElseGet(Collections::emptyList).stream().filter(item -> ThingStatus.ONLINE.getCode().equals(item.get(CacheNameEnum.EntityField.THING_ENTITY_STATUS.getField()).asText())).count(); + long offline = optionalList.orElseGet(Collections::emptyList).stream().filter(item -> ThingStatus.OFFLINE.getCode().equals(item.get(CacheNameEnum.EntityField.THING_ENTITY_STATUS.getField()).asText())).count(); + result.setOnlineCount((int) online); + result.setOfflineCount((int) offline); + + String thingCode = params.get("thingCode").toString(); + String date = params.get("date").toString(); + + Long yearStartTime = DateTimeUtils.dateToStamp(date.substring(0,4)+"-01-01 00:00:00"); + Long monthStartTime = DateTimeUtils.dateToStamp(date.substring(0,7)+"-01 00:00:00"); + Long dayStartTime = DateTimeUtils.dateToStamp(date+" 00:00:00"); + + Optional optional = thingManageContextService.findEntityByCode(thingCode, UserContext.getRealTenantCode(), true); + if (optional.isPresent()){ + List keys1 = new ArrayList<>(); + keys1.add("tce_runyy"); + keys1.add("CO2_runyy"); + + List tsKvDTOS = tsKvService.findTsKvByCodeAndAttrs(thingCode, keys1, yearStartTime - 1000L, yearStartTime + 1000L, false); + if(CollectionUtils.isNotEmpty(tsKvDTOS)){ + Map> tsDataList = tsKvDTOS.stream().collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if (CollectionUtils.isNotEmpty(tsDataList.get("tce_runyy"))){ + result.setYearConsumption(new BigDecimal(tsDataList.get("tce_runyy").get(0).getVal())); + } + if (CollectionUtils.isNotEmpty(tsDataList.get("CO2_runyy"))){ + result.setYearCarbon(new BigDecimal(tsDataList.get("CO2_runyy").get(0).getVal())); + } + } + + List keys2 = new ArrayList<>(); + keys2.add("tce_runmm"); + keys2.add("CO2_runmm"); + + List singleDeviceDatas2 = tsKvService.findTsKvByCodeAndAttrs(thingCode, keys2, monthStartTime-1000L, monthStartTime+1000L, false); + if(CollectionUtils.isNotEmpty(singleDeviceDatas2)){ + Map> tsDataList = singleDeviceDatas2.stream().collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if (CollectionUtils.isNotEmpty(tsDataList.get("tce_runmm"))){ + result.setMonthConsumption(new BigDecimal(tsDataList.get("tce_runmm").get(0).getVal())); + } + if (CollectionUtils.isNotEmpty(tsDataList.get("CO2_runmm"))){ + result.setMonthCarbon(new BigDecimal(tsDataList.get("CO2_runmm").get(0).getVal())); + } + } + List keys3 = new ArrayList<>(); + keys3.add("tce_rundd"); + keys3.add("CO2_rundd"); + + List singleDeviceDatas3 = tsKvService.findTsKvByCodeAndAttrs(thingCode, keys3, dayStartTime-1000L, dayStartTime+1000L, false); + if(CollectionUtils.isNotEmpty(singleDeviceDatas3)){ + Map> tsDataList = singleDeviceDatas3.stream().collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if (CollectionUtils.isNotEmpty(tsDataList.get("tce_rundd"))){ + result.setDayConsumption(new BigDecimal(tsDataList.get("tce_rundd").get(0).getVal())); + } + if (CollectionUtils.isNotEmpty(tsDataList.get("CO2_rundd"))){ + result.setDayCarbon(new BigDecimal(tsDataList.get("CO2_rundd").get(0).getVal())); + } + } + + Map newParams = new HashMap<>(); + newParams.put("page",1); + newParams.put("limit",100); + newParams.put("attrType","base"); + //获取基础能源品种 + PageData page = carbonEnergyDictRelationService.handlePage(newParams); + List list = page.getList(); + List attitudeValues = new ArrayList<>(); + if (CollectionUtils.isNotEmpty(list)){ + for (CarbonEnergyDictRelationDTO dictRelationDTO:list){ + AttibuteValueDTO attibuteValueDTO = new AttibuteValueDTO(); + attibuteValueDTO.setName(dictRelationDTO.getEnergyVarietyName()); + attibuteValueDTO.setValue(null); + String key = dictRelationDTO.getEnergyVarietyCode(); + List keys4 = new ArrayList<>(); + keys4.add(key+"dd"); + List singleDeviceDatas4 = tsKvService.findTsKvByCodeAndAttrs(thingCode, keys4, dayStartTime-1000L, dayStartTime+1000L, false); + if(CollectionUtils.isNotEmpty(singleDeviceDatas4)){ + Map> tsDataList = singleDeviceDatas4.stream().collect(Collectors.groupingBy(TsKvDTO::getAttrKey)); + if (CollectionUtils.isNotEmpty(tsDataList.get(key+"dd"))){ + attibuteValueDTO.setValue(new BigDecimal(tsDataList.get(key+"dd").get(0).getVal())); + } + } + attitudeValues.add(attibuteValueDTO); + } + } + result.setAttitudeValues(attitudeValues); + } + + return result; + } + + @Override + public ThingInfoDTO getCompanyThingCode() { + ThingInfoDTO thingInfoDTO = new ThingInfoDTO(); + Long tenantCode = UserContext.getRealTenantCode(); + SysTenantDTO tenantCode1 = sysTenantService.getTenantCode(tenantCode); + + if (tenantCode1 != null){ + + Optional optional = thingManageContextService.findViewByCode(tenantCode1.getThingCode(), UserContext.getRealTenantCode(), true); + if (optional.isPresent()){ + thingInfoDTO.setId(optional.get().getEntityId()); + thingInfoDTO.setCode(optional.get().getEntityCode()); + thingInfoDTO.setName(optional.get().getEntityName()); + return thingInfoDTO; + } + } + return null; + } + +} diff --git a/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyDictRelationMapper.xml b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyDictRelationMapper.xml new file mode 100644 index 0000000..1bc6be3 --- /dev/null +++ b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyDictRelationMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + id, energy_variety_id, attr_code, attr_name, attr_type, group_name, tenant_code, company_id, dept_id, + creator, create_date, updater, update_date + + + + + insert into carbon_energy_dict_relation ( + + ) + values + + (#{item.id}, + #{item.energyVarietyId}, + #{item.attrCode}, + #{item.attrName}, + #{item.attrType}, + #{item.groupName}, + #{item.tenantCode}, + #{item.companyId}, + #{item.deptId}, + #{item.creator}, + #{currentTime}, + #{item.updater}, + #{currentTime} + ) + + ON CONFLICT (attr_code) + DO UPDATE SET (update_date, attr_name, energy_variety_id) + = (#{currentTime}, excluded.attr_name,excluded.energy_variety_id) + + + \ No newline at end of file diff --git a/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyPriceMapper.xml b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyPriceMapper.xml new file mode 100644 index 0000000..b4e2f5b --- /dev/null +++ b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyPriceMapper.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyVarietyMapper.xml b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyVarietyMapper.xml new file mode 100644 index 0000000..d8872e2 --- /dev/null +++ b/modules/report-analysis/src/main/resources/mapper/config/CarbonEnergyVarietyMapper.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/modules/report-analysis/src/main/resources/mapper/config/CarbonPeakConfigMapper.xml b/modules/report-analysis/src/main/resources/mapper/config/CarbonPeakConfigMapper.xml new file mode 100644 index 0000000..e02d2ea --- /dev/null +++ b/modules/report-analysis/src/main/resources/mapper/config/CarbonPeakConfigMapper.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/report-analysis/src/main/resources/mapper/config/CarbonTsKvMapper.xml b/modules/report-analysis/src/main/resources/mapper/config/CarbonTsKvMapper.xml new file mode 100644 index 0000000..4077bb2 --- /dev/null +++ b/modules/report-analysis/src/main/resources/mapper/config/CarbonTsKvMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + thing_id, thing_code, attr_key, ts, val, day_time, create_time, update_time, attribute_type + + + + insert into carbon_ts_kv ( ) + values + + (#{item.thingId}, + #{item.thingCode}, + #{item.attrKey}, + #{item.ts}, + #{item.val}, + #{item.dayTime}, + #{currentTime}, + #{currentTime}, + #{item.attributeType} + ) + + ON CONFLICT (thing_code, attr_key, ts) + DO UPDATE SET (update_time, val) = (#{currentTime}, excluded.val) + + + + delete from carbon_ts_kv + where thing_id = #{thingId} + and attr_key = #{attrKey} + and ts = #{ts} + + + \ No newline at end of file diff --git a/modules/thing/pom.xml b/modules/thing/pom.xml new file mode 100644 index 0000000..2e70038 --- /dev/null +++ b/modules/thing/pom.xml @@ -0,0 +1,169 @@ + + + 4.0.0 + + com.thing + modules + 5.1 + + + com.thing.modules + thing + jar + ThingBI Server Modules thing + + UTF-8 + + + + com.thing.common + cache + + + com.thing.common + tskv + + + com.thing.common + orm + + + com.thing.common + script + + + com.thing.common + security + + + com.thing.common + transport + + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-starter-websocket + + + + org.eclipse.paho + org.eclipse.paho.client.mqttv3 + + + + com.infiniteautomation + modbus4j + + + + com.aliyun.oss + aliyun-sdk-oss + + + + org.apache.shiro + shiro-spring + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-core + + + org.apache.shiro + shiro-web + + + + + + org.apache.shiro + shiro-core + jakarta + ${shiro.version} + + + commons-collections + commons-collections + + + + + + org.apache.shiro + shiro-web + jakarta + ${shiro.version} + + + org.apache.shiro + shiro-core + + + + + + com.github.tobato + fastdfs-client + + + + commons-beanutils + commons-beanutils + ${commons.beanutils.version} + + + commons-collections + commons-collections + + + + + + io.minio + minio + + + + com.google.code.gson + gson + + + + org.jsoup + jsoup + + + + com.aliyun + aliyun-java-sdk-dysmsapi + + + com.github.qcloudsms + qcloudsms + + + com.sun.mail + jakarta.mail + + + + org.freemarker + freemarker + + + com.thing.modules + msg + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/cache/controller/CacheTsKvController.java b/modules/thing/src/main/java/com/thing/cache/controller/CacheTsKvController.java new file mode 100644 index 0000000..b328c75 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/controller/CacheTsKvController.java @@ -0,0 +1,149 @@ +package com.thing.cache.controller; + +import com.thing.cache.dto.BatchTsKvModifyForm; +import com.thing.cache.dto.CacheTsKvDTO; +import com.thing.cache.dto.TsKvDeleteForm; +import com.thing.cache.dto.TsKvModifyForm; +import com.thing.cache.service.TsKvHandleService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + + +/** + * 物对应属性、值 + * + * @author zhangxin 949588190@qq.com + * @since 3.0 2022-01-12 + */ +@RestController +@RequestMapping("cache/tskv") +@Tag(name = "物对应属性、值") +public class CacheTsKvController { + + @Resource + private TsKvHandleService tsKvHandleService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingCodeList", description = "物编码list"), + @Parameter(name = "thingAttrList", description = "物属性list"), + @Parameter(name = "beginTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page(@RequestParam Map params) { + PageData page = tsKvHandleService.page(params); + return new Result>().ok(page); + } + + + @GetMapping("{thingCode}/{attrKey}/{ts}") + @Operation(summary="信息") + public Result get(@PathVariable String thingCode, @PathVariable String attrKey, @PathVariable Long ts){ + CacheTsKvDTO data = tsKvHandleService.get(thingCode, attrKey, ts); + return new Result().ok(data); + } + + + @PostMapping + @Operation(summary = "保存") + //@RequiresPermissions("cache:tskv:save") + public Result save(@RequestBody TsKvModifyForm dto) { + //效验数据 + //ValidatorUtils.validateEntity(dto, DefaultGroup.class); + tsKvHandleService.save(dto); + return new Result(); + } + + @PutMapping + @Operation(summary = "修改") + //@RequiresPermissions("cache:tskv:update") + public Result update(@RequestBody TsKvModifyForm dto) { + //效验数据 + // ValidatorUtils.validateEntity(dto, DefaultGroup.class); + tsKvHandleService.update(dto); + return new Result(); + } + + + @PutMapping("batchUpdate") + @Operation(summary = "批量修改") + public Result batchUpdate(@RequestBody List dtos) { + //效验数据 + dtos.forEach(dto -> { + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + tsKvHandleService.update(dto); + }); + return new Result(); + } + + + @DeleteMapping + @Operation(summary = "删除") +// @RequiresPermissions("cache:tskv:delete") + public Result delete(@RequestBody TsKvDeleteForm form) { + //效验数据 + ValidatorUtils.validateEntity(form, DefaultGroup.class); + tsKvHandleService.delete(form); + return new Result(); + } + + + @PostMapping("export") + @Operation(summary = "导出") + @Parameters({ + @Parameter(name = Constant.LIMIT, description = "分页记录数"), + @Parameter(name = "titleName", description = "标题"), + @Parameter(name = "thingCodeList", description = "物编码list"), + @Parameter(name = "thingAttrList", description = "物属性list"), + @Parameter(name = "beginTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public void export(@RequestParam Map params, HttpServletResponse response) throws Exception { + tsKvHandleService.exportExcel(params, response); + } + + @PostMapping("import") + @Operation(summary = "导入") +// @RequiresPermissions("cache:tskv:import") + public Result importExcel(MultipartFile file, HttpServletRequest request) throws Exception { + return tsKvHandleService.importExcel(file, request); + + } + + @PostMapping("template") + @Operation(summary = "模板下载") + public void exportTemplate(HttpServletResponse response) { + tsKvHandleService.template(response); + } + + @PostMapping("batchSave") + @Operation(summary = "按条件,批量生成数据保存") + public Result batchSave(@RequestBody List dto) { + //效验数据 + tsKvHandleService.batchSave(dto); + return new Result(); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/cache/dto/BatchTsKvModifyForm.java b/modules/thing/src/main/java/com/thing/cache/dto/BatchTsKvModifyForm.java new file mode 100644 index 0000000..9cda25c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/dto/BatchTsKvModifyForm.java @@ -0,0 +1,39 @@ +package com.thing.cache.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @author zhenghh. 2022-09-15 + **/ +@Data +@Schema( name= "设备属性值") +public class BatchTsKvModifyForm { + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "属性编码") + private String attrKey; + + @Schema(description = "开始时间") + private Long startTime; + + @Schema(description = "结束时间") + private Long endTime; + + @Schema(description = "采集频率(秒)") + private Integer frequency; + + @Schema(description = "保留小数点位数") + private Integer formatDecimal; + + @Schema(description = "基准值") + private String baseline; + + @Schema(description = "上限") + private String max; + + @Schema(description = "下限") + private String min; +} diff --git a/modules/thing/src/main/java/com/thing/cache/dto/CacheTsKvDTO.java b/modules/thing/src/main/java/com/thing/cache/dto/CacheTsKvDTO.java new file mode 100644 index 0000000..3ed7ba7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/dto/CacheTsKvDTO.java @@ -0,0 +1,63 @@ +package com.thing.cache.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.Date; + +/** +* 物对应属性、值 +* +* @author zhangxin 949588190@qq.com +* @since 3.0 2022-01-12 +*/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "物对应属性、值") +public class CacheTsKvDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 8857945057305462650L; + + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "属性key") + private String attrKey; + @Schema(description = "ts") + private Long ts; + @Schema(description = "值") + private String val; + @Schema(description = "ts时间") + private Date dayTime; + @Schema(description = "更新时间") + private Long createTime; + + private BigDecimal tceVal = new BigDecimal("0.0"); + + private BigDecimal tCO2Val= new BigDecimal("0.0"); + + + public CacheTsKvDTO(String thingCode, String attrKey, Long ts, String val) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.val = val; + this.dayTime = new Date(ts); + this.createTime = System.currentTimeMillis(); + } + + public CacheTsKvDTO(String thingCode, String attrKey, Long ts, String val, Long createTime) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.val = val; + this.dayTime = new Date(ts); + this.createTime = createTime == null ? System.currentTimeMillis() : createTime; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/cache/dto/TsKvDeleteForm.java b/modules/thing/src/main/java/com/thing/cache/dto/TsKvDeleteForm.java new file mode 100644 index 0000000..441b68e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/dto/TsKvDeleteForm.java @@ -0,0 +1,41 @@ +package com.thing.cache.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-09-15 + **/ +@Data +@Schema( name= "删除表单") +public class TsKvDeleteForm { + + @Schema(description = "列表") + @NotNull(message = "物编码不能为空", groups = DefaultGroup.class) + private List list; + + @Schema(description = "是否删除tb数据") + @NotNull(message = "物编码不能为空", groups = DefaultGroup.class) + private Boolean deleteTb; + + @Data + @Schema( name= "表单") + public static class Entity { + + @Schema(description = "设备编码") + @NotBlank(message = "设备编码不能为空", groups = DefaultGroup.class) + private String thingCode; + @Schema(description = "属性编码") + @NotBlank(message = "属性编码不能为空", groups = DefaultGroup.class) + private String attrKey; + @Schema(description = "时间") + @NotNull(message = "时间不能为空", groups = DefaultGroup.class) + private Long ts; + } + +} diff --git a/modules/thing/src/main/java/com/thing/cache/dto/TsKvModifyForm.java b/modules/thing/src/main/java/com/thing/cache/dto/TsKvModifyForm.java new file mode 100644 index 0000000..6f3bc6a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/dto/TsKvModifyForm.java @@ -0,0 +1,29 @@ +package com.thing.cache.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @author zhenghh. 2022-09-15 + **/ +@Data +@Schema( name= "设备属性值") +public class TsKvModifyForm { + + @Schema(description = "物编码") + @NotBlank(message = "物编码不能为空", groups = DefaultGroup.class) + private String thingCode; + @Schema(description = "属性编码") + @NotBlank(message = "属性编码不能为空", groups = DefaultGroup.class) + private String attrKey; + @Schema(description = "时间") + @NotNull(message = "时间不能为空", groups = DefaultGroup.class) + private Long ts; + @Schema(description = "属性值") + @NotBlank(message = "属性值不能为空", groups = DefaultGroup.class) + private String val; +} diff --git a/modules/thing/src/main/java/com/thing/cache/entity/CacheTsKvEntity.java b/modules/thing/src/main/java/com/thing/cache/entity/CacheTsKvEntity.java new file mode 100644 index 0000000..7020b16 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/entity/CacheTsKvEntity.java @@ -0,0 +1,47 @@ +package com.thing.cache.entity; + +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 物对应属性、值 + * + * @author zhangxin 949588190@qq.com + * @since 3.0 2022-01-12 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("thing_ts_kv") +public class CacheTsKvEntity implements Serializable { + + @Serial + private static final long serialVersionUID = -2606985673001633711L; + /** + * 物编码 + */ + private String thingCode; + /** + * 属性key + */ + private String attrKey; + /** + * ts + */ + private Long ts; + /** + * 值 + */ + private String val; + /** + * ts时间 + */ + private Date dayTime; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/cache/excel/CacheTsKvLastestExcel.java b/modules/thing/src/main/java/com/thing/cache/excel/CacheTsKvLastestExcel.java new file mode 100644 index 0000000..2c493fe --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/excel/CacheTsKvLastestExcel.java @@ -0,0 +1,25 @@ +package com.thing.cache.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; + +@Data +public class CacheTsKvLastestExcel { + + @Excel(name="物编码", orderNum = "1") + private String thingCode; + + @Excel(name="属性编码", orderNum = "2") + private String attrKey; + + @Excel(name="时间", orderNum = "3") + private String ts; + + @Excel(name="属性值", orderNum = "4") + private String val; + + public void setTs(Long ts) { + this.ts = DateTimeUtils.timestampToDate(ts, DateTimeUtils.DATE_TIME_PATTERN_STR); + } +} diff --git a/modules/thing/src/main/java/com/thing/cache/excel/TsKvModifyFormExcel.java b/modules/thing/src/main/java/com/thing/cache/excel/TsKvModifyFormExcel.java new file mode 100644 index 0000000..c20595e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/excel/TsKvModifyFormExcel.java @@ -0,0 +1,27 @@ +package com.thing.cache.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TsKvModifyFormExcel { + + @Schema(description = "物编码") + @Excel(name="物编码", orderNum = "1") + private String thingCode; + @Schema(description = "属性编码") + @Excel(name="属性编码", orderNum = "2") + private String attrKey; + @Schema(description = "时间") + @Excel(name="时间", orderNum = "3") + private String dataTime; + @Schema(description = "属性值") + @Excel(name="属性值", orderNum = "4") + private String val; + +} diff --git a/modules/thing/src/main/java/com/thing/cache/service/TsKvHandleService.java b/modules/thing/src/main/java/com/thing/cache/service/TsKvHandleService.java new file mode 100644 index 0000000..1b7e554 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/service/TsKvHandleService.java @@ -0,0 +1,39 @@ +package com.thing.cache.service; + +import com.thing.cache.dto.BatchTsKvModifyForm; +import com.thing.cache.dto.CacheTsKvDTO; +import com.thing.cache.dto.TsKvDeleteForm; +import com.thing.cache.dto.TsKvModifyForm; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * @author zhenghh. 2022-09-15 + **/ +public interface TsKvHandleService { + + PageData page(Map params); + + CacheTsKvDTO get(String thingCode, String key, Long ts); + + void save(TsKvModifyForm dto); + + void update(TsKvModifyForm dto); + + void delete(TsKvDeleteForm form); + + void exportExcel(Map params, HttpServletResponse response); + + void template(HttpServletResponse response); + + Result importExcel(MultipartFile file, HttpServletRequest request) throws Exception; + + void batchSave(List dto); +} diff --git a/modules/thing/src/main/java/com/thing/cache/service/impl/TsKvHandleServiceImpl.java b/modules/thing/src/main/java/com/thing/cache/service/impl/TsKvHandleServiceImpl.java new file mode 100644 index 0000000..b31cbd7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/cache/service/impl/TsKvHandleServiceImpl.java @@ -0,0 +1,256 @@ +package com.thing.cache.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.map.MapUtil; +import com.google.common.collect.Lists; +import com.thing.cache.dto.BatchTsKvModifyForm; +import com.thing.cache.dto.CacheTsKvDTO; +import com.thing.cache.dto.TsKvDeleteForm; +import com.thing.cache.dto.TsKvModifyForm; +import com.thing.cache.excel.CacheTsKvLastestExcel; +import com.thing.cache.excel.TsKvModifyFormExcel; +import com.thing.cache.service.TsKvHandleService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.event.TsKvEvent; +import com.thing.common.tskv.service.TsKvService; +import com.thing.queue.util.Topics; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-09-15 + **/ +@Slf4j +@Service +public class TsKvHandleServiceImpl implements TsKvHandleService { + + @Resource + private TsKvService tsKvService; + @Resource + private ApplicationEventPublisher publisher; + /** + * 分页查询 + * + * @param params 查询参数 + * @return page + */ + @Override + public PageData page(Map params) { + List thingCodeList = MapUtil.get(params, "thingCodeList", List.class); + List atterList = MapUtil.get(params, "thingAttrList", List.class); + Long startTime = MapUtil.getLong(params, "beginTime"); + Long endTime = MapUtil.getLong(params, "endTime"); + if (StringUtils.isNotBlank((String) params.get("beginTime"))) { + return tsKvService.findPageTsKvByCodesAndAttrs(thingCodeList, atterList, startTime, endTime, Boolean.FALSE, MapUtil.getInt(params, Constant.PAGE), MapUtil.getInt(params, Constant.LIMIT)); + } else { + Map> queryMap = new HashMap<>(); + if (thingCodeList != null) { + thingCodeList.forEach(temp -> queryMap.put(temp, atterList)); + } + + return tsKvService.findPageLatestByMultiMap(queryMap, Boolean.FALSE, MapUtil.getInt(params, Constant.PAGE), MapUtil.getInt(params, Constant.LIMIT)); + } + } + + @Override + public CacheTsKvDTO get(String thingCode, String key, Long ts) { + List tsKvByCodeAndAttr = tsKvService.findTsKvByCodeAndAttr(thingCode, key, ts, ts, false); + //判空 + if (tsKvByCodeAndAttr.isEmpty()) { + return null; + } + return ConvertUtils.sourceToTarget(tsKvByCodeAndAttr.get(0), CacheTsKvDTO.class); + } + + /** + * 保存 + * + * @param dto 入参 + */ + @Override + public void save(TsKvModifyForm dto) { + saveOrUpdateTskv(dto); + } + + + /** + * 保存 + * + * @param dto 入参 + */ + @Override + public void update(TsKvModifyForm dto) { + saveOrUpdateTskv(dto); + } + + /** + * 删除 + * + * @param form 删除条件 + */ + @Override + public void delete(TsKvDeleteForm form) { + form.getList().forEach(temp -> { + TsKvDTO kvDTO = new TsKvDTO(temp.getThingCode(), temp.getAttrKey(), temp.getTs(), null); + tsKvService.deleteTskv(kvDTO); + tsKvService.deleteLastTskv(kvDTO); + + Map> thingAttrMap = new HashMap<>(); + thingAttrMap.put(temp.getThingCode(),List.of(temp.getAttrKey())); + //重新计算 + tsKvService.amSumMaps(thingAttrMap, temp.getTs()-((15*60*1000)+1), temp.getTs()+((15*60*1000)+1)); + }); + } + + @Override + public void template(HttpServletResponse response) { + TsKvModifyFormExcel excel = new TsKvModifyFormExcel(); + excel.setThingCode("xx_code--数据样例,请修改"); + excel.setAttrKey("xx_attr"); + excel.setVal("100"); + excel.setDataTime("2022-01-01 15:00:00"); + ExcelUtils.exportExcel(Lists.newArrayList(excel), "原始数据表--请参考第三行数据填写", "原始数据表", TsKvModifyFormExcel.class, "template.xls", response); + } + + @Override + public Result importExcel(MultipartFile file, HttpServletRequest request) throws Exception { + List sheetData = ExcelUtils.importExcel(file, 1, 1, TsKvModifyFormExcel.class, 0); + if (CollectionUtil.isEmpty(sheetData)) { + throw new SysException("导入数据为空"); + } + //解析 调用save方法入库 + for (TsKvModifyFormExcel info : sheetData) { + TsKvModifyForm model = ConvertUtils.convertWithTypeAdapt(info, TsKvModifyForm.class); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + model.setTs(format.parse(info.getDataTime()).getTime()); + this.save(model); + } + return new Result().ok("导入成功"); + } + + @Override + public void batchSave(List tsKvModifyFormList) { + if(CollectionUtils.isEmpty(tsKvModifyFormList)){ + throw new SysException("批量保存数据为空"); + } + tsKvModifyFormList.forEach(dto -> { + if (dto.getMax() != null) { + dto.setMax(new BigDecimal(dto.getBaseline()).add(new BigDecimal(dto.getMax())).toString()); + if (dto.getMin() == null) { + dto.setMin(dto.getBaseline()); + } + } + if (dto.getMin() != null) { + if (!dto.getMin().equals(dto.getBaseline())) { + dto.setMin(new BigDecimal(dto.getBaseline()).subtract(new BigDecimal(dto.getMin())).toString()); + if (dto.getMax() == null) { + dto.setMax(dto.getBaseline()); + } + } + } + this.collectData(dto.getStartTime(), dto.getEndTime(), dto.getFrequency() * 1000, dto); + }); + } + + + /** + * 递归处理 + */ + public void collectData(long startTime, long endTime, int frequency, BatchTsKvModifyForm dto) { + if (startTime + frequency <= endTime) { + TsKvModifyForm param = new TsKvModifyForm(); + param.setThingCode(dto.getThingCode()); + param.setAttrKey(dto.getAttrKey()); + param.setTs(startTime); + if (StringUtils.isNotEmpty(dto.getMax()) && StringUtils.isNotEmpty(dto.getMin())) { + param.setVal(this.createVal(dto.getMax(), dto.getMin(), dto.getFormatDecimal())); + } else { + param.setVal(dto.getBaseline()); + } + this.save(param); + collectData(startTime + frequency, endTime, frequency, dto); + } + } + + + /** + * 生成val + */ + private String createVal(String max, String min, Integer formatDecimal) { + // 创建一个Random对象 + Random random = new Random(); + // 生成随机数 + double randomNumber = Double.parseDouble(min) + (Double.parseDouble(max) - Double.parseDouble(min)) * random.nextDouble(); + DecimalFormat decimalFormat; + if (null == formatDecimal || formatDecimal == 0) { + decimalFormat = new DecimalFormat("#"); + } else { + // 格式化输出,保留n位小数 + decimalFormat = new DecimalFormat("#0." + "0".repeat(Objects.requireNonNullElse(formatDecimal, 0))); + } + return decimalFormat.format(randomNumber); + } + + + /** + * 导出excel + * + * @param params 查询参数 + * @param response response + */ + @Override + public void exportExcel(Map params, HttpServletResponse response) { + Integer limit = Integer.parseInt((String) params.get("limit")); + List cacheTsKvExcelList = new ArrayList<>(); + Integer pageNum = 1; + boolean hashNext = Boolean.TRUE; + while (hashNext) { + params.put("page", String.valueOf(pageNum)); + PageData dtoPageData = page(params); + List list = dtoPageData.getList(); + if (CollectionUtil.isNotEmpty(list)) { + cacheTsKvExcelList.addAll(ConvertUtils.sourceToTarget(list, CacheTsKvLastestExcel.class)); + } + if (CompareUtil.compare(list.size(), limit) < 0) { + hashNext = Boolean.FALSE; + } + pageNum++; + } + String titleName = (String) params.get("titleName"); + ExcelUtils.exportExcel(cacheTsKvExcelList, titleName, null, CacheTsKvLastestExcel.class, titleName.concat(".xls"), response); + } + + /** + * 保存或修改tskv数据 + * + * @param dto + */ + private void saveOrUpdateTskv(TsKvModifyForm dto) { + List tsKvDTOList = new ArrayList<>(); + TsKvDTO kvDTO = new TsKvDTO(dto.getThingCode(), dto.getAttrKey(), dto.getTs(), dto.getVal()); + tsKvDTOList.add(kvDTO); + //send 队列 + publisher.publishEvent(new TsKvEvent(Topics.V1_TSKV_HISTORY.getValue(), TsKvDTO.toDataProtoList(tsKvDTOList))); + } + +} diff --git a/modules/thing/src/main/java/com/thing/config/CacheConfig.java b/modules/thing/src/main/java/com/thing/config/CacheConfig.java new file mode 100644 index 0000000..b12912b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/config/CacheConfig.java @@ -0,0 +1,17 @@ +package com.thing.config; + +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableCaching +public class CacheConfig { + + @Bean + public CustomKeyGenerator customKeyGenerator() { + return new CustomKeyGenerator(); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/config/CustomKeyGenerator.java b/modules/thing/src/main/java/com/thing/config/CustomKeyGenerator.java new file mode 100644 index 0000000..30eb498 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/config/CustomKeyGenerator.java @@ -0,0 +1,35 @@ +package com.thing.config; + +import com.thing.sys.security.context.UserContext; +import org.apache.commons.lang3.StringUtils; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.cache.interceptor.SimpleKeyGenerator; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@Component +public class CustomKeyGenerator implements KeyGenerator { + + + @Override + public Object generate(Object target, Method method, Object... params) { + String name = target.getClass().getName(); + String methodName = method.getClass().getName(); + if (params.length > 0 && params[0] instanceof Map) { + Map paramMap = (Map) params[0]; + // 将Map转换为一个稳定的字符串形式 + String tempStr = paramMap.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .filter(e -> !StringUtils.equals("_t",e.getKey())) + .filter(e -> Objects.nonNull(e.getValue())) + .map(e -> e.getKey() + "=" + e.getValue()) + .collect(Collectors.joining(",")); + return StringUtils.join(tempStr, name,methodName, UserContext.getRealTenantCode()); + } + return SimpleKeyGenerator.generateKey(params); + } +} diff --git a/modules/thing/src/main/java/com/thing/config/RelationProperties.java b/modules/thing/src/main/java/com/thing/config/RelationProperties.java new file mode 100644 index 0000000..39e78fc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/config/RelationProperties.java @@ -0,0 +1,41 @@ +package com.thing.config; + +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/01 15:59 + * Description: 关系配置信息 + */ +@Data +@Component +@ConfigurationProperties(prefix = "thing.relation") +public class RelationProperties { + + private List groups; + + /** + * 关系组信息 + */ + @Data + @NoArgsConstructor + public static class Group { + private Long id; + private String code; + private List configTypes; + + public Group(Long id, String code) { + this.id = id; + this.code = code; + } + } + + public Group buildGroup(Long id, String code) { + return new Group(id, code); + } +} diff --git a/modules/thing/src/main/java/com/thing/config/RestTemplateConfiguration.java b/modules/thing/src/main/java/com/thing/config/RestTemplateConfiguration.java new file mode 100644 index 0000000..db7b313 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/config/RestTemplateConfiguration.java @@ -0,0 +1,69 @@ +package com.thing.config; + +import org.apache.hc.client5.http.classic.HttpClient; +import org.apache.hc.client5.http.config.RequestConfig; +import org.apache.hc.client5.http.impl.classic.HttpClientBuilder; +import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager; +import org.apache.hc.client5.http.socket.ConnectionSocketFactory; +import org.apache.hc.client5.http.socket.PlainConnectionSocketFactory; +import org.apache.hc.client5.http.ssl.SSLConnectionSocketFactory; +import org.apache.hc.core5.http.config.Registry; +import org.apache.hc.core5.http.config.RegistryBuilder; +import org.apache.hc.core5.util.TimeValue; +import org.apache.hc.core5.util.Timeout; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +/** + * RestTemplate配置 + * + * @author zhenghh. 2020-11-06 + **/ +@Configuration +public class RestTemplateConfiguration { + + /** + * 服务器返回数据(response)的时间 + */ + private static final Integer READ_TIME_OUT = 5000; + /** + * 连接上服务器(握手成功)的时间 + */ + private static final Integer CONNECT_TIME_OUT = 5000; + + @Bean("tbRestTemplate") + public RestTemplate restTemplate() { + ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient()); + return new RestTemplate(requestFactory); + } + + @Bean + public HttpClient httpClient() { + // 支持HTTP、HTTPS + Registry registry = RegistryBuilder.create() + .register("http", PlainConnectionSocketFactory.getSocketFactory()) + .register("https", SSLConnectionSocketFactory.getSocketFactory()) + .build(); + PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager( + registry); + connectionManager.setMaxTotal(200); + connectionManager.setDefaultMaxPerRoute(100); + connectionManager.setValidateAfterInactivity(TimeValue.ofMilliseconds(2000)); + RequestConfig requestConfig = RequestConfig.custom() + // 服务器返回数据(response)的时间,超时抛出read timeout + .setResponseTimeout(Timeout.of(Duration.ofMillis(READ_TIME_OUT))) + // 连接上服务器(握手成功)的时间,超时抛出connect timeout + .setConnectTimeout(CONNECT_TIME_OUT, TimeUnit.MILLISECONDS) + // 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException + .setConnectionRequestTimeout(10000, TimeUnit.MILLISECONDS) + .build(); + return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig) + .setConnectionManager(connectionManager).build(); + } +} diff --git a/modules/thing/src/main/java/com/thing/config/UnsignedLongSerializer.java b/modules/thing/src/main/java/com/thing/config/UnsignedLongSerializer.java new file mode 100644 index 0000000..c72d11d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/config/UnsignedLongSerializer.java @@ -0,0 +1,16 @@ +package com.thing.config; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.clickhouse.data.value.UnsignedLong; + +public class UnsignedLongSerializer implements ObjectSerializer { + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, java.lang.reflect.Type fieldType, int features) { + if (object instanceof UnsignedLong) { + serializer.write(((UnsignedLong) object).longValue()); + } else { + serializer.write(object); + } + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlController.java b/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlController.java new file mode 100644 index 0000000..08de089 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlController.java @@ -0,0 +1,167 @@ +package com.thing.control.controller; + +import cn.hutool.core.collection.CollectionUtil; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.control.dto.*; +import com.thing.control.service.IotDeviceControlService; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@RestController +@RequestMapping("device/control") +@Tag(name = "设备控制") +@RequiredArgsConstructor +public class IotDeviceControlController { + private final IotDeviceControlService iotDeviceControlService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="控制名称"), + @Parameter(name = "device",description ="设备信息"), + @Parameter(name = "attr",description ="属性信息"), + @Parameter(name = "extendId",description ="通讯协议") + }) + public Result> page( @RequestParam Map params) { + PageData page = iotDeviceControlService.pageList(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list( @RequestParam Map params) { + List data = iotDeviceControlService.listAs(params, IotDeviceControlDTO.class); + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + IotDeviceControlDTO data = iotDeviceControlService.getByIdAs(id, IotDeviceControlDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotDeviceControlDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotDeviceControlService.validate(dto); + iotDeviceControlService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDeviceControlDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDeviceControlService.validate(dto); + iotDeviceControlService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDeviceControlService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("control") + @Operation(summary="控制") + public Result control(@RequestBody ControlParam controlParam) { + //效验数据 + ValidatorUtils.validateEntity(controlParam, DefaultGroup.class); + return iotDeviceControlService.control(controlParam); + } + + @PostMapping("export") + @Operation(summary="json导出") + @Parameters({ + @Parameter(name = "name",description ="控制名称"), + @Parameter(name = "device",description ="设备信息"), + @Parameter(name = "attr",description ="属性信息"), + @Parameter(name = "extendId",description ="通讯协议") + }) + public Result exportJson( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + iotDeviceControlService.exportJson(Lists.newArrayList(ids), params, response); + return new Result<>(); + } + + @PostMapping("import") + @Operation(summary="导入json") + public Result importJson(@RequestBody List controlJsonList) { + if (CollectionUtil.isEmpty(controlJsonList)) { + throw new SysException("导入数据为空"); + } + controlJsonList.forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + iotDeviceControlService.importJson(controlJsonList); + return new Result<>(); + } + + @GetMapping("list/device") + @Operation(summary="设备列表") + public Result> deviceList() { + List data = iotDeviceControlService.getDeviceList(Maps.newHashMap()); + return new Result>().ok(data); + } + + @GetMapping("list/attr/{thingId}") + @Operation(summary="设备属性列表") + public Result> attrList(@PathVariable Long thingId) { + Map params = Maps.newHashMap(); + params.put("thingId", thingId); + List data = iotDeviceControlService.getAttrList(params); + return new Result>().ok(data); + } + + @GetMapping("list/{thingId}") + @Operation(summary="指令列表") + public Result> ctlList(@PathVariable Long thingId) { + List data = iotDeviceControlService.ctlList(thingId); + return new Result>().ok(data); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlLogController.java b/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlLogController.java new file mode 100644 index 0000000..342ef67 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/controller/IotDeviceControlLogController.java @@ -0,0 +1,74 @@ +package com.thing.control.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.control.dto.IotDeviceControlLogPage; +import com.thing.control.service.IotDeviceControlLogService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 设备控制日志 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@RestController +@RequestMapping("device/control/log") +@Tag(name = "设备控制日志") +@RequiredArgsConstructor +public class IotDeviceControlLogController { + private final IotDeviceControlLogService iotDeviceControlLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "name",description ="控制名称"), + @Parameter(name = "device",description ="设备信息"), + @Parameter(name = "attr",description ="属性信息"), + @Parameter(name = "extendId",description ="通讯协议") + }) + public Result> page( @RequestParam Map params) { + PageData page = iotDeviceControlLogService.pageList(params); + + return new Result>().ok(page); + } + + @PostMapping("retry") + @Operation(summary="命令重发") + public Result retry(@RequestBody Long[] ids) { + iotDeviceControlLogService.retry(ids); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name = "name",description ="控制名称"), + @Parameter(name = "device",description ="设备信息"), + @Parameter(name = "attr",description ="属性信息"), + @Parameter(name = "extendId",description ="通讯协议") + }) + public void exportThingAttrOrTagExcel( @RequestParam Map params, HttpServletResponse response) { + iotDeviceControlLogService.exportExcel(params, response); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/dto/ControlParam.java b/modules/thing/src/main/java/com/thing/control/dto/ControlParam.java new file mode 100644 index 0000000..f1c9b32 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/ControlParam.java @@ -0,0 +1,26 @@ +package com.thing.control.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import javax.validation.constraints.NotNull; + +/** + * @author zhenghh. 2023-03-09 + **/ +@Data +@Schema( name= "控制参数") +public class ControlParam { + + @Schema(description = "看板元素主键") + private Long elementId; + + @Schema(description = "控制主键") + @NotNull(message = "控制主键不能为空", groups = DefaultGroup.class) + private Long controlId; + + @Schema(description = "控制入参json") + private String condition; +} diff --git a/modules/thing/src/main/java/com/thing/control/dto/ControlSelected.java b/modules/thing/src/main/java/com/thing/control/dto/ControlSelected.java new file mode 100644 index 0000000..80ae3db --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/ControlSelected.java @@ -0,0 +1,44 @@ +package com.thing.control.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2023-03-13 + **/ +@Data +@Schema( name= "控制下拉") +@AllArgsConstructor +@NoArgsConstructor +public class ControlSelected { + + @Schema(description = "主键") + private Long id; + @Schema(description = "控制主键") + private Long thingId; + @Schema(description = "控制主键") + private Long ctlId; + @Schema(description = "设备编码") + private String code; + + @Schema(description = "设备名称") + private String name; + + @Schema(description = "控制名称") + private String ctlName; + + + private String controlDeviceAttr; + + + public ControlSelected(Long id, Long ctlId,String name, String ctlName, String controlDeviceAttr) { + this.id = id; + this.ctlId = ctlId; + this.name = name; + this.ctlName = ctlName; + this.controlDeviceAttr = controlDeviceAttr; + } +} diff --git a/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlDTO.java b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlDTO.java new file mode 100644 index 0000000..c4f5717 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlDTO.java @@ -0,0 +1,107 @@ +package com.thing.control.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@Schema( name= "设备控制") +public class IotDeviceControlDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "控制名称") + private String name; + @Schema(description = "物实体主键") + @NotNull(message="设备不能为空", groups = DefaultGroup.class) + private Long thingId; + @Schema(description = "属性编码") + @NotBlank(message="属性不能为空", groups = DefaultGroup.class) + private String attrCode; + @Schema(description = "通讯协议") + @NotNull(message="通讯协议不能为空", groups = DefaultGroup.class) + private Long extendId; + @Schema(description = "协议通道") + @NotNull(message="协议通道不能为空", groups = DefaultGroup.class) + private Long relationId; + @Schema(description = "辅助参数") + private String supMsg; + @Schema(description = "查询参数") + private String queryMsg; + @Schema(description = "控制逻辑") + private String ctlBody; + @Schema(description = "控制类型,1按钮,2文本,3其他") + private String ctlType; + + /** + * 控制样式json 数组json [ + * + * ] + */ + @Schema(description = "控制样式json 数组json {\n" + + " \"no\":\"123\",\n" + + " \"code\":\"a001\",\n" + + " \"name\":\"空调开\",\n" + + " \"url\":\"/dddd/eee.png\"\n" + + " },{\n" + + " \"no\":\"124\",\n" + + " \"code\":\"a002\",\n" + + " \"name\":\"空调关\",\n" + + " \"url\":\"/dddd/ccc.png\"\n" + + " }") + private String ctlJson; + + @Schema(description = "用户id列表,多个用户以英文逗号分割") + private String userList; + + @Schema(description = "备注") + private String remark; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + + @Schema(description = "属性标签id") + private Long attrId; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlJson.java b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlJson.java new file mode 100644 index 0000000..1917db2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlJson.java @@ -0,0 +1,53 @@ +package com.thing.control.dto; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.validator.group.DefaultGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@Schema( name= "设备控制") +public class IotDeviceControlJson implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "控制名称") + @NotBlank(message = "控制名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema(description = "设备编码") + @NotBlank(message = "设备编码不能为空", groups = DefaultGroup.class) + private String thingCode; + @Schema(description = "属性编码") + @NotBlank(message = "属性编码不能为空", groups = DefaultGroup.class) + private String attrCode; + @Schema(description = "通讯协议名称") + @NotBlank(message = "通讯协议名称不能为空", groups = DefaultGroup.class) + private String extendName; + @Schema(description = "协议通道配置") + @NotNull(message = "协议通道配置不能为空", groups = DefaultGroup.class) + private JsonNode relationConfig; + @Schema(description = "辅助参数") + private String supMsg; + @Schema(description = "查询参数") + private String queryMsg; + @Schema(description = "控制逻辑") + private String ctlBody; + @Schema(description = "备注说明") + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogDTO.java b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogDTO.java new file mode 100644 index 0000000..adffab2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogDTO.java @@ -0,0 +1,51 @@ +package com.thing.control.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 设备控制日志 +* +* @author zhh zhh +* @since 1.0.1 2023-03-09 +*/ +@Data +@Schema( name= "设备控制日志") +public class IotDeviceControlLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "控制主键") + private Long controlId; + @Schema(description = "请求参数") + private String inMsg; + @Schema(description = "输出结果") + private String outMsg; + @Schema(description = "异常信息") + private String errorMsg; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogPage.java b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogPage.java new file mode 100644 index 0000000..c24a9e1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlLogPage.java @@ -0,0 +1,53 @@ +package com.thing.control.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 设备控制日志 +* +* @author zhh zhh +* @since 1.0.1 2023-03-09 +*/ +@Data +@Schema( name= "设备控制日志") +public class IotDeviceControlLogPage implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "控制主键") + private Long controlId; + @Schema(description = "控制名称") + private String controlName; + @Schema(description = "设备编码") + private String thingCode; + @Schema(description = "设备名称") + private String thingName; + @Schema(description = "属性编码") + private String attrCode; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "请求参数") + private String inMsg; + @Schema(description = "输出结果") + private String outMsg; + @Schema(description = "异常信息") + private String errorMsg; + @Schema(description = "创建者") + private String creator; + @Schema(description = "创建时间") + private Long updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlPage.java b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlPage.java new file mode 100644 index 0000000..94f0c44 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/dto/IotDeviceControlPage.java @@ -0,0 +1,59 @@ +package com.thing.control.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@Schema( name= "设备控制") +public class IotDeviceControlPage implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "控制名称") + private String name; + @Schema(description = "设备编码") + private String thingCode; + @Schema(description = "设备名称") + private String thingName; + @Schema(description = "属性编码") + private String attrCode; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "通讯协议主键") + private Long extendId; + @Schema(description = "通讯协议") + private String extendName; + @Schema(description = "协议通道主键") + private Long relationId; + @Schema(description = "协议通道配置") + private String relationConfig; + @Schema(description = "协议通道地址") + private String addr; + @Schema(description = "辅助参数") + private String supMsg; + @Schema(description = "查询参数") + private String queryMsg; + @Schema(description = "控制逻辑") + private String ctlBody; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "创建时间") + private Long createDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlEntity.java b/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlEntity.java new file mode 100644 index 0000000..b4e61c8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlEntity.java @@ -0,0 +1,93 @@ +package com.thing.control.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_device_control") +public class IotDeviceControlEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 控制名称 + */ + private String name; + /** + * 物实体主键 + */ + private Long thingId; + /** + * 属性编码 + */ + private String attrCode; + /** + * 通讯主键 + */ + private Long extendId; + /** + * 通讯关系主键 + */ + private Long relationId; + /** + * 辅助参数 + */ + private String supMsg; + /** + * 查询参数 + */ + private String queryMsg; + /** + * 控制逻辑 + */ + private String ctlBody; + + /** + * 控制类型,1按钮,2文本,3其他 + */ + private String ctlType; + + /** + * 控制样式json 数组json [ + * { + * "no":"123", + * "code":"a001", + * "name":"空调开", + * "url":"/dddd/eee.png" + * },{ + * "no":"124", + * "code":"a002", + * "name":"空调关", + * "url":"/dddd/ccc.png" + * } + * ] + */ + private String ctlJson; + /** + *用户id列表,多个用户以英文逗号分割 + */ + private String userList; + /** + * 备注 + */ + private String remark; + /** + * 属性标签id + */ + private Long attrId; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlLogEntity.java b/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlLogEntity.java new file mode 100644 index 0000000..ab8cefa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/entity/IotDeviceControlLogEntity.java @@ -0,0 +1,39 @@ +package com.thing.control.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +/** + * 设备控制日志 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_device_control_log") +public class IotDeviceControlLogEntity extends BaseDateEntity { + private static final long serialVersionUID = 1L; + + /** + * 控制主键 + */ + private Long controlId; + /** + * 请求参数 + */ + private String inMsg; + /** + * 输出结果 + */ + private String outMsg; + /** + * 异常信息 + */ + private String errorMsg; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/excel/IotDeviceControlLogExcel.java b/modules/thing/src/main/java/com/thing/control/excel/IotDeviceControlLogExcel.java new file mode 100644 index 0000000..3f2d802 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/excel/IotDeviceControlLogExcel.java @@ -0,0 +1,34 @@ +package com.thing.control.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 设备控制日志 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class IotDeviceControlLogExcel { + @Excel(name = "日期时间", orderNum = "0") + private String createDate; + @Excel(name = "用户信息", orderNum = "1") + private String creator; + @Excel(name = "设备信息", orderNum = "2") + private String device; + @Excel(name = "属性信息", orderNum = "3") + private String attr; + @Excel(name = "控制名称", orderNum = "4") + private String controlName; + @Excel(name = "请求入参", orderNum = "5") + private String inMsg; + @Excel(name = "输出结果", orderNum = "6") + private String outMsg; + @Excel(name = "异常信息", orderNum = "7") + private String errorMsg; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlLogMapper.java b/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlLogMapper.java new file mode 100644 index 0000000..1149479 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlLogMapper.java @@ -0,0 +1,26 @@ +package com.thing.control.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.control.dto.IotDeviceControlLogPage; +import com.thing.control.entity.IotDeviceControlLogEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 设备控制日志 +* +* @author zhh zhh +* @since 1.0.1 2023-03-09 +*/ +@Mapper +public interface IotDeviceControlLogMapper extends PowerBaseMapper { + + /** + * 分页查询 + * @param params 查询参数 + * @return list + */ + List getList(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlMapper.java b/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlMapper.java new file mode 100644 index 0000000..38fb86c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/mapper/IotDeviceControlMapper.java @@ -0,0 +1,41 @@ +package com.thing.control.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.control.dto.ControlSelected; +import com.thing.control.dto.IotDeviceControlPage; +import com.thing.control.entity.IotDeviceControlEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 设备控制 +* +* @author zhh zhh +* @since 1.0.1 2023-03-09 +*/ +@Mapper +public interface IotDeviceControlMapper extends PowerBaseMapper { + + /** + * 分页查询 + * @param params 查询 + * @return list + */ + List getList(Map params); + + /** + * 设备列表 + * @param params 查询 + * @return list + */ + List getDeviceList(Map params); + + /** + * 属性列表 + * @param params 查询 + * @return list + */ + List getAttrList(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/service/ControlService.java b/modules/thing/src/main/java/com/thing/control/service/ControlService.java new file mode 100644 index 0000000..798e1ea --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/ControlService.java @@ -0,0 +1,19 @@ +package com.thing.control.service; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * @author zhenghh. 2023-03-10 + **/ +public interface ControlService { + + /** + * 控制 + * + * @param inMsg 入参 + * @param extendId 通讯协议主键 + * @param relationId 协议通道主键 + * @throws Exception e + */ + void control(JsonNode inMsg, Long extendId, Long relationId) throws Exception; +} diff --git a/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlLogService.java b/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlLogService.java new file mode 100644 index 0000000..3cab8cc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlLogService.java @@ -0,0 +1,45 @@ +package com.thing.control.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.control.dto.IotDeviceControlLogPage; +import com.thing.control.entity.IotDeviceControlLogEntity; + +import jakarta.servlet.http.HttpServletResponse; + +import java.util.Map; + +/** + * 设备控制日志 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +public interface IotDeviceControlLogService extends IBaseService { + + /** + * 分页 + * @param params 查询 + * @return page + */ + PageData pageList(Map params); + + /** + * 删除 + * @param controlIds 控制主键 + */ + void deleteByControlIds(Long[] controlIds); + + /** + * 命令重发 + * @param ids 主键集合 + */ + void retry(Long[] ids); + + /** + * 导出excel + * @param params 查询 + * @param response response + */ + void exportExcel(Map params, HttpServletResponse response); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlService.java b/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlService.java new file mode 100644 index 0000000..ef33082 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/IotDeviceControlService.java @@ -0,0 +1,84 @@ +package com.thing.control.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.control.dto.*; +import com.thing.control.entity.IotDeviceControlEntity; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +public interface IotDeviceControlService extends IBaseService { + + /** + * 分页 + * + * @param params 查询 + * @return page + */ + PageData pageList(Map params); + + /** + * 校验是否存在 + * thingId+attrCode+name + * + * @param dto dto + */ + void validate(IotDeviceControlDTO dto); + + /** + * 控制 + * + * @param controlParam 控制参数 + * @return Result + */ + Result control(ControlParam controlParam); + + /** + * 导出 + * + * @param ids 主键集合 + * @param params 查询参数 + * @param response response + */ + void exportJson(List ids, Map params, HttpServletResponse response); + + /** + * 导入 + * + * @param controlJsonList 控制json列表 + */ + void importJson(List controlJsonList); + + /** + * 设备列表 + * + * @param params 查询参数 + * @return list + */ + List getDeviceList(Map params); + + /** + * 设备属性列表 + * + * @param params 查询参数 + * @return list + */ + List getAttrList(Map params); + + /** + * 控制列表 + * + * @param thingId 设备主键 + * @return list + */ + List ctlList(Long thingId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/service/impl/ControlServiceImpl.java b/modules/thing/src/main/java/com/thing/control/service/impl/ControlServiceImpl.java new file mode 100644 index 0000000..c9e20bc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/impl/ControlServiceImpl.java @@ -0,0 +1,99 @@ +package com.thing.control.service.impl; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Maps; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.ScriptMsg; +import com.thing.api.ScriptEngine; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.control.service.ControlService; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import com.thing.extend.mapper.TransportExtendCalculationMapper; +import com.thing.extension.Extension; +import com.thing.extension.ExtensionManager; +import com.thing.extension.ExtensionPublishMsg; +import com.thing.modules.cache.bean.ScriptCache; +import com.thing.modules.cache.service.ScriptCacheService; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Objects; + +/** + * @author zhenghh. 2023-03-10 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class ControlServiceImpl implements ControlService { + + private final ExtensionManager extensionManager; + private final ScriptCacheService scriptCacheService; + private final TransportExtendCalculationMapper extendCalculationMapper; + private final ScriptCreateService scriptCreateService; + + /** + * 控制 + * + * @param inMsg 入参 + * @param extendId 通讯协议主键 + * @param relationId 协议通道主键 + * @throws Exception e + */ + @Override + public void control(JsonNode inMsg, Long extendId, Long relationId) throws Exception { + if (inMsg == null || inMsg.get("sendRPC") == null) { + throw new SysException("控制逻辑解析出参为空或sendRPC属性不存在"); + } + Extension extension = extensionManager.getExtension(extendId); + if (extension == null || !extension.online()) { + throw new SysException("通讯协议未找到或已断开连接"); + } + TransportExtendCalculationEntity relationEntity = extendCalculationMapper.selectOneById(relationId); + if (relationEntity == null) { + throw new SysException("协议通道信息未找到"); + } + //再进行一次协议通道解析 + ExtensionPublishMsg publishMsg = calculation(inMsg.get("sendRPC"), relationEntity); + //下发 + extension.sendMsg(publishMsg); + } + + /** + * 协议通道解析 + * + * @param inMsg 入参 + * @param relationEntity 协议通道 + * @return JsonNode + */ + private ExtensionPublishMsg calculation(JsonNode inMsg, TransportExtendCalculationEntity relationEntity) { + ExtensionPublishMsg publishMsg = new ExtensionPublishMsg(); + publishMsg.setRelationId(relationEntity.getId()); + publishMsg.setInMsg(inMsg.toString()); + publishMsg.setDebug(relationEntity.getDebug()); + publishMsg.setAddress(JacksonUtil.toJsonNode(relationEntity.getConfiguration())); + if (relationEntity.getCalculationId() == null) { + publishMsg.setOutMsg(publishMsg.getInMsg()); + return publishMsg; + } + try { + ScriptCache scriptCache = scriptCacheService.get(String.valueOf(relationEntity.getCalculationId())); + if (Objects.isNull(scriptCache)) { + publishMsg.setErrorMsg("协议通道解析缓存未找到"); + return publishMsg; + } + ScriptEngine scriptEngine = scriptCreateService.createScriptEngine(scriptCache.getScriptEId(),ScriptLanguage.get(scriptCache.getScriptType()), scriptCache.getScriptBody(), scriptCache.getDebug()); + publishMsg.setOutMsg(scriptEngine.executeToStringAsync(new ScriptMsg(inMsg.toString(), Maps.newHashMap())).get()); + } catch (Exception e) { + log.error("协议通道解析异常:{}", e.getMessage()); + throw new SysException("协议通道解析异常: " + e.getMessage()); + } + return publishMsg; + } +} diff --git a/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlLogServiceImpl.java b/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlLogServiceImpl.java new file mode 100644 index 0000000..00507fa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlLogServiceImpl.java @@ -0,0 +1,194 @@ +package com.thing.control.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.control.dto.IotDeviceControlLogPage; +import com.thing.control.entity.IotDeviceControlEntity; +import com.thing.control.entity.IotDeviceControlLogEntity; +import com.thing.control.excel.IotDeviceControlLogExcel; +import com.thing.control.mapper.IotDeviceControlLogMapper; +import com.thing.control.mapper.IotDeviceControlMapper; +import com.thing.control.service.ControlService; +import com.thing.control.service.IotDeviceControlLogService; + +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备控制日志 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class IotDeviceControlLogServiceImpl extends BaseServiceImpl implements IotDeviceControlLogService { + + private final IotDeviceControlMapper controlMapper; + private final ControlService controlService; + private final ScriptCreateService scriptCreateService; + + private final static ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + public void getParam(Map params) { + //转换成like + paramsToLike(params, "name", "device", "attr"); + String extendId = (String) params.get("extendId"); + params.put("extendId", StringUtils.isBlank(extendId) ? null : Long.parseLong(extendId)); + } + + /** + * 分页 + * + * @param params 查询 + * @return page + */ + @Override + @DataFilter(completeDataMark = false, tableAlias = "idc") + public PageData pageList(Map params) { + getParam(params); + //分页 + Page page = getPage(params); + List list = mapper.getList(params); + + for (IotDeviceControlLogPage iotDeviceControlLogPage : list) { + String inMsg = iotDeviceControlLogPage.getInMsg(); + String outMsg = iotDeviceControlLogPage.getOutMsg(); + try { + JsonNode inJsonNode = objectMapper.readTree(inMsg); + double inValue = inJsonNode.get("value").asDouble(); + ObjectNode updatedJsonNode = objectMapper.createObjectNode(); + updatedJsonNode.set("value", objectMapper.convertValue(inValue, JsonNode.class)); + updatedJsonNode.set("deviceName", inJsonNode.get("deviceName")); + updatedJsonNode.set("attrCode", inJsonNode.get("attrCode")); + String inJsonString = objectMapper.writeValueAsString(updatedJsonNode); + iotDeviceControlLogPage.setInMsg(inJsonString); + JsonNode jsonNode = objectMapper.readTree(outMsg); + JsonNode sendRPCNode = jsonNode.get("sendRPC"); + if (sendRPCNode != null && sendRPCNode.isObject()) { + ObjectNode sendRPCObjectNode = (ObjectNode) sendRPCNode; + JsonNode valueNode = sendRPCObjectNode.get("value"); + if (valueNode != null && valueNode.isTextual()) { + double value = Double.parseDouble(valueNode.asText()); + sendRPCObjectNode.put("value", value); + } + JsonNode data = sendRPCObjectNode.get("data"); + if(data != null && !data.isEmpty()){ + ObjectNode dataObjectNode = (ObjectNode) data; + JsonNode v = dataObjectNode.get("v"); + if (v != null && v.isTextual()) { + double v1 = Double.parseDouble(v.asText()); + dataObjectNode.put("v", v1); + } + } + } + String updatedJsonString = objectMapper.writeValueAsString(jsonNode); + iotDeviceControlLogPage.setOutMsg(updatedJsonString); + } catch (Exception e) { + log.error("日志记录数据解析失败", e); + continue; + } + } + + return new PageData<>(list, page.getTotalRow()); + } + + /** + * 删除 + * + * @param controlIds 控制主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByControlIds(Long[] controlIds) { + if (controlIds == null || controlIds.length == 0) { + return; + } + mapper.deleteByQuery(QueryWrapper.create() + .in(IotDeviceControlLogEntity::getControlId, Arrays.asList(controlIds))); + } + + /** + * 命令重发 + * + * @param ids 主键集合 + */ + @Override + public void retry(Long[] ids) { + List logList = mapper.selectListByIds(Arrays.asList(ids)); + if (CollectionUtil.isEmpty(logList)) { + return; + } + List controlIds = logList.stream().map(IotDeviceControlLogEntity::getControlId).distinct().collect(Collectors.toList()); + List controlList = controlMapper.selectListByIds(controlIds); + + for (IotDeviceControlLogEntity logEntity : logList) { + IotDeviceControlEntity controlEntity = controlList.stream() + .filter(item -> Objects.equals(logEntity.getControlId(), item.getId())).findFirst() + .orElse(null); + if (controlEntity == null) { + continue; + } + try { + //控制逻辑解析 + JsonNode outMsg = scriptCreateService.executeJson(controlEntity.getId(), ScriptLanguage.NASHORN, controlEntity.getCtlBody(), false, + logEntity.getInMsg(), JacksonUtil.toJsonNode(controlEntity.getSupMsg()), JacksonUtil.toJsonNode(controlEntity.getQueryMsg()), false); + logEntity.setOutMsg(Objects.isNull(outMsg) ? null : outMsg.toString()); + //下发 + controlService.control(outMsg, controlEntity.getExtendId(), controlEntity.getRelationId()); + logEntity.setErrorMsg(""); + } catch (Exception e) { + logEntity.setErrorMsg(e.getMessage()); + } + logEntity.setUpdateDate(System.currentTimeMillis()); + } + updateBatch(logList); + } + + /** + * 导出excel + * + * @param params 查询 + * @param response response + */ + @Override + public void exportExcel(Map params, HttpServletResponse response) { + getParam(params); + //分页 + List list = mapper.getList(params); + List collect = list.parallelStream() + .map(item -> new IotDeviceControlLogExcel(item.getUpdateDate().toString(), item.getCreator(), + StringUtils.isBlank(item.getThingCode()) ? "未知" : (item.getThingName() + "-" + item.getThingCode()), + StringUtils.isBlank(item.getAttrName()) ? "未知" : (item.getAttrName() + "-" + item.getAttrCode()), + item.getControlName(), item.getInMsg(), item.getOutMsg(), item.getErrorMsg())).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "设备控制日志", null, IotDeviceControlLogExcel.class, "设备控制日志.xls", response); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlServiceImpl.java b/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlServiceImpl.java new file mode 100644 index 0000000..0571729 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/control/service/impl/IotDeviceControlServiceImpl.java @@ -0,0 +1,346 @@ +package com.thing.control.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.control.dto.*; +import com.thing.control.entity.IotDeviceControlEntity; +import com.thing.control.mapper.IotDeviceControlMapper; +import com.thing.control.service.ControlService; +import com.thing.control.service.IotDeviceControlLogService; +import com.thing.control.service.IotDeviceControlService; +import com.thing.dashboard.service.IotDashboardElementService; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.mapper.TransportExtendMapper; +import com.thing.extend.service.TransportExtendCalculationService; +import com.thing.extension.ExtensionUtil; +import com.thing.modules.dto.QueryMsg; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 设备控制 + * + * @author zhh zhh + * @since 1.0.1 2023-03-09 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class IotDeviceControlServiceImpl extends BaseServiceImpl implements IotDeviceControlService { + + private final TransportExtendMapper extendMapper; + private final TransportExtendCalculationService extendCalculationService; + private final IotDeviceControlLogService controlLogService; + private final ControlService controlService; + private final IotDashboardElementService dashboardElementService; + private final ScriptCreateService scriptCreateService; + private final ThingManageContextService contextService; + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + /** + * 分页 + * + * @param params 查询 + * @return page + */ + @Override + @DataFilter(completeDataMark = false, tableAlias = "idc") + public PageData pageList(Map params) { + //转换成like + paramsToLike(params, "name", "device", "attr"); + String extendId = (String) params.get("extendId"); + params.put("extendId", StringUtils.isBlank(extendId) ? null : Long.parseLong(extendId)); + //分页 + long total = mapper.selectCountByQuery(getWrapper(params)); + List list = mapper.getList(params); + return new PageData<>(convertExtend(list), total); + } + + private List convertExtend(List list) { + if (CollectionUtil.isNotEmpty(list)) { + List extendIdList = list.stream().map(IotDeviceControlPage::getExtendId).distinct().collect(Collectors.toList()); + List relationIdList = list.stream().map(IotDeviceControlPage::getRelationId).distinct().collect(Collectors.toList()); + List extendList = extendMapper.selectListByIds(extendIdList); + List extendCalculationList = extendCalculationService.getWriteList(null, relationIdList); + return list.stream().peek(item -> { + extendList.parallelStream() + .filter(extend -> Objects.equals(item.getExtendId(), extend.getId())) + .findFirst() + .ifPresent(extend -> item.setExtendName(extend.getName())); + extendCalculationList.parallelStream() + .filter(relation -> Objects.equals(item.getRelationId(), relation.getId())) + .findFirst() + .ifPresent(relation -> { + item.setRelationConfig(relation.getConfiguration()); + item.setAddr(relation.getAddr()); + }); + }).collect(Collectors.toList()); + } + return list; + } + + /** + * 校验是否存在 + * thingId+attrCode+name + * + * @param dto dto + */ + @Override + public void validate(IotDeviceControlDTO dto) { + boolean exist = exists(QueryWrapper.create().eq(IotDeviceControlEntity::getThingId, dto.getThingId()) + .eq(IotDeviceControlEntity::getAttrCode, dto.getAttrCode()).eq(IotDeviceControlEntity::getName, StringUtils.trim(dto.getName())) + .ne(IotDeviceControlEntity::getId, dto.getId(), Objects.nonNull(dto.getId()))); + if (exist) { + throw new SysException("控制名称重复"); + } + if (StringUtils.isNotBlank(dto.getQueryMsg())) { + List queryMsgList = JacksonUtil.fromString(dto.getQueryMsg(), new TypeReference<>() { + }); + assert queryMsgList != null; + queryMsgList.forEach(item -> ValidatorUtils.validateEntity(item, DefaultGroup.class)); + } + } + + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + batchDelete(ids); + controlLogService.deleteByControlIds(ids); + } + + /** + * 控制 + * + * @param controlParam 控制参数 + * @return Result + */ + @Override + public Result control(ControlParam controlParam) { + //看板控制权限 + if (controlParam.getElementId() != null) { + dashboardElementService.validatePermissions(controlParam.getElementId()); + } + IotDeviceControlEntity controlEntity = mapper.selectOneById(controlParam.getControlId()); + if (controlEntity == null || controlEntity.getExtendId() == null || controlEntity.getRelationId() == null) { + throw new SysException("控制配置信息缺失"); + } + IotDeviceControlLogDTO logDTO = new IotDeviceControlLogDTO(); + logDTO.setControlId(controlParam.getControlId()); + logDTO.setInMsg(controlParam.getCondition()); + JsonNode outMsg = null; + try { + //控制逻辑解析 + outMsg = scriptCreateService.executeJson(controlEntity.getId(), ScriptLanguage.NASHORN, controlEntity.getCtlBody(), false, + controlParam.getCondition(), JacksonUtil.toJsonNode(controlEntity.getSupMsg()), JacksonUtil.toJsonNode(controlEntity.getQueryMsg()), false); + logDTO.setOutMsg(Objects.isNull(outMsg) ? null : outMsg.toString()); + //下发 + controlService.control(outMsg, controlEntity.getExtendId(), controlEntity.getRelationId()); + + } catch (Exception e) { + logDTO.setErrorMsg(e.getMessage()); + } + logDTO.setUpdateDate(new Date()); + //日志保存 + controlLogService.saveDto(logDTO); + if (StringUtils.isNotBlank(logDTO.getErrorMsg())) { + return new Result<>().error(logDTO.getErrorMsg()); + } + JsonNode respMsg = outMsg != null ? outMsg.get("respMsg") : JacksonUtil.newObjectNode(); + String resultMsg = respMsg != null && StringUtils.isNotBlank(respMsg.asText()) ? respMsg.asText() : "下发成功"; + return new Result<>().ok(resultMsg); + } + + /** + * 导出 + * + * @param ids 主键集合 + * @param params 查询参数 + * @param response response + */ + @Override + public void exportJson(List ids, Map params, HttpServletResponse response) { + //转换成like + paramsToLike(params, "name", "device", "attr"); + if (CollectionUtil.isNotEmpty(ids)) { + params.put("ids", ids); + } + String extendId = (String) params.get("extendId"); + params.put("extendId", StringUtils.isBlank(extendId) ? null : Long.parseLong(extendId)); + //查询 + List controlList = convertExtend(mapper.getList(params)); + List list = controlList.stream().map(item -> { + IotDeviceControlJson controlJson = ConvertUtils.sourceToTarget(item, IotDeviceControlJson.class); + controlJson.setRelationConfig(JacksonUtil.toJsonNode(item.getRelationConfig())); + return controlJson; + }).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(list)) { + throw new SysException("未找到数据"); + } + try { + byte[] data = JacksonUtil.writeValueAsPrettyBytes(list); + response.reset(); + response.setHeader("Access-Control-Allow-Origin", "*"); + response.setHeader("Content-Disposition", "attachment; filename=" + UUID.randomUUID() + ".json"); + response.addHeader("Content-Length", "" + data.length); + response.setContentType("application/octet-stream; charset=UTF-8"); + IOUtils.write(data, response.getOutputStream()); + } catch (IOException e) { + log.error("exportJson error: ",e); + throw new SysException("导出失败: " + e.getMessage()); + } + } + + + /** + * 导入 + * + * @param controlJsonList 控制json列表 + */ + @Override + public void importJson(List controlJsonList) { + //校验 + Map collect = controlJsonList.stream() + .collect(Collectors.groupingBy(item -> item.getThingCode() + ":" + item.getAttrCode() + ":" + item.getName(), Collectors.counting())); + if (collect.values().stream().anyMatch(num -> num > 1)) { + throw new SysException("同一设备属性存在相同控制"); + } + //设备列表 + List thingCodes = controlJsonList.stream().map(IotDeviceControlJson::getThingCode).distinct().collect(Collectors.toList()); + Optional> thingList = contextService.findEntityAllByCodeAndTenantCode(thingCodes, UserContext.getRealTenantCode(), true); + if (thingList.isEmpty()) { + throw new SysException("设备信息未找到"); + } + //属性列表 + List thingIdList = thingList.get().stream().map(IotThingEntityDTO::getId).collect(Collectors.toList()); + Optional> dictRelationDTOS = contextService.findDictRelationAllByEntityIdsAndCodes(Collections.synchronizedList(thingIdList), null); + if(dictRelationDTOS.isEmpty()){ + return; + } + Map> thingAttrMap = dictRelationDTOS.get().stream() + .collect(Collectors.groupingBy( + IotThingDictRelationParamDTO::getEntityId, + Collectors.mapping(IotThingDictRelationParamDTO::getCode, Collectors.toList()) + )); + //通讯协议列表 + List extendNames = controlJsonList.stream().map(item -> StringUtils.trim(item.getExtendName())).distinct().collect(Collectors.toList()); + List transportExtendList = extendMapper.selectListByQuery(QueryWrapper.create().in(TransportExtendEntity::getName, extendNames)); + //协议通道列表 + List extendIds = transportExtendList.parallelStream().map(TransportExtendEntity::getId).collect(Collectors.toList()); + List extendCalculationList = extendCalculationService.getWriteList(extendIds, null); + //已存在控制列表 + List controlList = mapper.selectListExt(QueryWrapper.create().in(IotDeviceControlEntity::getThingId, thingIdList)); + List entityList = controlJsonList.stream().map(controlJson -> { + //校验设备 + IotThingEntityDTO thingEntity = thingList.get().parallelStream().filter(item -> StringUtils.equals(item.getCode(), controlJson.getThingCode())) + .findFirst().orElseThrow(() -> new SysException(controlJson.getName() + "设备未匹配")); + //校验是否已存在 + if (CollectionUtil.isNotEmpty(controlList)) { + if (controlList.parallelStream().anyMatch(item -> Objects.equals(item.getThingId(), thingEntity.getId()) + && StringUtils.equals(item.getAttrCode(), controlJson.getAttrCode()) + && StringUtils.equals(StringUtils.trim(item.getName()), StringUtils.trim(controlJson.getName())))) { + throw new SysException(controlJson.getName() + "控制名称重复"); + } + } + //校验属性 + if (!thingAttrMap.containsKey(controlJson.getThingCode()) || !thingAttrMap.get(controlJson.getThingCode()).contains(controlJson.getAttrCode())) { + throw new SysException(controlJson.getName() + "设备属性未匹配"); + } + //校验通讯协议 + TransportExtendEntity extendEntity = transportExtendList.parallelStream() + .filter(item -> StringUtils.equals(StringUtils.trim(controlJson.getExtendName()), item.getName())) + .findFirst().orElseThrow(() -> new SysException(controlJson.getName() + "通讯协议未匹配")); + //校验协议通道 + TransportExtendCalculationDTO relationDTO = extendCalculationList.parallelStream() + .filter(item -> Objects.equals(extendEntity.getId(), item.getConfigId())) + .filter(item -> ExtensionUtil.compareConfig(extendEntity.getType(), controlJson.getRelationConfig(), item.getAddr())) + .findFirst().orElseThrow(() -> new SysException(controlJson.getName() + "协议通道未匹配")); + IotDeviceControlEntity entity = new IotDeviceControlEntity(); + entity.setName(controlJson.getName()); + entity.setThingId(thingEntity.getId()); + entity.setAttrCode(controlJson.getAttrCode()); + entity.setExtendId(extendEntity.getId()); + entity.setRelationId(relationDTO.getId()); + entity.setSupMsg(controlJson.getSupMsg()); + entity.setQueryMsg(controlJson.getQueryMsg()); + entity.setCtlBody(controlJson.getCtlBody()); + entity.setRemark(controlJson.getRemark()); + //根据物id和属性编码查询属性 + Optional> optional = + contextService.findDictRelationAllByEntityIdAndCodes(thingEntity.getId(), Collections.singletonList(controlJson.getAttrCode())); + entity.setAttrId(Long.valueOf(String.valueOf(optional.isPresent() ? optional.get().get(0).getId() : ""))); + return entity; + }).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(entityList)) { + mapper.insertBatch(entityList); + } + } + + /** + * 设备列表 + * + * @param params 查询参数 + * @return list + */ + @Override + @DataFilter(completeDataMark = false, tableAlias = "idc") + public List getDeviceList(Map params) { + return mapper.getDeviceList(params); + } + + /** + * 设备属性列表 + * + * @param params 物编码 + * @return list + */ + @Override + public List getAttrList(Map params) { + return mapper.getAttrList(params); + } + + /** + * 控制列表 + * + * @param thingId 设备主键 + * @return list + */ + @Override + public List ctlList(Long thingId) { + List list = mapper.selectListExt(QueryWrapper.create() + .eq(IotDeviceControlEntity::getThingId, thingId)); + return CollectionUtil.isEmpty(list) ? Lists.newArrayList() + : list.stream().map(item -> new ControlSelected(item.getId(),item.getId(),item.getName(), item.getName(),item.getAttrCode())).collect(Collectors.toList()); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardController.java b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardController.java new file mode 100644 index 0000000..39fe0f8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardController.java @@ -0,0 +1,161 @@ +package com.thing.dashboard.controller; + +import cn.hutool.core.collection.CollectionUtil; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.dashboard.dto.IotDashboardDTO; +import com.thing.dashboard.dto.IotDashboardElementAttrValueDTO; +import com.thing.dashboard.dto.SortDTO; +import com.thing.dashboard.entity.IotDashboardEntity; +import com.thing.dashboard.service.IotDashboardService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@RestController +@RequestMapping("dashboard/iotdashboard") +@Tag(name="看板表") +@RequiredArgsConstructor +public class IotDashboardController { + private final IotDashboardService iotDashboardService; + + @GetMapping("list") + @Operation(summary="看板列表") + @Parameters({ + @Parameter(name = "dashboardGroupId",description ="看板管理id") + }) + public Result> list( @RequestParam Map params){ + List iotDashboardDTOList = iotDashboardService.listAs(params, IotDashboardEntity.class); + + return new Result>().ok(ConvertUtils.convertWithTypeAdapt(iotDashboardDTOList, IotDashboardDTO.class)); + } + + @GetMapping("listShare/{dashboardGroupId}") + @Operation(summary="看板列表(分享版)") + public Result> listShare(@PathVariable("dashboardGroupId") Long dashboardGroupId){ + List iotDashboardDTOList = iotDashboardService.listShare(dashboardGroupId); + return new Result>().ok(iotDashboardDTOList); + } + + @GetMapping("listIotDashboardDTO") + @Operation(summary="看板列表(附带看板内元素)") + @Parameters({ + @Parameter(name = "dashboardGroupId",description ="看板管理id") + }) + public Result> listIotDashboardDTO( @RequestParam Map params){ + List iotDashboardDTOList = iotDashboardService.listIotDashboardDTO(params); + return CollectionUtil.isNotEmpty(iotDashboardDTOList) ? + new Result>().ok(iotDashboardDTOList.stream().sorted(Comparator.comparing(IotDashboardDTO::getCreateDate).reversed()).collect(Collectors.toList())) : + new Result>().ok(Lists.newArrayList()); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotDashboardDTO data = iotDashboardService.getByIdAs(id, IotDashboardDTO.class); + return new Result().ok(data); + } + + @GetMapping("/element/{id}") + @Operation(summary="信息(附带看板内元素)") + public Result getIotDashboardDTO(@PathVariable("id") Long id){ + IotDashboardDTO data = iotDashboardService.getIotDashboardDTO(id); + return new Result().ok(data); + } + + @PostMapping("saveOrUpdateIotDashboardDTO") + @Operation(summary="新增/修改") + @LogOperation("新增/修改") + public Result save(@RequestBody IotDashboardDTO dto){ + iotDashboardService.saveOrUpdateIotDashboardDTO(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDashboardDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDashboardService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDashboardService.deleteIotDashboardDTO(Lists.newArrayList(ids)); + return new Result<>(); + } + + @PostMapping("svg/upload") + @Operation(summary="svg转换xml") + public Result uploadXml(@RequestParam(value = "file") MultipartFile multipartFile) { + try { + return new Result().ok(new String(multipartFile.getBytes(), StandardCharsets.UTF_8)); + } catch (IOException e) { + return new Result().error(); + } + } + + @GetMapping("svg/xml/{id}") + @Operation(summary="获取svgXml") + public Result svgXml(@PathVariable Long id){ + + String data = iotDashboardService.svgXml(id); + + return new Result().ok(data); + } + + @GetMapping("getDashboardElementLatestAttr") + @Operation(summary="获取看板映射属性的最新值") + @LogOperation("获取看板映射属性的最新值") + @Parameters({ + @Parameter(name = "dashboardId",description ="看板id") + }) + public Result> getDashboardElementLatestAttr( @RequestParam Map params) { + return new Result>().ok(iotDashboardService.getDashboardElementLatestAttr(params)); + } + + @PostMapping("sort") + @Operation(summary="排序") + public Result sort(@RequestBody List sortList) { + //效验数据 + sortList.forEach(dto -> ValidatorUtils.validateEntity(dto, DefaultGroup.class)); + iotDashboardService.sort(sortList); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardElementController.java b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardElementController.java new file mode 100644 index 0000000..57c4faf --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardElementController.java @@ -0,0 +1,154 @@ +package com.thing.dashboard.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.dashboard.dto.IotDashboardElementDTO; +import com.thing.dashboard.excel.IotDashboardElementExcel; +import com.thing.dashboard.service.IotDashboardElementService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@RestController +@RequestMapping("dashboard/iotdashboardelement") +@Tag(name="看板元素表") +@RequiredArgsConstructor +public class IotDashboardElementController { + private final IotDashboardElementService iotDashboardElementService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "dashboardId",description ="看板id") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotDashboardElementService.getPageData(params, IotDashboardElementDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "dashboardId",description ="看板id") + }) + public Result> list( @RequestParam Map params){ + return new Result>().ok(iotDashboardElementService.listAs(params, IotDashboardElementDTO.class)); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotDashboardElementDTO data = iotDashboardElementService.getByIdAs(id, IotDashboardElementDTO.class); + + return new Result().ok(data); + } + + @GetMapping("getElementByElementId") + @Operation(summary="通过节点id来获取节点信息") + public Result getElementByElementId(Long dashboardId, String elementId){ + return new Result().ok(iotDashboardElementService.getElementByElementId(dashboardId, elementId)); + } + + @GetMapping("getElementByDashboardId") + @Operation(summary="通过看板id来获取节点信息") + public Result> getElementByDashboardId(Long dashboardId){ + return new Result>().ok(iotDashboardElementService.getElementByDashboardId(dashboardId)); + } + + @PostMapping("saveOrUpdateIotDashboardElementDTO") + @Operation(summary="新增/修改") + @LogOperation("新增/修改") + public Result save(@RequestBody List dtoList){ + iotDashboardElementService.saveOrUpdateIotDashboardElementDTO(dtoList); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDashboardElementDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDashboardElementService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDashboardElementService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "dashboardId",description ="看板id") + }) + public void exportIotDashboardElement( @RequestParam Map params, @RequestBody Long[] ids, HttpServletResponse response) { + iotDashboardElementService.exportIotDashboardElement(params, Lists.newArrayList(ids), response); + } + + @PostMapping("import/{dashBoardId}") + @Operation(summary="导入") + @LogOperation("导入") + public Result> importIotDashboardElement(@PathVariable Long dashBoardId, MultipartFile file, HttpServletRequest request) { + Set strings = iotDashboardElementService.importIotDashboardElement(dashBoardId, file, request); + return new Result>().ok(strings); + } + + @PostMapping("transform") + @Operation(summary="转换") + public void transform(@RequestBody List list, HttpServletResponse response) { + iotDashboardElementService.transform(list, response); + } + + + @GetMapping("delElementByDashboardId") + @Operation(summary="根据看板id删除看板下所有关联元素信息") + public Result delElementByDashboardId(Long dashboardId){ + //效验数据 + iotDashboardElementService.delElementByDashboardId(dashboardId); + return new Result<>(); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardGroupController.java b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardGroupController.java new file mode 100644 index 0000000..fe0564d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/controller/IotDashboardGroupController.java @@ -0,0 +1,94 @@ +package com.thing.dashboard.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.dashboard.service.IotDashboardGroupService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@RestController +@RequestMapping("dashboard/iotdashboardgroup") +@Tag(name="看板管理表") +@RequiredArgsConstructor +public class IotDashboardGroupController { + private final IotDashboardGroupService iotDashboardGroupService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "name",description ="名称") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotDashboardGroupService.pageIotDashboardGroupDTO(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotDashboardGroupDTO data = iotDashboardGroupService.getIotDashboardGroupDTO(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotDashboardGroupDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotDashboardGroupService.saveIotDashboardGroupDTO(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDashboardGroupDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDashboardGroupService.updateIotDashboardGroupDTO(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDashboardGroupService.deleteIotDashboardGroupDTO(Lists.newArrayList(ids)); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotAttrRspDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotAttrRspDTO.java new file mode 100644 index 0000000..5e5d772 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotAttrRspDTO.java @@ -0,0 +1,27 @@ +package com.thing.dashboard.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class IotAttrRspDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "属性编码") + private String attrKey; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "属性单位") + private String attrUnit; + @Schema(description = "ts") + private Long ts; + @Schema(description = "值") + private String val; +} diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashBoardRspDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashBoardRspDTO.java new file mode 100644 index 0000000..01dd3b8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashBoardRspDTO.java @@ -0,0 +1,24 @@ +package com.thing.dashboard.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +public class IotDashBoardRspDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + private Long thingId; + + private String thingCode; + + private String thingName; + + private String thingStatus; + + private List attrRspList; +} diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardDTO.java new file mode 100644 index 0000000..0ae3126 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardDTO.java @@ -0,0 +1,100 @@ +package com.thing.dashboard.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Schema( name= "看板表") +public class IotDashboardDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + @Schema(description = "id") + private Long id; + + @Schema(description = "看板管理id") + private Long dashboardGroupId; + + @Schema(description = "svgXml/svg上传需要,组态url配置,不需要") + //@NotBlank(message = "svg不能为空", groups = DefaultGroup.class) + private String svgXml; + + @Schema(description = "svg图片地址/组态设计的url地址") + private String svgUrl; + + @Schema(description = "看板类型/0:svg看板,1:组态设计看板") + private String type; + + @Schema(description = "组态看板url") + private String scadaUrl; + + @Schema(description = "图片地址") + private String imgUrl; + + @Schema(description = "标题") + private String title; + + @Schema(description = "高度") + private String height; + + @Schema(description = "宽度") + private String width; + + @Schema(description = "背景颜色") + private String backgroundColor; + + @Schema(description = "背景图片") + private String backgroundPicture; + + @Schema(description = "描述") + private String remark; + @Schema(description = "排序") + private Integer sort; + + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + + @Schema(description = "企业详情id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; + + @Schema(description = "看板元素集合") + private List iotDashboardElementDTOList; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementAttrValueDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementAttrValueDTO.java new file mode 100644 index 0000000..bb86ca0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementAttrValueDTO.java @@ -0,0 +1,40 @@ +package com.thing.dashboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 看板元素映射属性最新值 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Schema( name= "看板元素映射属性最新值") +public class IotDashboardElementAttrValueDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "元素id") + private String elementId; + + @Schema(description = "元素名称") + private String elementName; + + @Schema(description = "映射物") + private String thingCode; + + @Schema(description = "映射属性") + private String attrCode; + + @Schema(description = "ts") + private Long ts; + + @Schema(description = "值") + private String val; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementDTO.java new file mode 100644 index 0000000..099849e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardElementDTO.java @@ -0,0 +1,93 @@ +package com.thing.dashboard.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Schema( name= "看板元素表") +public class IotDashboardElementDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + @Schema(description = "id") + private Long id; + + @Schema(description = "看板id") + private Long dashboardId; + + @Schema(description = "元素id") + private String elementId; + + @Schema(description = "元素名称") + private String elementName; + + @Schema(description = "映射物id") + private Long thingId; + + @Schema(description = "映射物") + private String thingCode; + + @Schema(description = "映射属性") + private String attrCode; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "计算体") + private String calculateBody; + + @Schema(description = "控制配置,0-开启,1-关闭") + private String controlConfig; + + @Schema(description = "控制样式") + private String controlStyle; + + @Schema(description = "控制设备") + private String controlDevice; + + @Schema(description = "控制设备属性") + private String controlDeviceAttr; + + @Schema(description = "控制指令") + private Long controlCmdId; + + @Schema(description = "控制权限") + private String controlPermissions; + + @Schema(description = "对应关系") + private String controlRelation; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardGroupDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardGroupDTO.java new file mode 100644 index 0000000..4f91c14 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardGroupDTO.java @@ -0,0 +1,91 @@ +package com.thing.dashboard.dto; + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.sys.biz.dto.SysMenuDTO; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; + +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Schema( name= "看板管理表") +public class IotDashboardGroupDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + @Schema(description = "id") + private Long id; + + @Schema(description = "名称") + private String name; + + @Schema(description = "预览图") + private String previewPicture; + + @NotNull(message="是否配置菜单不能为空", groups = UpdateGroup.class) + @Schema(description = "是否配置菜单") + private String existMenu; + + @NotNull(message="是否生成分享地址不能为空", groups = UpdateGroup.class) + @Schema(description = "是否生成分享地址") + private String existUrl; + + @NotNull(message="是否是多个看板不能为空", groups = UpdateGroup.class) + @Schema(description = "是否是多个看板") + private String multiple; + + @NotNull(message="是否开启轮播不能为空", groups = UpdateGroup.class) + @Schema(description = "是否开启轮播") + private String slideshow; + + @Schema(description = "轮播间隔") + private String slideshowInterval; + + @Schema(description = "描述") + private String remark; + + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + + @Schema(description = "企业详情id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; + + @Schema(description = "配置菜单信息") + private SysMenuDTO sysMenuDTO; + + @Schema(description = "分享地址") + private JSONObject shareUrl; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardSvgDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardSvgDTO.java new file mode 100644 index 0000000..4bc0835 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/IotDashboardSvgDTO.java @@ -0,0 +1,29 @@ +package com.thing.dashboard.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 看板svg +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2023-03-03 +*/ +@Data +@Schema( name= "看板svg") +public class IotDashboardSvgDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "看板主键") + private Long dashboardId; + @Schema(description = "svg") + private String svgXml; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/dto/SortDTO.java b/modules/thing/src/main/java/com/thing/dashboard/dto/SortDTO.java new file mode 100644 index 0000000..98ed1be --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/dto/SortDTO.java @@ -0,0 +1,24 @@ +package com.thing.dashboard.dto; + +import com.thing.common.core.validator.group.DefaultGroup; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotNull; + +import lombok.Data; + +/** + * @author zhenghh. 2023-04-03 + */ +@Data +@Schema( name= "排序") +public class SortDTO { + @NotNull(message="主键不能为空", groups = DefaultGroup.class) + @Schema(description = "主键") + private Long id; + @NotNull(message="序号不能为空", groups = DefaultGroup.class) + @Schema(description = "序号") + private Integer sort; +} diff --git a/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardElementEntity.java b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardElementEntity.java new file mode 100644 index 0000000..8d0bace --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardElementEntity.java @@ -0,0 +1,89 @@ +package com.thing.dashboard.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_dashboard_element") +public class IotDashboardElementEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 看板id + */ + private Long dashboardId; + + /** + * 元素id + */ + private String elementId; + /** + * 元素名称 + */ + private String elementName; + + /** + * 映射物 + */ + private Long thingId; + + /** + * 映射物 + */ + private String thingCode; + /** + * 映射属性 + */ + private String attrCode; + /** + * 单位 + */ + private String unit; + /** + * 计算体 + */ + private String calculateBody; + /** + * 控制配置,0-开启,1-关闭 + */ + private String controlConfig; + /** + * 控制样式 + */ + private String controlStyle; + /** + * 控制设备 + */ + private String controlDevice; + /** + * 控制设备属性 + */ + private String controlDeviceAttr; + /** + * 控制指令 + */ + private Long controlCmdId; + /** + * 控制权限 + */ + private String controlPermissions; + /** + * 对应关系 + */ + private String controlRelation; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardEntity.java b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardEntity.java new file mode 100644 index 0000000..a124be4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardEntity.java @@ -0,0 +1,73 @@ +package com.thing.dashboard.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_dashboard") +public class IotDashboardEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 看板管理id + */ + private Long dashboardGroupId; + /** + * svg图片地址 + */ + private String svgUrl; + /** + * 图片地址 + */ + private String imgUrl; + /** + * 看板类型/0:svg看板,1:组态设计看板 + */ + private String type="0"; + /** + * 组态看板url + */ + private String scadaUrl; + /** + * 标题 + */ + private String title; + /** + * 高度 + */ + private String height; + /** + * 宽度 + */ + private String width; + /** + * 背景颜色 + */ + private String backgroundColor; + /** + * 背景图片 + */ + private String backgroundPicture; + /** + * 描述 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardGroupEntity.java b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardGroupEntity.java new file mode 100644 index 0000000..12931f8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardGroupEntity.java @@ -0,0 +1,58 @@ +package com.thing.dashboard.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_dashboard_group") +public class IotDashboardGroupEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 名称 + */ + private String name; + /** + * 预览图 + */ + private String previewPicture; + /** + * 是否配置菜单 + */ + private String existMenu; + /** + * 是否生成分享地址 + */ + private String existUrl; + /** + * 是否是多个看板 + */ + private String multiple; + /** + * 是否开启轮播 + */ + private String slideshow; + /** + * 轮播间隔 + */ + private String slideshowInterval; + /** + * 描述 + */ + private String remark; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardSvgEntity.java b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardSvgEntity.java new file mode 100644 index 0000000..ff50d0e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/entity/IotDashboardSvgEntity.java @@ -0,0 +1,34 @@ +package com.thing.dashboard.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 看板svg + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2023-03-03 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_dashboard_svg") +public class IotDashboardSvgEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 看板主键 + */ + private Long dashboardId; + /** + * svg + */ + private String svgXml; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardElementExcel.java b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardElementExcel.java new file mode 100644 index 0000000..834bd9f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardElementExcel.java @@ -0,0 +1,23 @@ +package com.thing.dashboard.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +public class IotDashboardElementExcel { + @Excel(name = "元素ID") + private String elementId; + @Excel(name = "元素名称") + private String elementName; + @Excel(name = "映射物") + private String thingCode; + @Excel(name = "映射属性") + private String attrCode; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardExcel.java b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardExcel.java new file mode 100644 index 0000000..129dcec --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardExcel.java @@ -0,0 +1,47 @@ +package com.thing.dashboard.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +public class IotDashboardExcel { + @Excel(name = "id") + private Long id; + @Excel(name = "svg图片地址") + private String svgUrl; + @Excel(name = "标题") + private String title; + @Excel(name = "高度") + private String height; + @Excel(name = "宽度") + private String width; + @Excel(name = "背景颜色") + private String backgroundColor; + @Excel(name = "背景图片") + private String backgroundPicture; + @Excel(name = "描述") + private String remark; + @Excel(name = "所属企业(租户code)") + private Long tenantCode; + @Excel(name = "企业详情id") + private Long companyId; + @Excel(name = "部门id") + private Long deptId; + @Excel(name = "创建者") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新者") + private Long updater; + @Excel(name = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardGroupExcel.java b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardGroupExcel.java new file mode 100644 index 0000000..cad5334 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/excel/IotDashboardGroupExcel.java @@ -0,0 +1,39 @@ +package com.thing.dashboard.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Data +public class IotDashboardGroupExcel { + @Excel(name = "id") + private Long id; + @Excel(name = "名称") + private String name; + @Excel(name = "预览图") + private String icon; + @Excel(name = "描述") + private String remark; + @Excel(name = "所属企业(租户code)") + private Long tenantCode; + @Excel(name = "企业详情id") + private Long companyId; + @Excel(name = "部门id") + private Long deptId; + @Excel(name = "创建者") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新者") + private Long updater; + @Excel(name = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardElementMapper.java b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardElementMapper.java new file mode 100644 index 0000000..be017e5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardElementMapper.java @@ -0,0 +1,17 @@ +package com.thing.dashboard.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.dashboard.entity.IotDashboardElementEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Mapper +public interface IotDashboardElementMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardGroupMapper.java b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardGroupMapper.java new file mode 100644 index 0000000..5c094fc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardGroupMapper.java @@ -0,0 +1,17 @@ +package com.thing.dashboard.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.dashboard.entity.IotDashboardGroupEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Mapper +public interface IotDashboardGroupMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardMapper.java b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardMapper.java new file mode 100644 index 0000000..9cc1951 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardMapper.java @@ -0,0 +1,17 @@ +package com.thing.dashboard.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.dashboard.entity.IotDashboardEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Mapper +public interface IotDashboardMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardSvgMapper.java b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardSvgMapper.java new file mode 100644 index 0000000..a15a78e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/mapper/IotDashboardSvgMapper.java @@ -0,0 +1,18 @@ +package com.thing.dashboard.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.dashboard.entity.IotDashboardSvgEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** +* 看板svg +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2023-03-03 +*/ +@Mapper +public interface IotDashboardSvgMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardElementService.java b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardElementService.java new file mode 100644 index 0000000..7546115 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardElementService.java @@ -0,0 +1,46 @@ +package com.thing.dashboard.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.dashboard.dto.IotDashboardElementDTO; +import com.thing.dashboard.entity.IotDashboardElementEntity; +import com.thing.dashboard.excel.IotDashboardElementExcel; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +public interface IotDashboardElementService extends IBaseService { + IotDashboardElementDTO getElementByElementId(Long dashboardId, String elementId); + List getElementByDashboardId(Long dashboardId); + void saveOrUpdateIotDashboardElementDTO(List dtoList); + void deleteByDashBoard(List dashBoardIds); + void exportIotDashboardElement(Map params, List ids, HttpServletResponse response); + Set importIotDashboardElement(Long dashBoardId, MultipartFile file, HttpServletRequest request); + List selectIotDashboardElementByDashboardIds(List dashboardIdList); + + /** + * 转换 + * @param list 列表 + * @param response response + */ + void transform(List list, HttpServletResponse response); + + /** + * 看板权限控制 + * @param id 主键 + */ + void validatePermissions(Long id); + + void delElementByDashboardId(Long dashboardId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardGroupService.java b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardGroupService.java new file mode 100644 index 0000000..e5352ae --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardGroupService.java @@ -0,0 +1,43 @@ +package com.thing.dashboard.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.dashboard.entity.IotDashboardGroupEntity; + +import java.util.List; +import java.util.Map; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +public interface IotDashboardGroupService extends IBaseService { + /** + * 看板管理分页 + * @param params + */ + PageData pageIotDashboardGroupDTO(Map params); + /** + * 看板管理单条信息 + * @param id + */ + IotDashboardGroupDTO getIotDashboardGroupDTO(Long id); + /** + * 新增看板管理 + * @param dto + */ + void saveIotDashboardGroupDTO(IotDashboardGroupDTO dto); + /** + * 编辑看板管理 + * @param dto + */ + void updateIotDashboardGroupDTO(IotDashboardGroupDTO dto); + /** + * 删除看板管理 + * @param ids + */ + void deleteIotDashboardGroupDTO(List ids); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardService.java b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardService.java new file mode 100644 index 0000000..9f61228 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardService.java @@ -0,0 +1,60 @@ +package com.thing.dashboard.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.dashboard.dto.IotDashboardDTO; +import com.thing.dashboard.dto.IotDashboardElementAttrValueDTO; +import com.thing.dashboard.dto.SortDTO; +import com.thing.dashboard.entity.IotDashboardEntity; + +import java.util.List; +import java.util.Map; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +public interface IotDashboardService extends IBaseService { + /** + * 新增一个看板 + */ + void saveOrUpdateIotDashboardDTO(IotDashboardDTO dto); + /** + * 删除一个看板 + */ + void deleteIotDashboardDTO(List ids); + /** + * 看板列表 + */ + List listIotDashboardDTO(Map params); + /** + * 看板列表(分享版) + */ + List listShare(Long dashboardGroupId); + /** + * 信息(附带看板内元素) + */ + IotDashboardDTO getIotDashboardDTO(Long id); + + /** + * 获取svg xml + * @param id 看板朱剑 + * @return xml + */ + String svgXml(Long id); + + + /** + * 获取看板映射属性的最新值 + * @param params params + * @return List + */ + List getDashboardElementLatestAttr(Map params); + + /** + * 排序 + * @param sortList 列表 + */ + void sort(List sortList); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardSvgService.java b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardSvgService.java new file mode 100644 index 0000000..ca8873f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/IotDashboardSvgService.java @@ -0,0 +1,36 @@ +package com.thing.dashboard.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.dashboard.entity.IotDashboardSvgEntity; + +import java.util.List; + +/** + * 看板svg + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2023-03-03 + */ +public interface IotDashboardSvgService extends IBaseService { + + /** + * 保存 + * + * @param dashboardId 看板主键 + * @param svgXml xml + */ + void saveSvgXml(Long dashboardId, String svgXml); + + /** + * 获取xml + * @param dashboardId 看板主键 + * @return IotDashboardSvgEntity + */ + IotDashboardSvgEntity findByDashboard(Long dashboardId); + + /** + * 删除 + * @param dashboardIds 看板主键集合 + */ + void deleteByDashBoard(List dashboardIds); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardElementServiceImpl.java b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardElementServiceImpl.java new file mode 100644 index 0000000..7a6f6ca --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardElementServiceImpl.java @@ -0,0 +1,160 @@ +package com.thing.dashboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.dashboard.dto.IotDashboardElementDTO; +import com.thing.dashboard.entity.IotDashboardElementEntity; +import com.thing.dashboard.excel.IotDashboardElementExcel; +import com.thing.dashboard.mapper.IotDashboardElementMapper; +import com.thing.dashboard.service.IotDashboardElementService; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 看板元素表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Service +@RequiredArgsConstructor +public class IotDashboardElementServiceImpl extends BaseServiceImpl implements IotDashboardElementService { + + private final ThingManageContextService contextService; + + @Override + public QueryWrapper getWrapper(Map params){ + String dashboardId = (String) params.get("dashboardId"); + QueryWrapper wrapper = new QueryWrapper(); + if (StringUtils.isBlank(dashboardId)) throw new SysException("看板id为必传字段"); + wrapper.eq("dashboard_id", dashboardId); + return wrapper; + } + + + @Override + public IotDashboardElementDTO getElementByElementId(Long dashboardId, String elementId) { + if (ObjectUtil.isNull(dashboardId) || StringUtils.isBlank(elementId)) throw new SysException("看板id或元素id为空,无法查询"); + IotDashboardElementEntity entity = mapper.selectOneByQuery(QueryWrapper.create().eq(IotDashboardElementEntity::getElementId, elementId).eq(IotDashboardElementEntity::getDashboardId, dashboardId).limit(1)); + return ObjectUtil.isNotNull(entity) ? ConvertUtils.sourceToTarget(entity, IotDashboardElementDTO.class) : null; + } + + @Override + public List getElementByDashboardId(Long dashboardId) { + if (ObjectUtil.isNull(dashboardId)) throw new SysException("看板id为空,无法查询"); + List entityList = mapper.selectListByQuery(QueryWrapper.create().eq(IotDashboardElementEntity::getDashboardId, dashboardId)); + return CollectionUtil.isNotEmpty(entityList) ? ConvertUtils.sourceToTarget(entityList, IotDashboardElementDTO.class) : Lists.newArrayList(); + } + + @Override + public void saveOrUpdateIotDashboardElementDTO(List dtoList) { + List insertList = dtoList.stream().filter(item -> ObjectUtil.isNull(item.getId())).collect(Collectors.toList()); + List pendingList = dtoList.stream().filter(item -> ObjectUtil.isNotNull(item.getId())).toList(); + List updateList = pendingList.stream().filter(item -> StringUtils.isNotBlank(item.getThingCode()) && StringUtils.isNotBlank(item.getAttrCode())).collect(Collectors.toList()); + List deleteList = pendingList.stream().filter(item -> StringUtils.isBlank(item.getThingCode()) || StringUtils.isBlank(item.getAttrCode())).collect(Collectors.toList()); + mapper.insertBatch(ConvertUtils.sourceToTarget(insertList, IotDashboardElementEntity.class)); + updateBatch(ConvertUtils.sourceToTarget(updateList, IotDashboardElementEntity.class)); + if (CollectionUtil.isNotEmpty(deleteList)) mapper.deleteBatchByIds(deleteList.stream().map(IotDashboardElementDTO::getId).collect(Collectors.toList())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByDashBoard(List dashBoardIds) { + mapper.deleteByQuery(QueryWrapper.create().in(IotDashboardElementEntity::getDashboardId, dashBoardIds)); + } + + @Override + public void exportIotDashboardElement(Map params, List ids, HttpServletResponse response) { + List iotDashboardElementDTOList = listAs(getWrapper(params), IotDashboardElementDTO.class); + ExcelUtils.exportExcel(ConvertUtils.sourceToTarget(iotDashboardElementDTOList, IotDashboardElementExcel.class), "看板元素表", "看板元素", IotDashboardElementExcel.class, "看板元素.xls", response); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public Set importIotDashboardElement(Long dashBoardId, MultipartFile file, HttpServletRequest request) { + Set resultInfo = new HashSet<>(); + + List sheetData = ExcelUtils.importExcel(file, 1, 1, IotDashboardElementExcel.class, 0); + if(CollectionUtil.isEmpty(sheetData)) { + return new HashSet<>(); + } + //1.清除表中所有的关系 + mapper.deleteByQuery(QueryWrapper.create().eq(IotDashboardElementEntity::getDashboardId, dashBoardId)); + List insertSheetData = Lists.newArrayList(); + //2.新增导入数据中存在的关系 + for (IotDashboardElementExcel item : sheetData) { + if (StringUtils.isNotBlank(item.getThingCode()) && StringUtils.isNotBlank(item.getAttrCode())) { + //1.存不存在该物 + Optional optional = contextService.findEntityByCode(item.getThingCode(), UserContext.getRealTenantCode(),true); + if (optional.isEmpty()){ + throw new SysException("不存在该编码" + item.getThingCode() + "的物"); + } + //2.存不存在该属性 + Optional> dictRelationDTOS = contextService.findDictRelationAllByEntityIdAndCodes(optional.get().getId(), Collections.singletonList(item.getAttrCode())); + if (dictRelationDTOS.isEmpty()){ + throw new SysException("不存在该编码" + item.getThingCode() + "的物和该编码" + item.getThingCode() + "的属性的对应关系"); + } + insertSheetData.add(item); + } + } + + List iotDashboardElementDTOList = ConvertUtils.sourceToTarget(insertSheetData, IotDashboardElementEntity.class); + iotDashboardElementDTOList.forEach(temp-> temp.setDashboardId(dashBoardId)); + if(CollectionUtil.isNotEmpty(iotDashboardElementDTOList)) { + mapper.insertBatch(iotDashboardElementDTOList); + } + return resultInfo; + } + + @Override + public List selectIotDashboardElementByDashboardIds(List dashboardIdList) { + List entityList = mapper.selectListByQuery(QueryWrapper.create().in(IotDashboardElementEntity::getDashboardId, dashboardIdList)); + return CollectionUtil.isNotEmpty(entityList) ? ConvertUtils.sourceToTarget(entityList, IotDashboardElementDTO.class) : Lists.newArrayList(); + } + + @Override + public void transform(List list, HttpServletResponse response) { + ExcelUtils.exportExcel(list, "看板元素表", "看板元素", IotDashboardElementExcel.class, "看板元素.xls", response); + } + + /** + * 看板权限控制 + * + * @param id 主键 + */ + @Override + public void validatePermissions(Long id) { + UserDetail userDetail = SecurityUser.getUser(); + IotDashboardElementDTO dto = getByIdAs(id, IotDashboardElementDTO.class); + if(dto != null && StringUtils.isNotBlank(dto.getControlPermissions()) && userDetail.getId() != null) { + if(!dto.getControlPermissions().contains(String.valueOf(userDetail.getId()))) { + throw new SysException("无权限"); + } + } + } + + @Override + public void delElementByDashboardId(Long dashboardId) { + mapper.deleteByQuery(QueryWrapper.create().eq(IotDashboardElementEntity::getDashboardId, dashboardId)); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardGroupServiceImpl.java b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardGroupServiceImpl.java new file mode 100644 index 0000000..a74b21e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardGroupServiceImpl.java @@ -0,0 +1,227 @@ +package com.thing.dashboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.util.ObjectUtil; + +import com.alibaba.fastjson.JSONObject; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.dashboard.mapper.IotDashboardMapper; +import com.thing.dashboard.mapper.IotDashboardElementMapper; +import com.thing.dashboard.mapper.IotDashboardGroupMapper; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.dashboard.entity.IotDashboardElementEntity; +import com.thing.dashboard.entity.IotDashboardEntity; +import com.thing.dashboard.entity.IotDashboardGroupEntity; +import com.thing.dashboard.service.IotDashboardGroupService; +import com.thing.dashboard.service.IotDashboardService; +import com.thing.sys.biz.dto.SysMenuDTO; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysLanguageEntity; +import com.thing.sys.biz.entity.SysMenuEntity; +import com.thing.sys.biz.entity.SysRoleMenuEntity; +import com.thing.sys.biz.mapper.SysLanguageMapper; +import com.thing.sys.biz.mapper.SysRoleMenuMapper; +import com.thing.sys.biz.service.SysLanguageService; +import com.thing.sys.biz.service.SysMenuService; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysRoleService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 看板管理表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Service +@RequiredArgsConstructor +public class IotDashboardGroupServiceImpl extends BaseServiceImpl implements IotDashboardGroupService { + private final SysMenuService sysMenuService; + private final SysLanguageMapper sysLanguageDao; + private final SysLanguageService sysLanguageService; + private final SysRoleService sysRoleService; + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleMenuMapper sysRoleMenuDao; + private final IotDashboardMapper iotDashboardMapper; + private final IotDashboardService iotDashboardService; + private final IotDashboardElementMapper iotDashboardElementMapper; + + + @Override + public QueryWrapper getWrapper(Map params){ + String name = (String) params.get("name"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("name", name, StringUtils.isNotBlank(name)); + return wrapper; + } + + + @Override + @DataFilter(completeDataMark = false) + public PageData pageIotDashboardGroupDTO(Map params) { + PageData pageData = getPageData(params, IotDashboardGroupDTO.class); + List list = pageData.getList(); + if (CollectionUtil.isEmpty(list)) return pageData; + List urlList = list.stream().map(item -> "dashboard/index?id=" + item.getId()).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(urlList)) return pageData; + List sysMenuDTOList = sysMenuService.getByUrlList(urlList); + if (CollectionUtil.isEmpty(sysMenuDTOList)) return pageData; + Map stringSysMenuEntityMap = sysMenuDTOList.stream().collect(Collectors.toMap(itemKey -> itemKey.getUrl().substring(itemKey.getUrl().lastIndexOf("id=") + 3), item -> item)); + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + list = list.stream().peek(item -> { + SysMenuDTO dto = stringSysMenuEntityMap.get(String.valueOf(item.getId())); + if (ObjectUtil.isNotNull(dto)) { + if (CompareUtil.compare(item.getExistMenu(), "1") == 0) item.setSysMenuDTO(ConvertUtils.sourceToTarget(dto, SysMenuDTO.class)); + } + if (CompareUtil.compare(item.getExistUrl(), "1") == 0) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", item.getId()); + jsonObject.put("interval", CompareUtil.compare(item.getSlideshow(), "1") == 0 ? item.getSlideshowInterval() : ""); + jsonObject.put("tenantCode", tenantCode); + item.setShareUrl(jsonObject); + } + }).collect(Collectors.toList()); + pageData.setList(list); + return pageData; + } + + @Override + public IotDashboardGroupDTO getIotDashboardGroupDTO(Long id) { + IotDashboardGroupDTO dto = getByIdAs(id, IotDashboardGroupDTO.class); + SysMenuDTO sysMenuDTO = sysMenuService.getByUrl("dashboard/index?id=" + dto.getId()); + if (ObjectUtil.isNotNull(sysMenuDTO)) { + if (CompareUtil.compare(dto.getExistMenu(), "1") == 0) dto.setSysMenuDTO(sysMenuDTO); + } + if (CompareUtil.compare(dto.getExistUrl(), "1") == 0) { + JSONObject jsonObject = new JSONObject(); + jsonObject.put("id", dto.getId()); + jsonObject.put("interval", CompareUtil.compare(dto.getSlideshow(), "1") == 0 ? dto.getSlideshowInterval() : ""); + jsonObject.put("tenantCode", TenantContext.getTenantCode(SecurityUser.getUser())); + dto.setShareUrl(jsonObject); + } + return dto; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveIotDashboardGroupDTO(IotDashboardGroupDTO dto) { + //新增看板管理信息 + IotDashboardGroupEntity entity = ConvertUtils.sourceToTarget(dto, IotDashboardGroupEntity.class); + save(entity); + + if (CompareUtil.compare(dto.getExistMenu(), "1") == 0) { + //新增菜单信息 + SysMenuDTO menuDTO = dto.getSysMenuDTO(); + if (ObjectUtil.isNull(menuDTO) || ObjectUtil.isNull(menuDTO.getPid()) || ObjectUtil.isNull(menuDTO.getName())) throw new SysException("菜单参数未进行上传"); + menuDTO.setType(0); + menuDTO.setOpenStyle(0); + menuDTO.setSaView(1); + menuDTO.setUrl("dashboard/index?id=" + entity.getId()); + SysMenuEntity sysMenuEntity = ConvertUtils.sourceToTarget(menuDTO, SysMenuEntity.class); + sysMenuService.save(sysMenuEntity); + sysLanguageService.saveOrUpdate("sys_menu", sysMenuEntity.getId(), "name", sysMenuEntity.getName(), HttpContextUtils.getLanguage()); + + //将菜单分配给对应的租户/企业下的默认角色 + SysRoleDTO sysRoleDTO = sysRoleService.getDefaultRole(new HashMap<>(1)); + if (ObjectUtil.isNull(sysRoleDTO)) return; + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setRoleId(sysRoleDTO.getId()); + sysRoleMenuEntity.setMenuId(sysMenuEntity.getId()); + sysRoleMenuService.save(sysRoleMenuEntity); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateIotDashboardGroupDTO(IotDashboardGroupDTO dto) { + //更新看板管理信息 + IotDashboardGroupEntity entity = ConvertUtils.sourceToTarget(dto, IotDashboardGroupEntity.class); + updateById(entity); + + if (CompareUtil.compare(dto.getExistMenu(), "1") == 0) { + //更新菜单信息 + SysMenuDTO menuDTO = dto.getSysMenuDTO(); + if (ObjectUtil.isNull(menuDTO) || ObjectUtil.isNull(menuDTO.getPid()) || ObjectUtil.isNull(menuDTO.getName())) throw new SysException("菜单参数未进行上传"); + if (ObjectUtil.isNotNull(menuDTO.getId())) { + sysMenuService.update(menuDTO); + } else { + //新增菜单信息 + menuDTO.setType(0); + menuDTO.setOpenStyle(0); + menuDTO.setSaView(1); + menuDTO.setUrl("dashboard/index?id=" + entity.getId()); + SysMenuEntity sysMenuEntity = ConvertUtils.sourceToTarget(menuDTO, SysMenuEntity.class); + sysMenuService.save(sysMenuEntity); + sysLanguageService.saveOrUpdate("sys_menu", sysMenuEntity.getId(), "name", sysMenuEntity.getName(), HttpContextUtils.getLanguage()); + + //将菜单分配给对应的租户/企业下的默认角色 + SysRoleDTO sysRoleDTO = sysRoleService.getDefaultRole(new HashMap<>(1)); + if (ObjectUtil.isNull(sysRoleDTO)) return; + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setRoleId(sysRoleDTO.getId()); + sysRoleMenuEntity.setMenuId(sysMenuEntity.getId()); + sysRoleMenuService.save(sysRoleMenuEntity); + } + } else { + List entityList = mapper.selectListExt(QueryWrapper.create().in(IotDashboardGroupEntity::getId, entity.getId())); + List urlList = entityList.stream().map(item -> "dashboard/index?id=" + item.getId()).collect(Collectors.toList()); + List sysMenuDTOList = sysMenuService.getByUrlList(urlList); + if (CollectionUtil.isNotEmpty(sysMenuDTOList)) { + List menuIdList = sysMenuDTOList.stream().map(SysMenuDTO::getId).collect(Collectors.toList()); + sysRoleMenuDao.deleteByMenuIds(menuIdList); + //删除菜单表 + sysMenuService.getMapper().deleteBatchByIds(menuIdList); + sysLanguageDao.deleteByQuery(QueryWrapper.create().in(SysLanguageEntity::getTableId, menuIdList)); + } + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteIotDashboardGroupDTO(List ids) { + if (CollectionUtil.isEmpty(ids)) return; + List entityList = mapper.selectListExt(QueryWrapper.create().in(IotDashboardGroupEntity::getId, ids)); + //删除角色与菜单的关联表 + List urlList = entityList.stream().map(item -> "dashboard/index?id=" + item.getId()).collect(Collectors.toList()); + List sysMenuDTOList = sysMenuService.getByUrlList(urlList); + if (CollectionUtil.isNotEmpty(sysMenuDTOList)) { + List menuIdList = sysMenuDTOList.stream().map(SysMenuDTO::getId).collect(Collectors.toList()); + sysRoleMenuDao.deleteByMenuIds(menuIdList); + //删除菜单表 + sysMenuService.getMapper().deleteBatchByIds(menuIdList); + sysLanguageDao.deleteByQuery(QueryWrapper.create().in(SysLanguageEntity::getTableId, menuIdList)); + } + + //删除看板相关表 + QueryWrapper wrapper = QueryWrapper.create().in(IotDashboardEntity::getDashboardGroupId, ids); + List iotDashboardEntityList = iotDashboardMapper.selectListByQuery(wrapper); + if (CollectionUtil.isNotEmpty(iotDashboardEntityList)) { + List dashboardIdList = iotDashboardEntityList.stream().map(IotDashboardEntity::getId).collect(Collectors.toList()); + //删除看板元素表 + if (CollectionUtil.isNotEmpty(dashboardIdList)) iotDashboardElementMapper.deleteByQuery(QueryWrapper.create().in(IotDashboardElementEntity::getDashboardId, dashboardIdList)); + //删除看板表 + iotDashboardService.getMapper().deleteBatchByIds(dashboardIdList); + } + //删除看板管理表 + mapper.deleteBatchByIds(ids); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardServiceImpl.java b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardServiceImpl.java new file mode 100644 index 0000000..4299a62 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardServiceImpl.java @@ -0,0 +1,245 @@ +package com.thing.dashboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.data.dto.ThingAttrParam; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.dashboard.dto.*; +import com.thing.dashboard.entity.IotDashboardElementEntity; +import com.thing.dashboard.entity.IotDashboardEntity; +import com.thing.dashboard.mapper.IotDashboardMapper; +import com.thing.dashboard.service.IotDashboardElementService; +import com.thing.dashboard.service.IotDashboardService; +import com.thing.dashboard.service.IotDashboardSvgService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 看板表 + * + * @author zzx + * @since 1.0.0 2022-12-05 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class IotDashboardServiceImpl extends BaseServiceImpl implements IotDashboardService { + + private final IotDashboardElementService iotDashboardElementService; + private final IotDashboardSvgService dashboardSvgService; + private final ThingManageContextService thingManageContextService; + private final TsKvService tsKvService; + private final RestTemplate restTemplate; + + + @Override + public QueryWrapper getWrapper(Map params) { + String dashboardGroupId = (String) params.get("dashboardGroupId"); + QueryWrapper wrapper = new QueryWrapper(); + if (StringUtils.isBlank(dashboardGroupId)) throw new SysException("看板组id为必传字段"); + wrapper.eq("dashboard_group_id", Long.valueOf(dashboardGroupId)) + .orderBy(IotDashboardEntity::getSort).asc() + .orderBy(IotDashboardEntity::getCreateDate).desc(); + return wrapper; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdateIotDashboardDTO(IotDashboardDTO dto) { + //保存看板 + if (ObjectUtil.isNotNull(dto.getId())) { + if(!mapper.selectOneById(dto.getId()).getSvgUrl().equals(dto.getSvgUrl())){ + iotDashboardElementService.delElementByDashboardId(dto.getId()); + } + updateDto(dto); + } else { + saveDto(dto); + } + if(ObjectUtil.isNotNull(dto.getSvgXml())){ + //保存xml + dashboardSvgService.saveSvgXml(dto.getId(), dto.getSvgXml()); + } + //保存看板元素 + List iotDashboardElementDTOList = dto.getIotDashboardElementDTOList(); + if (CollectionUtil.isNotEmpty(iotDashboardElementDTOList)) { + iotDashboardElementDTOList = iotDashboardElementDTOList.stream().peek(item -> item.setDashboardId(dto.getId())).collect(Collectors.toList()); + List insertList = iotDashboardElementDTOList.stream().filter(item -> ObjectUtil.isNull(item.getId())).collect(Collectors.toList()); + List updateList = iotDashboardElementDTOList.stream().filter(item -> ObjectUtil.isNotNull(item.getId())).collect(Collectors.toList()); + iotDashboardElementService.saveBatch(ConvertUtils.sourceToTarget(insertList, IotDashboardElementEntity.class)); + iotDashboardElementService.updateBatch(ConvertUtils.sourceToTarget(updateList, IotDashboardElementEntity.class)); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteIotDashboardDTO(List ids) { + //删除看板 + mapper.deleteBatchByIds(ids); + //删除看板内的元素 + iotDashboardElementService.deleteByDashBoard(ids); + dashboardSvgService.deleteByDashBoard(ids); + } + + @Override + public List listIotDashboardDTO(Map params) { + List iotDashboardDTOList = listAs(params, IotDashboardDTO.class); + List dashboardIdList = iotDashboardDTOList.stream().map(IotDashboardDTO::getId).collect(Collectors.toList()); + List iotDashboardElementDTOList = iotDashboardElementService.selectIotDashboardElementByDashboardIds(dashboardIdList); + if (CollectionUtil.isEmpty(iotDashboardElementDTOList)) return iotDashboardDTOList; + Map> map = iotDashboardElementDTOList.stream().collect(Collectors.groupingBy(IotDashboardElementDTO::getDashboardId)); + return iotDashboardDTOList.stream().peek(item -> item.setIotDashboardElementDTOList(map.get(item.getId()))).collect(Collectors.toList()); + } + + @Override + public List listShare(Long dashboardGroupId) { + List list = mapper.selectListByQuery(QueryWrapper.create().eq(IotDashboardEntity::getDashboardGroupId, dashboardGroupId) + .orderBy(IotDashboardEntity::getSort).asc().orderBy(IotDashboardEntity::getCreateDate).desc()); + return CollectionUtil.isNotEmpty(list) ? ConvertUtils.sourceToTarget(list, IotDashboardDTO.class) : Lists.newArrayList(); + } + + @Override + public IotDashboardDTO getIotDashboardDTO(Long id) { + IotDashboardDTO dto = getByIdAs(id, IotDashboardDTO.class); + List iotDashboardElementDTOList = iotDashboardElementService.selectIotDashboardElementByDashboardIds(Lists.newArrayList(dto.getId())); + if (CollectionUtil.isEmpty(iotDashboardElementDTOList)) return dto; + Map> map = iotDashboardElementDTOList.stream().collect(Collectors.groupingBy(IotDashboardElementDTO::getDashboardId)); + dto.setIotDashboardElementDTOList(map.get(dto.getId())); + return dto; + } + + @Override + public String svgXml(Long id) { + IotDashboardEntity dashboard = getById(id); + if(Objects.isNull(dashboard) || Objects.isNull(dashboard.getSvgUrl())) { + throw new SysException("看板svg图片地址不存在"); + } + try { + String result = restTemplate.getForObject(URI.create(dashboard.getSvgUrl()), String.class); + return StringUtils.isBlank(result) ? null : new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8); + } catch (Exception e) { + log.error("获取看板svg图片地址失败: {}", e.getMessage()); + } + return null; + } + + @Override + public List getDashboardElementLatestAttr(Map params) { + Long dashboardId = Long.parseLong(String.valueOf(params.get("dashboardId"))); + IotDashboardDTO dto = getIotDashboardDTO(dashboardId); + List iotDashboardElementDTOList = dto.getIotDashboardElementDTOList(); + if (CollectionUtil.isNotEmpty(iotDashboardElementDTOList)) { + Map> stringListMap = iotDashboardElementDTOList.stream().collect(Collectors.groupingBy(IotDashboardElementDTO::getThingCode)); + List deviceDataParamList = iotDashboardElementDTOList.stream().filter(item -> StringUtils.isNotBlank(item.getThingCode()) && StringUtils.isNotBlank(item.getAttrCode())).map(item -> { + ThingAttrParam thingAttrParam = new ThingAttrParam(); + thingAttrParam.setThingCode(item.getThingCode()); + thingAttrParam.setAttrList(Lists.newArrayList(item.getAttrCode())); + return thingAttrParam; + }).collect(Collectors.toList()); + List dashBoardRspDTOList = getThingAndLatestAttrShare(deviceDataParamList); + if (CollectionUtil.isNotEmpty(dashBoardRspDTOList)) { + List iotDashboardElementAttrValueDTOList = Lists.newArrayList(); + dashBoardRspDTOList.forEach(item -> { + List tempList = Lists.newArrayList(); + Map attrRspDTOMap = item.getAttrRspList().stream().collect(Collectors.toMap(IotAttrRspDTO::getAttrKey, itemObject -> itemObject)); + List elementDTOList = stringListMap.get(item.getThingCode()); + if (CollectionUtil.isNotEmpty(elementDTOList)) { + for (IotDashboardElementDTO dashboardElementDTO : elementDTOList) { + IotDashboardElementAttrValueDTO dashboardElementAttrValueDTO = new IotDashboardElementAttrValueDTO(); + IotAttrRspDTO attrRspDTO = attrRspDTOMap.get(dashboardElementDTO.getAttrCode()); + if (ObjectUtil.isNull(attrRspDTO)) continue; + dashboardElementAttrValueDTO.setElementId(dashboardElementDTO.getElementId()); + dashboardElementAttrValueDTO.setElementName(dashboardElementDTO.getElementName()); + dashboardElementAttrValueDTO.setThingCode(item.getThingCode()); + dashboardElementAttrValueDTO.setAttrCode(attrRspDTO.getAttrKey()); + dashboardElementAttrValueDTO.setTs(attrRspDTO.getTs()); + dashboardElementAttrValueDTO.setVal(attrRspDTO.getVal()); + tempList.add(dashboardElementAttrValueDTO); + } + } + iotDashboardElementAttrValueDTOList.addAll(tempList); + }); + return iotDashboardElementAttrValueDTOList; + } + } + return Lists.newArrayList(); + } + + private List getThingAndLatestAttrShare(List deviceDataParamList) { + if (CollectionUtil.isEmpty(deviceDataParamList)) throw new SysException("入参List为空"); + List dashBoardRspDTOList = Lists.newArrayList(); + deviceDataParamList = deviceDataParamList.stream().distinct().collect(Collectors.toList()); + + Map> paramMap = deviceDataParamList.stream().collect(Collectors.toMap( + ThingAttrParam::getThingCode, + ThingAttrParam::getAttrList, + (existing, replacement) -> existing, + LinkedHashMap::new // 使用 LinkedHashMap 保留顺序 + )); + List cacheTsKvLastestDTOList = tsKvService.findLatestByMultiMap(paramMap, true); + List thingCodeList = deviceDataParamList.parallelStream().map(ThingAttrParam::getThingCode).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(thingCodeList)) throw new SysException("缓存tb中均不存在该物,请联系管理员"); + Map> thingCodeMap = cacheTsKvLastestDTOList.parallelStream().collect(Collectors.groupingBy(TsKvDTO::getThingCode)); + for (Map.Entry> entry : thingCodeMap.entrySet()) { + IotDashBoardRspDTO rspDTO = new IotDashBoardRspDTO(); + rspDTO.setThingCode(entry.getKey()); + List dtoList = entry.getValue(); + List dictCode = dtoList.parallelStream().map(TsKvDTO::getAttrKey).collect(Collectors.toList()); + Optional optionalIotThingViewDTO = thingManageContextService.findEntityByCode(entry.getKey(), UserContext.getRealTenantCode(), true); + if(optionalIotThingViewDTO.isEmpty()){ + continue; + } + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdAndCodes(optionalIotThingViewDTO.get().getId(), dictCode); + if(optionalList.isEmpty()){ + continue; + } + List entityDictDTOList = optionalList.get(); + Map nameMap = entityDictDTOList.parallelStream().collect(Collectors.toMap(IotThingDictRelationDTO::getCode, IotThingDictRelationDTO::getName)); + Map unitMap = entityDictDTOList.parallelStream().collect(Collectors.toMap(IotThingDictRelationDTO::getCode, IotThingDictRelationDTO::getUnit)); + List attrRspList = dtoList.parallelStream().map(dto -> { + IotAttrRspDTO attrRspDTO = new IotAttrRspDTO(); + String attrKey = dto.getAttrKey(); + attrRspDTO.setAttrKey(attrKey); + attrRspDTO.setTs(dto.getTs()); + attrRspDTO.setVal(dto.getVal()); + attrRspDTO.setAttrName(nameMap.get(attrKey)); + attrRspDTO.setAttrUnit(String.valueOf(unitMap.get(attrKey))); + return attrRspDTO; + }).collect(Collectors.toList()); + rspDTO.setAttrRspList(attrRspList); + dashBoardRspDTOList.add(rspDTO); + } + return dashBoardRspDTOList; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void sort(List sortList) { + List ids = sortList.parallelStream().map(SortDTO::getId).collect(Collectors.toList()); + List iotDashboardEntities = mapper.selectListByIds(ids); + List collect = iotDashboardEntities.parallelStream().peek(item -> { + List dtos = sortList.stream().filter(sortDTO -> ObjectUtil.equals(sortDTO.getId(), item.getId())).toList(); + item.setSort(dtos.get(0).getSort()); + }).collect(Collectors.toList()); + updateBatch(collect); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardSvgServiceImpl.java b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardSvgServiceImpl.java new file mode 100644 index 0000000..54e6493 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/dashboard/service/impl/IotDashboardSvgServiceImpl.java @@ -0,0 +1,72 @@ +package com.thing.dashboard.service.impl; + +import cn.hutool.core.collection.CollectionUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.dashboard.mapper.IotDashboardSvgMapper; +import com.thing.dashboard.entity.IotDashboardSvgEntity; +import com.thing.dashboard.service.IotDashboardSvgService; + +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 看板svg + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2023-03-03 + */ +@Service +public class IotDashboardSvgServiceImpl extends BaseServiceImpl implements IotDashboardSvgService { + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + /** + * 保存 + * + * @param dashboardId 看板主键 + * @param svgXml xml + */ + @Override + public void saveSvgXml(Long dashboardId, String svgXml) { + if (dashboardId == null /*|| StringUtils.isBlank(svgXml)*/) { + throw new SysException("参数缺失"); + } + mapper.deleteByQuery(QueryWrapper.create().eq(IotDashboardSvgEntity::getDashboardId, dashboardId)); + IotDashboardSvgEntity entity = new IotDashboardSvgEntity(); + entity.setDashboardId(dashboardId); + entity.setSvgXml(svgXml); + save(entity); + } + + /** + * 获取xml + * + * @param dashboardId 看板主键 + * @return IotDashboardSvgEntity + */ + @Override + public IotDashboardSvgEntity findByDashboard(Long dashboardId) { + return mapper.selectOneByQuery(QueryWrapper.create() + .eq(IotDashboardSvgEntity::getDashboardId, dashboardId).limit(1)); + } + + /** + * 删除 + * + * @param dashboardIds 看板主键集合 + */ + @Override + public void deleteByDashBoard(List dashboardIds) { + if (CollectionUtil.isEmpty(dashboardIds)) { + return; + } + mapper.deleteByQuery(QueryWrapper.create().in(IotDashboardSvgEntity::getDashboardId, dashboardIds)); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/controller/AnalysisDataController.java b/modules/thing/src/main/java/com/thing/device/analysisdata/controller/AnalysisDataController.java new file mode 100644 index 0000000..760c3b2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/controller/AnalysisDataController.java @@ -0,0 +1,179 @@ +package com.thing.device.analysisdata.controller; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.dto.ThingAttrParam; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.device.analysisdata.dto.AnalysisDataReqDTO; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.MonitoringDataReqDTO; +import com.thing.device.analysisdata.service.AnalysisDataService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.text.DecimalFormat; +import java.util.*; + +@RestController +@RequestMapping("analysis") +@Tag(name = "数据分析API管理模块") +@RequiredArgsConstructor +public class AnalysisDataController { + + private final AnalysisDataService analysisDataService; + + + @PostMapping("getDeviceData") + @Operation(summary="监控分析-获取设备数据") + public Result> getDeviceDataMap(@Validated @RequestBody MonitoringDataReqDTO monitoringDataReqDTO) { + Map analysisDataRespMap = analysisDataService.findDeviceDataMap(monitoringDataReqDTO); + return new Result>().ok(analysisDataRespMap); + } + + @PostMapping("deviceDataExport") + @Operation(summary="导出实时数据列表-V2版本") + public void deviceDataExport(@Validated @RequestBody MonitoringDataReqDTO monitoringDataReqDTO, HttpServletResponse response) { + analysisDataService.deviceDataExport(monitoringDataReqDTO, response); + } + + @PostMapping("device/dict/latest/value") + @Operation(summary="单设备属性最新值") + public Result> getDeviceLatestValue(@RequestBody ThingAttrParam request) { + List lastTimeSeries = analysisDataService.getDeviceLatestValue(request); + return new Result>().ok(lastTimeSeries); + } + + @PostMapping("device/dict/history/values") + @Operation(summary="单设备属性历史值") + public Result> getDeviceHistoryValues(@RequestParam Long thingSourceId) { + List result = analysisDataService.getDeviceHistoryValues(thingSourceId); + return new Result>().ok(result); + } + + @GetMapping ("device/history/values") + @Operation(summary="单设备单属性历史值查询") + public Result> getHistoryValues(@RequestParam Long id) { + List result = analysisDataService.getHistoryValues(id); + return new Result>().ok(result); + } + + @PostMapping("getDeviceDataNew") + @Operation(summary = "监控分析-V2版本(小程序用-无需rootId)包含最大,最小,平均值") + public Result> getDeviceDataMapNew(@Validated @RequestBody MonitoringDataReqDTO monitoringDataReqDTO) { + Map analysisDataRespMap = analysisDataService.getDeviceDataMapNew(monitoringDataReqDTO); + if (analysisDataRespMap!=null && analysisDataRespMap.get("result")!=null){ + analysisDataRespMap.put("max",null); + analysisDataRespMap.put("min",null); + analysisDataRespMap.put("average",null); + + List> list = MapUtil.get(analysisDataRespMap, "result", List.class); + if (CollectionUtil.isNotEmpty(list)){ + DecimalFormat decimalFormat = new DecimalFormat("#.##"); + // 计算最大值 + OptionalDouble maxVal = list.stream() + .filter(m -> m.get("val") != null) // 过滤掉 "val" 为空的元素 + .mapToDouble(m -> { + if(m.get("val") instanceof Number){ + return ((Number)m.get("val")).doubleValue(); + }else{ + return Double.parseDouble(m.get("val").toString()); + } + }).max(); + String formattedMax = decimalFormat.format(maxVal.getAsDouble()); + analysisDataRespMap.put("max",formattedMax); + // 计算最小值 + OptionalDouble minVal = list.stream() + .filter(m -> m.get("val") != null) // 过滤掉 "val" 为空的元素 + .mapToDouble(m -> { + if(m.get("val") instanceof Number){ + return ((Number)m.get("val")).doubleValue(); + }else{ + return Double.parseDouble(m.get("val").toString()); + } + }).min(); + String formattedMin = decimalFormat.format(minVal.getAsDouble()); + analysisDataRespMap.put("min",formattedMin); + // 计算平均值 + Double averageVal = list.stream() + .filter(m -> m.get("val") != null) // 过滤掉 "val" 为空的元素 + .mapToDouble(m -> { + if(m.get("val") instanceof Number){ + return ((Number)m.get("val")).doubleValue(); + }else{ + return Double.parseDouble(m.get("val").toString()); + } + }).average().orElse(0D); + + String formattedAverage = decimalFormat.format(averageVal); + analysisDataRespMap.put("average",formattedAverage); + } + } + return new Result>().ok(analysisDataRespMap); + } + + + @PostMapping("appletRealTimeDataMap") + @Operation(summary = "监控分析-小程序-能源品种") + public Result>> getAppletRealTimeDataMap(@Validated @RequestBody AnalysisDataReqDTO analysisDataReqDTO) { + Map> statistics = new HashMap<>(); + Map>> analysisDataRespMap = analysisDataService.getAppletRealTimeDataMap(analysisDataReqDTO); + for (Map.Entry>> stringMapEntry : analysisDataRespMap.entrySet()) { + Map> value = stringMapEntry.getValue(); + for (Map.Entry> stringListEntry : value.entrySet()) { + Map mapValue = new HashMap<>(); + mapValue.put(stringListEntry.getKey(),stringListEntry.getValue()); + List list = stringListEntry.getValue(); + if (CollectionUtil.isNotEmpty(list)){ + // 计算最大值 + Optional maxVal = list.stream() + .map(AnalysisDataRespDTO::getVal) + .filter(val -> val != null && !val.isEmpty()) + .map(Double::parseDouble) + .max(Double::compareTo); + // 如果没有数据,就默认为 0 + Double maxValStr = maxVal.orElse(0D); + mapValue.put("max",maxValStr); + // 计算最小值 + Optional minVal = list.stream() + .map(AnalysisDataRespDTO::getVal) + .filter(val -> val != null && !val.isEmpty()) + .map(Double::parseDouble) + .min(Double::compareTo); + // 如果没有数据,就默认为 0 + Double minValStr = minVal.orElse(0D); + mapValue.put("min",minValStr); + // 计算平均值 + + OptionalDouble averageVal = list.stream() + .mapToDouble(dto -> { + if (dto.getVal() != null) { + return Double.parseDouble(dto.getVal()); + } else { + return 0.0; + } + }).average(); + // 如果没有数据,就默认为 0 + double averageValDouble = averageVal.orElse(0d); + DecimalFormat decimalFormat = new DecimalFormat("#.##"); + String formattedAverage = decimalFormat.format(averageValDouble); + mapValue.put("average",formattedAverage); + // 计算总和 + double sumVal = list.stream() + .filter(dto -> dto.getVal() != null) + .mapToDouble(dto -> Double.parseDouble(dto.getVal())) + .sum(); + mapValue.put("sum",sumVal); + } + statistics.put(stringMapEntry.getKey(),mapValue); + } + } + return new Result>>().ok(statistics); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataPageReqDTO.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataPageReqDTO.java new file mode 100644 index 0000000..86fd643 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataPageReqDTO.java @@ -0,0 +1,30 @@ +package com.thing.device.analysisdata.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import javax.validation.constraints.NotNull; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 16:31 2022/5/5 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class AnalysisDataPageReqDTO extends AnalysisDataReqDTO { + + @NotNull(message = "当前页码不允许为空") + @Schema(description = "起始页") + private Integer page; + + @Schema(description = "分页大小") + @NotNull(message = "分页条数不允许为空") + private Integer limit; + + @Schema(description = "排序") + private Boolean order; +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataReqDTO.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataReqDTO.java new file mode 100644 index 0000000..e23fa54 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataReqDTO.java @@ -0,0 +1,88 @@ +package com.thing.device.analysisdata.dto; + +import com.thing.device.source.dto.IotThingSourcePidDTO; +import com.thing.device.source.dto.IotThingSourceRelationDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 14:05 2022/5/5 + */ +@Data +public class AnalysisDataReqDTO { + + @Schema(description = "开始时间") + private String beginTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "时间范围类型") + private String timeHorizon; + + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + @NotBlank(message = "配置类型不允许为空") + private String configType; + + @Schema(description = "物关系列表") + private List thingRelationList; + + @Schema(description = "属性列表集合") + private List thingAttrCodeList; + + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + + @Schema(description = "搜索时间范围" + + "1=30分钟 " + + "2=1小时 " + + "3=5小时 " + + "4=12小时 " + + "5=1天 " + + "6=7天 " + + "7=30天 " + + "8=3月 " + + "9=6月 " + + "10=1年 " + + "高级查询时 ,传入 天,时,分 英文逗号分割形式,天,时未填也要传0 例: 0,5,40(表示0天5小时40分钟)") + private String searchTimeRange; + + + @Schema(description = "聚合时间范围" + + "1=30分钟 " + + "2=1小时 " + + "3=5小时 " + + "4=12小时 " + + "5=1天 " + + "6=7天 " + + "7=30天 " + + "8=3月 " + + "9=6月 " + + "10=1年 " + + "高级查询时 ,传入 天,时,分 英文逗号分割形式,天,时未填也要传0 例: 0,5,40(表示0天5小时40分钟)") + private String aggregationTimeRange; + + + @Schema(description = "聚合函数类型:" + + "1=最大值 " + + "2=最小值 " + + "3=平均值 " + + "4=求和 " + + "5=最新值(无)") + private String polymerizationType; + + + + @Schema(description = "最大数据量") + private Integer maxDataCount; + private List iotThingSourceList = new ArrayList<>(); + + private List childList = new ArrayList<>(); + +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataRespDTO.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataRespDTO.java new file mode 100644 index 0000000..aa204f0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/AnalysisDataRespDTO.java @@ -0,0 +1,36 @@ +package com.thing.device.analysisdata.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class AnalysisDataRespDTO { + + @Schema(description = "物名称") + private String thingName; + @Schema(description = "属性key") + @JsonIgnore + private String attrKey; + @Schema(description = "物属性名称") + private String attrName; + @Schema(description = "物属性单位") + private String thingAttrUnit; + @Schema(description = "物属性颜色") + private String icon; + @Schema(description = "ts") + private Long ts; + @Schema(description = "值") + private String val; + @JsonIgnore + @Schema(description = "根主键") + private Long rootId; + @Schema(description = "物id") + private Long thingId; +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringDataReqDTO.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringDataReqDTO.java new file mode 100644 index 0000000..2bcae64 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringDataReqDTO.java @@ -0,0 +1,83 @@ +package com.thing.device.analysisdata.dto; + +import com.thing.device.source.dto.IotThingSourceDTO; +import com.thing.device.source.dto.IotThingSourcePidDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Data +public class MonitoringDataReqDTO implements Serializable { + + @Serial + private static final long serialVersionUID = -8868412528182030308L; + + @Schema(description = "开始时间") + private String beginTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "时间类型 " + + "lastest=最新(暂时不用) " + + "nearest=最近 " + + "interval=区间 " + + "range=时间段(当日) ") + private String type; + + @Schema(description = "搜索时间范围-例如:{\"day\": \"0\",\"hour\":\"0\",\"minute\":\"30\"}") + private String searchTimeRange; + + @Schema(description = "聚合函数类型:" + + "max=最大值 " + + "min=最小值 " + + "avg=平均值 " + + "sum=求和 " + + "count=计数 " + + "blank=默认值(无)") + private String polymerizationType; + + @Schema(description = "聚合时间范围-例如:{\"day\": \"0\",\"hour\":\"0\",\"minute\":\"30\"}") + private String aggregationTimeRange; + + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + @NotBlank(message = "配置类型不允许为空") + private String configType; + + @Schema(description = "物关系列表") + private List thingRelationList; + + @Schema(description = "属性列表集合") + private List thingAttrCodeList; + + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + + @Schema(description = "最大数据量") + private Integer maxDataCount; + + @Schema(description = "排序字段:desc/asc") + private String sort; + + @Schema(description = "分页信息:第几页;若为空,则不分页") + private Integer page; + + @Schema(description = "分页信息:一页多少记录;若为空,则不分页") + private Integer limit; + + @Schema(description = "是否分页:默认 0 不分页 1分页") + private Integer isPaging; + + @Schema(description = "后台处理字段:前端无需处理") + private List iotThingSourceList = new ArrayList<>(); + + @Schema(description = "后台处理字段:前端无需处理") + private List childList = new ArrayList<>(); + + +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringTimeDTO.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringTimeDTO.java new file mode 100644 index 0000000..b2f2cf6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/MonitoringTimeDTO.java @@ -0,0 +1,43 @@ +package com.thing.device.analysisdata.dto; + + + +import lombok.Data; +import io.swagger.v3.oas.annotations.media.Schema; + +/** + * @Author: ls + * @Description: + * @Date: Create in 14:05 2022/5/5 + */ +@Data +public class MonitoringTimeDTO { + + @Schema(description = "开始时间") + private String beginTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "年") + private Long year; + + @Schema(description = "月") + private Long month; + + @Schema(description = "周") + private Long week; + + @Schema(description = "天") + private Long day; + + @Schema(description = "时") + private Long hour; + + @Schema(description = "分钟") + private Long minute; + + @Schema(description = "秒") + private Long seconds; + +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/dto/ThingEnergyDictData.java b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/ThingEnergyDictData.java new file mode 100644 index 0000000..06fc1f4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/dto/ThingEnergyDictData.java @@ -0,0 +1,95 @@ +package com.thing.device.analysisdata.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.HashMap; +import java.util.Map; + +/** + * Author: SiYang + * Date: 2023/09/20 11:26 + * Description: 物的用量属性&对应数据 + */ +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class ThingEnergyDictData { + + /** + * 物id + */ + private Long thingId; + + /** + * 物名称 + */ + private String thingName; + + /** + * 物编码 + */ + private String thingCode; + + /** + * 能源品种id + */ + private Long energyVarietyId; + + /** + * 能源品种 + */ + private String energyVarietyName; + + /** + * 能源品种编码 + */ + private String energyVarietyCode; + + /** + * 属性编码 + */ + private String attrCode; + + /** + * 属性名称 + */ + private String attrName; + + /** + * 属性单位 + */ + private String attrUnit; + + /** + * 属性类型(用量区间):base, am, hh, dd, mm, yy + */ + private String attrType; + + /** + * 按照物的用量属性获取的数据项 + */ + private Map dataMap; + + public Map initDataMap(){ + dataMap = new HashMap<>(); + return dataMap; + } + + public ThingEnergyDictData copy() { + return new ThingEnergyDictData() + .setThingId(thingId) + .setThingName(thingName) + .setThingCode(thingCode) + .setEnergyVarietyId(energyVarietyId) + .setEnergyVarietyName(energyVarietyName) + .setEnergyVarietyCode(energyVarietyCode) + .setAttrCode(attrCode) + .setAttrName(attrName) + .setAttrUnit(attrUnit) + .setAttrType(attrType); + } +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/excel/AnalysisDataExcel.java b/modules/thing/src/main/java/com/thing/device/analysisdata/excel/AnalysisDataExcel.java new file mode 100644 index 0000000..913d23e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/excel/AnalysisDataExcel.java @@ -0,0 +1,34 @@ +package com.thing.device.analysisdata.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 13:27 2022/5/9 + */ +@Data +public class AnalysisDataExcel { + + @Excel(name = "设备名称", orderNum = "0") + private String thingName; + + @Excel(name = "时间(毫秒值)", orderNum = "1") + private String time; + + @Schema(description = "时间(毫秒值)") + private Long ts; + + @Excel(name = "属性名称", orderNum = "2") + private String attrName; + + @Excel(name = "属性值", orderNum = "3") + private String val; + + @Excel(name = "属性单位", orderNum = "4") + private String thingAttrUnit; +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/service/AnalysisDataService.java b/modules/thing/src/main/java/com/thing/device/analysisdata/service/AnalysisDataService.java new file mode 100644 index 0000000..a4a1867 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/service/AnalysisDataService.java @@ -0,0 +1,30 @@ +package com.thing.device.analysisdata.service; + + +import com.thing.common.data.dto.ThingAttrParam; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.device.analysisdata.dto.AnalysisDataReqDTO; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.MonitoringDataReqDTO; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +public interface AnalysisDataService { + + Map findDeviceDataMap(MonitoringDataReqDTO analysisDataReqDTO); + + void deviceDataExport(MonitoringDataReqDTO monitoringDataReqDTO, HttpServletResponse response); + + List getDeviceLatestValue(ThingAttrParam request); + + List getDeviceHistoryValues(Long thingSourceId); + + List getHistoryValues(Long id); + + Map getDeviceDataMapNew(MonitoringDataReqDTO monitoringDataReqDTO); + + Map>> getAppletRealTimeDataMap(AnalysisDataReqDTO analysisDataReqDTO); + +} diff --git a/modules/thing/src/main/java/com/thing/device/analysisdata/service/impl/AnalysisDataServiceImpl.java b/modules/thing/src/main/java/com/thing/device/analysisdata/service/impl/AnalysisDataServiceImpl.java new file mode 100644 index 0000000..f2913aa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/analysisdata/service/impl/AnalysisDataServiceImpl.java @@ -0,0 +1,1034 @@ +package com.thing.device.analysisdata.service.impl; + + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.cglib.CglibUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.api.FormulaInvokeService; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.dto.ThingAttrParam; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.device.analysisdata.dto.AnalysisDataReqDTO; +import com.thing.device.analysisdata.dto.AnalysisDataRespDTO; +import com.thing.device.analysisdata.dto.MonitoringDataReqDTO; +import com.thing.device.analysisdata.dto.MonitoringTimeDTO; +import com.thing.device.analysisdata.excel.AnalysisDataExcel; +import com.thing.device.analysisdata.service.AnalysisDataService; +import com.thing.device.deviceManagement.entity.IotDeviceManagementEntity; +import com.thing.device.deviceManagement.service.IotDeviceManagementService; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; +import com.thing.device.menu.mapper.IotThingMenuConfigMapper; +import com.thing.device.source.dto.IotThingSourceDTO; +import com.thing.device.source.dto.IotThingSourcePidDTO; +import com.thing.device.source.dto.IotThingSourceRelationDTO; +import com.thing.device.source.entity.IotThingSourceEntity; +import com.thing.device.source.mapper.IotThingSourceMapper; +import com.thing.device.source.service.IotThingSourceService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.thing.entity.dto.IotThingViewSourceDTO; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.util.BeanUtil; +import com.thing.util.ToolUtil; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.jetbrains.annotations.Nullable; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.thing.common.core.utils.DateTimeUtils.DATE_TIME_PATTERN_STR; + +@Slf4j +@Service +@RequiredArgsConstructor +public class AnalysisDataServiceImpl implements AnalysisDataService { + + private final ThingManageContextService contextService; + private final IotDeviceManagementService deviceManagementService; + private final IotThingSourceService iotThingSourceService; + private final TsKvService tskvService; + + @Resource + private IotThingSourceMapper iotThingSourceDao; + + @Resource + private IotThingMenuConfigMapper iotThingMenuConfigDao; + + @Resource + private ThingManageContextService thingManageContextService; + + @Resource + private FormulaInvokeService formulaInvokeService; + + + @Override + public Map findDeviceDataMap(MonitoringDataReqDTO monitoringDataReqDTO) { + Map resMap = new HashMap<>(); + //获取数据源信息 + List iotThingSourceList = iotThingSourceService.findAllByFromIdAndThingIdAndConfigType( + monitoringDataReqDTO.getThingRelationList().stream().map(IotThingSourceDTO::getThingId).toList(), + null, + monitoringDataReqDTO.getConfigType(), + monitoringDataReqDTO.getThingRelationList().stream().map(IotThingSourceDTO::getRootId).toList() + ); + if (CollectionUtil.isEmpty(iotThingSourceList)) { + throw new SysException("请先到配置中心配置当前物相关信息"); + } + List thingAttrCodeList = monitoringDataReqDTO.getThingAttrCodeList(); + String thingAttrCodeType = monitoringDataReqDTO.getThingAttrCodeType(); + + if(StringUtils.isNotBlank(thingAttrCodeType)){ + thingAttrCodeList = thingAttrCodeList.stream().map(thingAttrCode -> AttributeTypeEnum.matchEndAdapter(thingAttrCode,thingAttrCodeType)).toList(); + } + List finalThingAttrCodeList = thingAttrCodeList; + iotThingSourceList = iotThingSourceList.stream() + .filter(dto -> finalThingAttrCodeList.stream().anyMatch(thingAttrCode -> StringUtils.equals(dto.getThingAttrCode(), thingAttrCode))) + .toList(); + //构建相关的物和属性 + Map thingViewDTOMap = buildIotThingViewDTOMap(iotThingSourceList); + //获取相关物和属性信息 + resMap.put("info", thingViewDTOMap); + //聚合函数:默认函数为blank + String func = ObjectUtil.isNull(monitoringDataReqDTO.getPolymerizationType()) ? MonitorPolymerizationType.BLANK.getName() : monitoringDataReqDTO.getPolymerizationType(); + //聚合时间 + String aggregationTimeRange = monitoringDataReqDTO.getAggregationTimeRange(); + Integer interval = null; + String dataType = null; + if (StringUtils.isNotBlank(aggregationTimeRange)) { + MonitoringTimeDTO aggTimeDTO = BeanUtil.jsonConvertBean(aggregationTimeRange, MonitoringTimeDTO.class); + Map dataTypeMap = getDataType(aggTimeDTO); + for (Map.Entry stringLongEntry : dataTypeMap.entrySet()) { + dataType = stringLongEntry.getKey(); + interval = stringLongEntry.getValue().intValue(); + } + } + //是否分页 + Integer isPaging = monitoringDataReqDTO.getIsPaging(); + Integer maxDataCount = monitoringDataReqDTO.getMaxDataCount(); + Integer page = Objects.isNull(monitoringDataReqDTO.getPage()) ? 0 : monitoringDataReqDTO.getPage(); + Integer limit = Objects.isNull(monitoringDataReqDTO.getLimit()) ? 10 : monitoringDataReqDTO.getLimit(); + //若分页,计算分页信息 开始页码和页记录数 + if (ObjectUtil.equals(isPaging,1) && !Objects.isNull(maxDataCount) && !Objects.equals(maxDataCount,0)){ + limit = maxDataCount; + } + //组装查询属性信息参数 + Map> queryParamMap = iotThingSourceList.stream().collect(Collectors.groupingBy( + IotThingSourceDTO::getThingCode, + Collectors.mapping(s -> AttributeTypeEnum.matchEndAdapter(s.getThingAttrCode(), s.getThingAttrCodeType()), Collectors.toCollection(ArrayList::new)))); + + //排序信息: //默认排序正序 + Boolean isAsc = ObjectUtil.equals(monitoringDataReqDTO.getSort(),Constant.DESC) ; + //最新数据查询 + if (StringUtils.equals(monitoringDataReqDTO.getType(), MonitorTimeType.LASTEST.getName())) { + //分页 + if(ObjectUtil.equals(isPaging,1)){ + PageData pageData = tskvService.findPageLatestByMultiMap(queryParamMap, isAsc, page, limit); + Map pageMap = Maps.newHashMap(); + pageMap.put("page", page); + pageMap.put("limit", limit); + pageMap.put("total", pageData.getTotal()); + //分页信息 + resMap.put("page", pageMap); + }else{ + List tsKvDTOList = tskvService.findLatestByMultiMap(queryParamMap, isAsc); + if (ObjectUtil.isNotNull(maxDataCount) && Objects.equals(maxDataCount,0) ) { + tsKvDTOList = tsKvDTOList.stream().limit(maxDataCount).collect(Collectors.toList()); + } + resMap.put("result", tsKvDTOList); + } + }else{ + //历史数据 + List timeList = generateTime(monitoringDataReqDTO); + //分页 + if(ObjectUtil.equals(isPaging,1)){ + PageData resList ; + if (StringUtils.isBlank(func) || StringUtils.equals(func, ApiFuncEnum.BLANK.getValue()) || ObjectUtil.equals(interval, 0L)) { + resList = tskvService.findPageTsKvByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),isAsc, page, limit); + } else { + resList = tskvService.findPageTsKvIntervalAggByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),AggType.match(func),interval,TimeType.matchTimeType(dataType),isAsc, page, limit); + } + Map pageMap = Maps.newHashMap(); + pageMap.put("page", page); + pageMap.put("limit", limit); + pageMap.put("total", resList.getTotal()); + //分页信息 + resMap.put("page", pageMap); + resMap.put("result", resList.getList()); + }else{ + List dataList ; + if (StringUtils.isBlank(func) || StringUtils.equals(func, ApiFuncEnum.BLANK.getValue()) || ObjectUtil.equals(interval, 0L)) { + dataList = tskvService.findTsKvByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),isAsc); + }else{ + dataList = tskvService.findTsKvIntervalAggByMultiMap(queryParamMap,timeList.get(0),timeList.get(1) + , AggType.match(func), interval,TimeType.matchTimeType(dataType),isAsc); + } + if (ObjectUtil.isNotNull(maxDataCount) && Objects.equals(maxDataCount,0) ) { + dataList = dataList.stream().limit(maxDataCount).collect(Collectors.toList()); + } + resMap.put("result", dataList); + } + } + + return resMap; + } + + + //调整 + private Map buildIotThingViewDTOMap(List iotThingSourceList) { + if(CollectionUtil.isEmpty(iotThingSourceList)){ + return Maps.newHashMapWithExpectedSize(0); + } + List thingIds = iotThingSourceList.stream().map(IotThingSourceDTO::getThingId).toList(); + //物实体列表 + Optional> optionalIotThingViewDTOS = contextService.findViewEntityAllByIds(thingIds); + if(optionalIotThingViewDTOS.isEmpty()){ + return Maps.newHashMapWithExpectedSize(0); + } + //物属性关系列表 + List dictCodes = iotThingSourceList.stream().map(IotThingSourceDTO::getThingAttrCode).toList(); + //这里的功能每次只有一个属性,所以这里没什么问题 + Optional> relationEntities = contextService.findDictRelationAllByEntityIdsAndCodes(thingIds, dictCodes); + + return relationEntities.map(iotThingDictRelationDTOS -> optionalIotThingViewDTOS.get().stream().collect(Collectors.toMap(IotThingViewDTO::getEntityCode, + iotThingViewDTO -> { + IotThingViewSourceDTO viewSourceDTO = ConvertUtils.sourceToTarget(iotThingViewDTO, IotThingViewSourceDTO.class); + // 获取与当前 IotThingViewDTO 关联的属性列表 + List relationDTOList = iotThingDictRelationDTOS.stream().filter(relation -> Objects.equals(relation.getEntityId(), viewSourceDTO.getEntityId())).toList(); + // 对 relationDTOList 进行处理,例如设置默认值等 + relationDTOList.forEach(relationDTO -> { + if (relationDTO.getGatherFrequency() == null) { + relationDTO.setGatherFrequency(900L); + } + iotThingSourceList.stream().filter(thingSourceDTO -> + Objects.equals(thingSourceDTO.getThingAttrCode(), relationDTO.getCode()) + && Objects.equals(thingSourceDTO.getThingId(), relationDTO.getEntityId()) + ).findFirst().ifPresent(thingSourceDTO -> relationDTO.setThingAttrBoundary(thingSourceDTO.getThingAttrBoundary())); + }); + // 将属性列表转换为 Map,并设置给当前的 IotThingViewDTO + Map dictRelationDTOMap = relationDTOList.stream().collect(Collectors.toMap(IotThingDictRelationParamDTO::getCode, Function.identity(), (k1, k2) -> k2)); + viewSourceDTO.setAttrs(dictRelationDTOMap); + relationDTOList.forEach(relationDTO ->{ + Optional first = iotThingSourceList.stream().filter(iotThingSourceDTO -> + StringUtils.equals(relationDTO.getCode(), iotThingSourceDTO.getThingAttrCode())).findFirst(); + if(first.isPresent()){ + IotThingSourceDTO iotThingSourceDTO = first.get(); + relationDTO.setThingAttrUnit(iotThingSourceDTO.getThingAttrUnit()); + }}); + viewSourceDTO.setDictList(relationDTOList); + return viewSourceDTO; + }, (existingValue, newValue) -> existingValue + ))).orElseGet(() -> Maps.newHashMapWithExpectedSize(0)); + } + + + + /** + * 根据时间类型生成时间列表 + */ + private List generateTime(MonitoringDataReqDTO analysisDataReqDTO) { + List timeList = Lists.newArrayList(); + //获取时间类型 + String type = analysisDataReqDTO.getType(); + //获取时间的JSON值 + String searchTimeRange = analysisDataReqDTO.getSearchTimeRange(); + //解析时间参数 + MonitoringTimeDTO monitoringTimeDTO = JSON.parseObject(searchTimeRange, new TypeReference<>() { + }); + //获取当前时间,即结束时间 + LocalDateTime now = LocalDateTime.now(); + long startTime = 0; + long endTime = getCurrentTimestamp(now); + //最近时间段 + if (StringUtils.equals(type, MonitorTimeType.NEAREST.getName())) { + startTime = generateTimeFromNow(monitoringTimeDTO, now); + } else if (StringUtils.equals(type, MonitorTimeType.INTERVAL.getName())) { + //区间 + startTime = generateIntervalTime(monitoringTimeDTO, now, true); + endTime = generateIntervalTime(monitoringTimeDTO, now, false); + } else if (StringUtils.equals(type, MonitorTimeType.RANGE.getName())) { + //时间段 + startTime = DateTimeUtils.convertTimeToLong(analysisDataReqDTO.getBeginTime()); + endTime = DateTimeUtils.convertTimeToLong(analysisDataReqDTO.getEndTime()); + } + timeList.add(startTime); + timeList.add(endTime); + return timeList; + } + + + private Map getDataType(MonitoringTimeDTO timeDTO) { + Map resMap = Maps.newHashMapWithExpectedSize(1); + long year = ObjectUtil.isNull(timeDTO.getYear()) ? 0L : timeDTO.getYear(); + long month = ObjectUtil.isNull(timeDTO.getMonth()) ? 0L : timeDTO.getMonth(); + long week = ObjectUtil.isNull(timeDTO.getWeek()) ? 0L : timeDTO.getWeek(); + long day = ObjectUtil.isNull(timeDTO.getDay()) ? 0L : timeDTO.getDay(); + long hour = ObjectUtil.isNull(timeDTO.getHour()) ? 0L : timeDTO.getHour(); + long minute = ObjectUtil.isNull(timeDTO.getMinute()) ? 0L : timeDTO.getMinute(); + if (year != 0) { + resMap.put("year", year); + } else if (month != 0) { + resMap.put("month", month); + } else if (week != 0) { + resMap.put("week", week); + } else if (day != 0) { + resMap.put("day", day); + } else if (hour != 0) { + resMap.put("hour", hour); + } else if (minute != 0) { + resMap.put("minute", minute); + } + return resMap; + } + + @Override + public void deviceDataExport(MonitoringDataReqDTO monitoringDataReqDTO, HttpServletResponse response) { + Map deviceDataMap = findDeviceDataMap(monitoringDataReqDTO); + List result = MapUtil.get(deviceDataMap, "result", List.class); + Map infoMap = MapUtil.get(deviceDataMap, "info", Map.class); + ReportTypeEnum reportType = ReportTypeEnum.getReportType(monitoringDataReqDTO.getConfigType()); + String title = reportType.getDesc(); + if (CollectionUtil.isEmpty(result)) { + ExcelUtils.exportExcel(Lists.newArrayList(), title, null, AnalysisDataExcel.class, title.concat(".xls"), response); + } + // 创建 DecimalFormat 对象,并设置小数点后保留两位 + DecimalFormat decimalFormat = new DecimalFormat("#0.00"); + List analysisDataList = result.stream().map(s -> { + AnalysisDataExcel dataExcel = new AnalysisDataExcel(); + String thing_code = s.getThingCode(); + String attr_key = s.getAttrKey(); + Long ts = s.getTs(); + String val = s.getVal(); + for (Map.Entry entry : infoMap.entrySet()) { + IotThingViewSourceDTO entryValue = entry.getValue(); + Map attrs = entryValue.getAttrs(); + IotThingDictRelationParamDTO iotThingDictRelationDTO = attrs.get(attr_key); + if (!Objects.isNull(iotThingDictRelationDTO)) { + String unit = iotThingDictRelationDTO.getUnit(); + dataExcel.setThingAttrUnit(unit); + } + } + dataExcel.setThingName(thing_code); + dataExcel.setAttrName(attr_key); + dataExcel.setTime(LocalDateTimeUtil.format(LocalDateTimeUtil.of(ts), DatePattern.NORM_DATETIME_FORMATTER)); + // 格式化 val,并获取保留两位小数后的字符串 + String formattedVal = decimalFormat.format(Double.parseDouble(val)); + dataExcel.setVal(formattedVal); + return dataExcel; + }).collect(Collectors.toList()); + if (null != monitoringDataReqDTO.getMaxDataCount()) { + analysisDataList = analysisDataList.subList(0, Math.min(monitoringDataReqDTO.getMaxDataCount(), analysisDataList.size())); + } + ExcelUtils.exportExcel(analysisDataList, title, null, AnalysisDataExcel.class, title.concat(".xls"), response); + + } + + @Override + public List getDeviceLatestValue(ThingAttrParam request) { + List lastTimeSeries = tskvService.findLatestByCodeAndAttrs(request.getThingCode(), request.getAttrList(), false); + // val字段解析: 部分数据存储的是json格式,需要略微转换 + lastTimeSeries.forEach(this::jsonValConvert); + return lastTimeSeries; + } + + @Override + public List getDeviceHistoryValues(Long thingSourceId) { + IotThingSourceDTO thingSource = iotThingSourceService.findById(thingSourceId); + TimeHorizonEnum timeHorizon = TimeHorizonEnum.getByCode(thingSource.getTimeHorizonType()); + Pair timestampRange = timeHorizon.getTimestampRange(); + return tskvService.findTsKvByCodeAndAttr(thingSource.getThingCode(), thingSource.getThingAttrCode(), timestampRange.getLeft(), timestampRange.getRight(), true); + } + + private void jsonValConvert(TsKvDTO cacheTsKv) { + if (!StringUtils.startsWith(cacheTsKv.getVal(), "{")) { + return; + } + JSONObject jsonVal = JSONObject.parseObject(cacheTsKv.getVal()); + for (Granularity granularity : Granularity.values()) { + String valObjStr = jsonVal.getString(granularity.getSuffix()); + if (StringUtils.isBlank(valObjStr)) { + continue; + } + List singleValueList = JSONObject.parseObject(valObjStr).values().stream().map(Object::toString).toList(); + if (!CollectionUtils.isEmpty(singleValueList)) { + cacheTsKv.setVal(singleValueList.get(0)); + return; + } + } + } + + @Override + public List getHistoryValues(Long id) { + //根据sourceid 获取物,属性编码,查询时间得详细信息 + IotDeviceManagementEntity management = deviceManagementService.getById(id); + String thingCode = management.getThingCode(); + String attrCode = management.getThingAttrCode(); + long endTime = System.currentTimeMillis(); + String timeStr = management.getTimeInfo(); + // 解析时间字符串为天、小时和分钟 + String[] timeParts = timeStr.split(","); + int days = Integer.parseInt(timeParts[0]); + int hours = Integer.parseInt(timeParts[1]); + int minutes = Integer.parseInt(timeParts[2]); + // 计算时间差的毫秒数 + long timeDifferenceMillis = ((long) days * 24 * 60 * 60 + (long) hours * 60 * 60 + minutes * 60L) * 1000; + Long startTime = endTime - timeDifferenceMillis; + return tskvService.findTsKvByCodesAndAttrs(List.of(thingCode),List.of(attrCode),startTime,endTime,true); + } + + @Override + public Map getDeviceDataMapNew(MonitoringDataReqDTO monitoringDataReqDTO) { + //取类型的root配置 + List configTypes = new ArrayList<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + configTypes.add(monitoringDataReqDTO.getConfigType()); + List configList = iotThingMenuConfigDao.selectListExt( + QueryWrapper.create() + .in(IotThingMenuConfigEntity::getConfigType, configTypes) + .eq(IotThingMenuConfigEntity::getTenantCode, tenantCode)); + if (CollectionUtil.isEmpty(configList)){ + return null; + } + IotThingMenuConfigEntity entity = configList.get(0); + List menuConfigs = new ArrayList<>(); + if (org.apache.commons.lang3.StringUtils.isNotBlank(entity.getConfigType())){ + List newMenuConfigs = JSON.parseArray(entity.getMenuConfig(), IotThingMenuConfigDTO.MenuConfig.class); + if (CollectionUtil.isNotEmpty(newMenuConfigs)){ + for (IotThingMenuConfigDTO.MenuConfig menuConfig:newMenuConfigs){ +// if (menuConfig.getIsSelected()){ + menuConfigs.add(menuConfig); +// } + } + } + } + if (CollectionUtil.isEmpty(menuConfigs)){ + return null; + } + menuConfigs = menuConfigs.stream().sorted(Comparator.comparing(IotThingMenuConfigDTO.MenuConfig::getSort)).collect(Collectors.toList()); + //获取物对应的rootId + for (IotThingSourceDTO sourceRelationDTO:monitoringDataReqDTO.getThingRelationList()){ + List rootIds = iotThingSourceDao.getRootIdsByThingIdAndConfigTypeAndTenantCode(sourceRelationDTO.getThingId(),monitoringDataReqDTO.getConfigType(),tenantCode); + for (IotThingMenuConfigDTO.MenuConfig menuConfig:menuConfigs){ + if (rootIds.contains(Long.parseLong(menuConfig.getId()))){ + sourceRelationDTO.setRootId(Long.parseLong(menuConfig.getId())); + break; + } + } + } + + + Map resMap = new HashMap<>(); + //默认排序正序 + // String sort = ObjectUtil.isNull(monitoringDataReqDTO.getSort()) ? Constant.ASC : monitoringDataReqDTO.getSort(); + //获取数据源信息 + List iotThingSourceList = getIotThingSourceDTOS(monitoringDataReqDTO); + if (iotThingSourceList == null) return null; + //子数据源 + List pidList = iotThingSourceList.stream() + .filter(item -> StringUtils.equals(item.getDataTreatingMark(), DataTreatingMarkEnum.DISPOSE.getValue())) + .map(IotThingSourcePidDTO::getId).distinct().collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(pidList)) { + Map param = Maps.newHashMapWithExpectedSize(1); + param.put("pidList", pidList); + List childList = iotThingSourceService.getChildList(param); + if (CollectionUtil.isNotEmpty(childList)) { + monitoringDataReqDTO.getChildList().addAll(childList); + } + } + monitoringDataReqDTO.getIotThingSourceList().addAll(iotThingSourceList); + //获取相关物和属性信息 + Map thingViewDTOMap = getStringIotThingViewDTOMap(iotThingSourceList); + resMap.put("info", thingViewDTOMap); + //聚合函数:默认函数为blank + String func = ObjectUtil.isNull(monitoringDataReqDTO.getPolymerizationType()) ? MonitorPolymerizationType.BLANK.getName() : monitoringDataReqDTO.getPolymerizationType(); + //聚合时间 + String aggregationTimeRange = monitoringDataReqDTO.getAggregationTimeRange(); + Integer interval = null; + String dataType = null; + if (StringUtils.isNotBlank(aggregationTimeRange)) { + MonitoringTimeDTO aggTimeDTO = BeanUtil.jsonConvertBean(aggregationTimeRange, MonitoringTimeDTO.class); + Map dataTypeMap = getDataType(aggTimeDTO); + for (Map.Entry stringLongEntry : dataTypeMap.entrySet()) { + dataType = stringLongEntry.getKey(); + interval = stringLongEntry.getValue().intValue(); + } + } + //是否分页 + Integer isPaging = monitoringDataReqDTO.getIsPaging(); + Integer maxDataCount = monitoringDataReqDTO.getMaxDataCount(); + Integer page = Objects.isNull(monitoringDataReqDTO.getPage()) ? 0 : monitoringDataReqDTO.getPage(); + Integer limit = Objects.isNull(monitoringDataReqDTO.getLimit()) ? 10 : monitoringDataReqDTO.getLimit(); + //若分页,计算分页信息 开始页码和页记录数 + if (ObjectUtil.equals(isPaging,1) && !Objects.isNull(maxDataCount) && !Objects.equals(maxDataCount,0)){ + limit = maxDataCount; + } + //组装查询属性信息参数 + Map> queryParamMap = iotThingSourceList.stream().collect(Collectors.groupingBy( + IotThingSourceDTO::getThingCode, + Collectors.mapping(s -> AttributeTypeEnum.matchEndAdapter(s.getThingAttrCode(), s.getThingAttrCodeType()), Collectors.toCollection(ArrayList::new)))); + + //排序信息: //默认排序正序 + Boolean isAsc = ObjectUtil.equals(monitoringDataReqDTO.getSort(),Constant.DESC) ; + //最新数据查询 + if (StringUtils.equals(monitoringDataReqDTO.getType(), MonitorTimeType.LASTEST.getName())) { + //分页 + if(ObjectUtil.equals(isPaging,1)){ + PageData pageData = tskvService.findPageLatestByMultiMap(queryParamMap, isAsc, page, limit); + Map pageMap = Maps.newHashMap(); + pageMap.put("page", page); + pageMap.put("limit", limit); + pageMap.put("total", pageData.getTotal()); + //分页信息 + resMap.put("page", pageMap); + }else{ + List tsKvDTOList = tskvService.findLatestByMultiMap(queryParamMap, isAsc); + if (ObjectUtil.isNotNull(maxDataCount) && Objects.equals(maxDataCount,0) ) { + tsKvDTOList = tsKvDTOList.stream().limit(maxDataCount).collect(Collectors.toList()); + } + resMap.put("result", tsKvDTOList); + } + }else{ + //历史数据 + List timeList = generateTime(monitoringDataReqDTO); + //分页 + if(ObjectUtil.equals(isPaging,1)){ + PageData resList ; + if (StringUtils.isBlank(func) || StringUtils.equals(func, ApiFuncEnum.BLANK.getValue()) || ObjectUtil.equals(interval, 0L)) { + resList = tskvService.findPageTsKvByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),isAsc, page, limit); + } else { + resList = tskvService.findPageTsKvIntervalAggByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),AggType.match(func),interval,TimeType.matchTimeType(dataType),isAsc, page, limit); + } + Map pageMap = Maps.newHashMap(); + pageMap.put("page", page); + pageMap.put("limit", limit); + pageMap.put("total", resList.getTotal()); + //分页信息 + resMap.put("page", pageMap); + }else{ + List dataList ; + if (StringUtils.isBlank(func) || StringUtils.equals(func, ApiFuncEnum.BLANK.getValue()) || ObjectUtil.equals(interval, 0L)) { + dataList = tskvService.findTsKvByMultiMap(queryParamMap,timeList.get(0), timeList.get(1),isAsc); + }else{ + dataList = tskvService.findTsKvIntervalAggByMultiMap(queryParamMap,timeList.get(0),timeList.get(1) + , AggType.match(func), interval,TimeType.matchTimeType(dataType),isAsc); + } + if (ObjectUtil.isNotNull(maxDataCount) && Objects.equals(maxDataCount,0) ) { + dataList = dataList.stream().limit(maxDataCount).collect(Collectors.toList()); + } + resMap.put("result", dataList); + } + } + return resMap; + } + + @Override + public Map>> getAppletRealTimeDataMap(AnalysisDataReqDTO analysisDataReqDTO) { + List dataRespDTOS = queryAppletList(analysisDataReqDTO); + if (null != analysisDataReqDTO.getMaxDataCount()) { + dataRespDTOS = dataRespDTOS.subList(0, Math.min(analysisDataReqDTO.getMaxDataCount(), dataRespDTOS.size())); + } + + return dataRespDTOS.isEmpty() ? null : dataRespDTOS.parallelStream().collect(Collectors.groupingBy(item -> String.valueOf(item.getThingId()), + Collectors.groupingBy(AnalysisDataRespDTO::getAttrKey, Collectors.mapping(item -> { + item.setThingName(null); + return item; + }, Collectors.toList())))); + } + + + /** + * 获取数据,聚合/直接获取 + * + * @param analysisDataReqDTO + * @return + */ + private List queryAppletList(AnalysisDataReqDTO analysisDataReqDTO) { + List respDTOS = new ArrayList<>(); + List thingRelationList = analysisDataReqDTO.getThingRelationList(); + List thingAttrCodeList = analysisDataReqDTO.getThingAttrCodeList(); + if (CollectionUtil.isEmpty(thingRelationList) || CollectionUtil.isEmpty(thingAttrCodeList)) { + return Lists.newArrayList(); + } + String thingAttrCodeType = analysisDataReqDTO.getThingAttrCodeType(); + List sourceDTOList = thingRelationList.stream().flatMap(s -> thingAttrCodeList.stream().map(a -> { + IotThingSourcePidDTO iotThingSourceDTO = new IotThingSourcePidDTO(); + iotThingSourceDTO.setThingCode(s.getThingCode()); + iotThingSourceDTO.setThingId(s.getThingId()); + iotThingSourceDTO.setThingTenantId(s.getThingId()); + iotThingSourceDTO.setThingName(s.getThingName()); + iotThingSourceDTO.setThingAttrCode(a.endsWith(thingAttrCodeType) ? a : a.concat(thingAttrCodeType)); + return iotThingSourceDTO; + })).collect(Collectors.toList()); + analysisDataReqDTO.setIotThingSourceList(sourceDTOList); + if (StringUtils.isEmpty(analysisDataReqDTO.getBeginTime())) { + //处理搜索时间 + if (StringUtils.isNotEmpty(analysisDataReqDTO.getSearchTimeRange())) { + if (analysisDataReqDTO.getSearchTimeRange().length() > Constant.LIST_LENGTH) { + analysisDataReqDTO.setBeginTime(DateTimeUtils.millisecondsFormatStrDate(System.currentTimeMillis() - ToolUtil.formartMilliSeconds(analysisDataReqDTO.getSearchTimeRange()))); + } else { + if (analysisDataReqDTO.getSearchTimeRange().equals("6")) { + analysisDataReqDTO.setBeginTime(DateTimeUtils.millisecondsFormatStrDate(DateTimeUtils.firstDayOfWeekMilli())); + } else { + analysisDataReqDTO.setBeginTime(DateTimeUtils.millisecondsFormatStrDate(System.currentTimeMillis() - Constant.EMUNTIMEMAP.get(analysisDataReqDTO.getSearchTimeRange()))); + } + } + if (StringUtils.isNotEmpty(analysisDataReqDTO.getEndTime())) { + long endTime = DateTimeUtils.dateToTimestamp(analysisDataReqDTO.getEndTime()); + if (endTime > System.currentTimeMillis()) { + endTime = System.currentTimeMillis(); + } + analysisDataReqDTO.setEndTime(DateTimeUtils.millisecondsFormatStrDate(endTime)); + } else { + analysisDataReqDTO.setEndTime(DateTimeUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss")); + } + } + } + if (StringUtils.isEmpty(analysisDataReqDTO.getEndTime())) { + analysisDataReqDTO.setBeginTime(DateTimeUtils.getDayBeginTime()); + analysisDataReqDTO.setEndTime(DateTimeUtils.millisecondsFormatStrDate(System.currentTimeMillis())); + } + if (StringUtils.isNotEmpty(analysisDataReqDTO.getPolymerizationType()) && StringUtils.isNotEmpty(analysisDataReqDTO.getAggregationTimeRange())) { + //获取分割的毫秒值 + if (StringUtils.isNotEmpty(analysisDataReqDTO.getAggregationTimeRange())) { + if (analysisDataReqDTO.getAggregationTimeRange().length() <= Constant.LIST_LENGTH) { + analysisDataReqDTO.setAggregationTimeRange(Constant.EMUNSTRTIMEMAP.get(analysisDataReqDTO.getAggregationTimeRange())); + } + } + Long milli = ToolUtil.formartMilliSeconds(analysisDataReqDTO.getAggregationTimeRange()); + //开始数据处理 初始化时间 + Long endTime = null; + try { + endTime = DateTimeUtils.dateToTimestamp(analysisDataReqDTO.getEndTime()); + if (endTime > System.currentTimeMillis()) { + endTime = System.currentTimeMillis(); + } + } catch (Exception e) { + endTime = System.currentTimeMillis(); + } + long startTime = endTime - milli; + + AnalysisDataReqDTO param = CglibUtil.copy(analysisDataReqDTO, AnalysisDataReqDTO.class); + //endTime=startTime-1; + param.setBeginTime(DateTimeUtils.millisecondsFormatStrDate(startTime)); + param.setEndTime(DateTimeUtils.millisecondsFormatStrDate(endTime)); + List realtimeDataList = getRealtimeDataList(param); + //返回结果为为空,即为聚合时间小于数据采集间隔,不进行聚合 + if (CollectionUtils.isEmpty(realtimeDataList)) { + realtimeDataList = getRealtimeDataList(analysisDataReqDTO); + //为空,不进行分组 + if (CollectionUtils.isEmpty(realtimeDataList)) { + return new ArrayList<>(); + } + return realtimeDataList.parallelStream().distinct().toList() + .parallelStream().sorted(Comparator.comparingLong(AnalysisDataRespDTO::getTs).reversed()).collect(Collectors.toList()); + } else { + JSONObject object = ToolUtil.handleByPolymerizationType(JSON.parseArray(JSONObject.toJSONString(realtimeDataList)), "val", analysisDataReqDTO.getPolymerizationType()); + object.put("ts", startTime); + respDTOS.add(JSONObject.parseObject(JSONObject.toJSONString(object), AnalysisDataRespDTO.class)); + + List params = new ArrayList<>(); + boolean flag = true; + int count = 1; + //循环处理,直至时间为空 + while (flag) { + param = CglibUtil.copy(analysisDataReqDTO, AnalysisDataReqDTO.class); + endTime = startTime; + startTime = endTime - milli; + count++; + param.setBeginTime(DateTimeUtils.millisecondsFormatStrDate(startTime)); + param.setEndTime(DateTimeUtils.millisecondsFormatStrDate(endTime)); + params.add(param); + if (startTime < DateTimeUtils.dateToTimestamp(analysisDataReqDTO.getBeginTime()) - milli) { + flag = false; + } + if (null != analysisDataReqDTO.getMaxDataCount() && analysisDataReqDTO.getMaxDataCount() != 0) { + if (count == analysisDataReqDTO.getMaxDataCount()) { + flag = false; + } + } + } + params.parallelStream().forEach(temp -> { + List info = getAppletRealtimeDataList(temp); + if (!info.isEmpty()) { + Map> tempMap = info.parallelStream() + .collect(Collectors.groupingBy(item -> item.getRootId() + "_" + item.getThingId() + "_" + item.getAttrKey())); + tempMap.keySet().parallelStream().forEach(key -> { + JSONObject tempObj = ToolUtil.handleByPolymerizationType(JSON.parseArray(JSONObject.toJSONString(tempMap.get(key))), "val", analysisDataReqDTO.getPolymerizationType()); + tempObj.put("ts", DateTimeUtils.dateToTimestamp(temp.getBeginTime())); + respDTOS.add(JSONObject.parseObject(JSONObject.toJSONString(tempObj), AnalysisDataRespDTO.class)); + }); + } + }); + } + } else { + return getAppletRealtimeDataList(analysisDataReqDTO).parallelStream().distinct().toList() + .parallelStream().sorted(Comparator.comparingLong(AnalysisDataRespDTO::getTs).reversed()).collect(Collectors.toList()); + } + return respDTOS.parallelStream().distinct().toList() + .parallelStream().sorted(Comparator.comparingLong(AnalysisDataRespDTO::getTs).reversed()).collect(Collectors.toList()); + } + + + /** + * 获取实时数据列表 + * + * @param analysisDataReqDTO 参数 + * @return + */ + public List getAppletRealtimeDataList(AnalysisDataReqDTO analysisDataReqDTO) { + if (CollectionUtils.isEmpty(analysisDataReqDTO.getThingRelationList()) || CollectionUtils.isEmpty(analysisDataReqDTO.getThingAttrCodeList())) { + return Lists.newArrayList(); + } + List iotThingSourceList = analysisDataReqDTO.getIotThingSourceList(); + List childList = analysisDataReqDTO.getChildList(); + //获取数据 + List cacheTsKvList = getCacheTsKvDTO(analysisDataReqDTO, iotThingSourceList, childList); + //数据组装 + return iotThingSourceList.parallelStream().flatMap(item -> { + String thingName = analysisDataReqDTO.getThingRelationList().parallelStream() + .filter(relation -> Objects.equals(relation.getThingId(), item.getThingId())).findFirst() + .map(IotThingSourceRelationDTO::getThingName).orElse(null); + List children = childList.parallelStream().filter(child -> Objects.equals(child.getPid(), item.getId())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(children)) { + return calculation(item, children, cacheTsKvList, thingName); + } + return cacheTsKvList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), item.getThingCode())) + .map(tsKv -> new AnalysisDataRespDTO(thingName, item.getThingAttrCode(), item.getThingAttrName(), item.getThingAttrUnit(), item.getThingIcon(), tsKv.getTs(), tsKv.getVal(), item.getRootId(), item.getThingId())); + }).collect(Collectors.toList()); + } + + public List getRealtimeDataList(AnalysisDataReqDTO analysisDataReqDTO) { + if (CollectionUtils.isEmpty(analysisDataReqDTO.getThingRelationList()) || CollectionUtils.isEmpty(analysisDataReqDTO.getThingAttrCodeList())) { + return Lists.newArrayList(); + } + List iotThingSourceList; + List childList; + if (analysisDataReqDTO.getIotThingSourceList().size() == Constant.COUNT_0) { + //父数据源 + Map params = Maps.newHashMapWithExpectedSize(1); + List thingIdList = analysisDataReqDTO.getThingRelationList().parallelStream().map(IotThingSourceRelationDTO::getThingId).distinct().collect(Collectors.toList()); + List rootIdList = analysisDataReqDTO.getThingRelationList().parallelStream().map(IotThingSourceRelationDTO::getRootId).distinct().collect(Collectors.toList()); + params.put("thingIdList", thingIdList); + params.put("rootIdList", rootIdList); + params.put("thingAttrCodeList", analysisDataReqDTO.getThingAttrCodeList()); + params.put("configType", analysisDataReqDTO.getConfigType()); + params.put("thingAttrCodeType", analysisDataReqDTO.getThingAttrCodeType()); + iotThingSourceList = iotThingSourceService.getParentList(params); + if (CollectionUtils.isEmpty(iotThingSourceList)) { + return Lists.newArrayList(); + } + //子数据源 + childList = new ArrayList<>(); + List pidList = iotThingSourceList.stream() + .filter(item -> StringUtils.equals(item.getDataTreatingMark(), DataTreatingMarkEnum.DISPOSE.getValue())) + .map(IotThingSourceDTO::getId).collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(pidList)) { + Map param = Maps.newHashMapWithExpectedSize(1); + param.put("pidList", pidList); + childList.addAll(iotThingSourceService.getChildList(param)); + } + } else { + iotThingSourceList = analysisDataReqDTO.getIotThingSourceList(); + childList = analysisDataReqDTO.getChildList(); + } + + //获取数据 + List cacheTsKvList = getCacheTsKvDTO(analysisDataReqDTO, iotThingSourceList, childList); + //数据组装 + return iotThingSourceList.parallelStream().flatMap(item -> { + String thingName = analysisDataReqDTO.getThingRelationList().parallelStream() + .filter(relation -> Objects.equals(relation.getThingId(), item.getThingId())).findFirst() + .map(IotThingSourceRelationDTO::getThingName).orElse(null); + List children = childList.parallelStream().filter(child -> Objects.equals(child.getPid(), item.getId())).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(children)) { + return calculation(item, children, cacheTsKvList, thingName); + } + return cacheTsKvList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), item.getThingCode())) + .map(tsKv -> new AnalysisDataRespDTO(thingName, item.getThingAttrCode(), item.getThingAttrName(), item.getThingAttrUnit(), item.getThingIcon(), tsKv.getTs(), tsKv.getVal(), item.getRootId(), item.getThingId())); + }).collect(Collectors.toList()); + } + + private Stream calculation(IotThingSourceDTO item, List children, List tsKvList, String thingName) { + //过滤数据 + Map> tsListMap = tsKvList.parallelStream() + .filter(tsKv -> children.stream().anyMatch(child -> +// Objects.equals(tsKv.get(), child.getThingId()) && + StringUtils.equals(tsKv.getAttrKey(), StringUtils.isBlank(child.getThingAttrCodeType()) ? child.getThingAttrCode() : child.getThingAttrCode().concat(child.getThingAttrCodeType())))) + .collect(Collectors.groupingBy(TsKvDTO::getTs)); + if (CollectionUtil.isEmpty(tsListMap)) { + return Stream.empty(); + } + List result = Lists.newArrayListWithExpectedSize(tsListMap.size()); + tsListMap.forEach((ts, list) -> { + //数据不全不进行计算,直接返回0 + if (children.size() != list.size()) { + log.warn("{}-{}-{}计算数据不全,结果置0", item.getThingCode(), item.getThingAttrCode(), ts); + result.add(new AnalysisDataRespDTO(thingName, item.getThingAttrCode(), item.getThingAttrName(), item.getThingAttrUnit(), item.getThingIcon(), ts, "0", item.getRootId(), item.getThingId())); + } else { + BigDecimal value = BigDecimal.ZERO; + try { + Map env = Maps.newHashMapWithExpectedSize(children.size()); + for (IotThingSourceDTO child : children) { + env.put(child.getThingSerial(), list.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), child.getThingCode()) + && StringUtils.equals(tsKv.getAttrKey(), StringUtils.isBlank(child.getThingAttrCodeType()) ? child.getThingAttrCode() : child.getThingAttrCode().concat(child.getThingAttrCodeType()))) + .findFirst().map(tsKv -> new BigDecimal(tsKv.getVal())).orElse(BigDecimal.ZERO)); + } + value = formulaInvokeService.invokeFormula(item.getDataRule(), env, 4); + } catch (Exception e) { + log.error("{}-{} 公式:{}, {}计算区间值失败: {}", item.getThingCode(), item.getThingAttrCode(), item.getDataRule(), ts, e.getMessage()); + } + result.add(new AnalysisDataRespDTO(thingName, item.getThingAttrCode(), item.getThingAttrName(), item.getThingAttrUnit(), item.getThingIcon(), ts, value.toPlainString(), item.getRootId(), item.getThingId())); + } + }); + return result.stream(); + } + + /** + * 缓存接口调用获取数据列表 + * + * @param analysisDataReqDTO 入参 + * @param iotThingSourceList 父数据源 + * @param childList 子数据源 + * @return list + */ + private List getCacheTsKvDTO(AnalysisDataReqDTO analysisDataReqDTO, List iotThingSourceList, List childList) { + List thingAttrList = iotThingSourceList.parallelStream() + .filter(item -> childList.stream().noneMatch(child -> Objects.equals(child.getPid(), item.getId()))) + .map(item -> new ThingAttrDTO(item.getThingTenantId(), item.getThingCode(), Lists.newArrayList(item.getThingAttrCode()))) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(childList)) { + thingAttrList.addAll(childList.parallelStream().map(item -> new ThingAttrDTO(item.getThingId(), item.getThingCode(), Lists.newArrayList(item.getThingAttrCode()))).toList()); + } + if (CollectionUtils.isEmpty(thingAttrList)) { + return Lists.newArrayList(); + } + //聚合函数:默认函数为blank + String func = ObjectUtil.isNull(analysisDataReqDTO.getPolymerizationType()) ? MonitorPolymerizationType.BLANK.getName() : analysisDataReqDTO.getPolymerizationType(); + //聚合时间 + String aggregationTimeRange = analysisDataReqDTO.getAggregationTimeRange(); + Integer interval = null; + String dataType = null; + if (StringUtils.isNotBlank(aggregationTimeRange)) { + MonitoringTimeDTO aggTimeDTO = BeanUtil.jsonConvertBean(aggregationTimeRange, MonitoringTimeDTO.class); + Map dataTypeMap = getDataType(aggTimeDTO); + for (Map.Entry stringLongEntry : dataTypeMap.entrySet()) { + dataType = stringLongEntry.getKey(); + interval = stringLongEntry.getValue().intValue(); + } + } + Map> thingAttrMap = thingAttrList.stream().collect(Collectors.toMap(ThingAttrDTO::getThingCode, ThingAttrDTO::getAttrList)); + return (StringUtils.isBlank(analysisDataReqDTO.getBeginTime()) || StringUtils.isBlank(analysisDataReqDTO.getEndTime())) + ? tskvService.findLatestByMultiMap(thingAttrMap,false) + : tskvService.findTsKvIntervalAggByMultiMap(thingAttrMap, DateTimeUtils.dateToTimestamp(analysisDataReqDTO.getBeginTime(), DATE_TIME_PATTERN_STR), + DateTimeUtils.dateToTimestamp(analysisDataReqDTO.getEndTime(), DATE_TIME_PATTERN_STR), AggType.match(func),interval, TimeType.matchTimeType(dataType),false); + } + + private Map getStringIotThingViewDTOMap(List iotThingSourceList) { + //物实体列表 + Optional> thingViewEntities = thingManageContextService.findViewEntityAllByIds(iotThingSourceList.stream().map(IotThingSourcePidDTO::getThingId).collect(Collectors.toList())); + //物属性关系列表 + List dictCodes = iotThingSourceList.stream().map(IotThingSourcePidDTO::getThingAttrCode).toList(); + //物属性关系列表 + Optional> dictRelationAllByEntityIdsAndCodes = thingManageContextService + .findDictRelationAllByEntityIdsAndCodes(iotThingSourceList.stream().map(IotThingSourcePidDTO::getThingId).collect(Collectors.toList()), dictCodes); + + return dictRelationAllByEntityIdsAndCodes.map(iotThingDictRelationDTOS -> thingViewEntities.get().stream().collect(Collectors.toMap(IotThingViewDTO::getEntityCode, + iotThingViewDTO -> { + IotThingViewSourceDTO viewSourceDTO = ConvertUtils.sourceToTarget(iotThingViewDTO, IotThingViewSourceDTO.class); + // 获取与当前 IotThingViewDTO 关联的属性列表 + List relationDTOList = iotThingDictRelationDTOS.stream().filter(relation -> Objects.equals(relation.getEntityId(), viewSourceDTO.getEntityId())).toList(); + // 对 relationDTOList 进行处理,例如设置默认值等 + relationDTOList.forEach(relationDTO -> { + if (relationDTO.getGatherFrequency() == null) { + relationDTO.setGatherFrequency(900L); + } + }); + // 将属性列表转换为 Map,并设置给当前的 IotThingViewDTO + Map dictRelationDTOMap = relationDTOList.stream().collect(Collectors.toMap(IotThingDictRelationParamDTO::getCode, Function.identity(), (k1, k2) -> k2)); + viewSourceDTO.setAttrs(dictRelationDTOMap); + return viewSourceDTO; + }, (existingValue, newValue) -> existingValue + ))).orElseGet(() -> Maps.newHashMapWithExpectedSize(0)); + } + + + @Nullable + private List getIotThingSourceDTOS(MonitoringDataReqDTO monitoringDataReqDTO) { + //所有待查寻thing and code + Map sourceParams = Maps.newHashMap(); + //物id集合,这里的thingid即fromid + List thingIdList = monitoringDataReqDTO.getThingRelationList().parallelStream().map(IotThingSourceDTO::getThingId).distinct().collect(Collectors.toList()); + if (CollectionUtil.isEmpty(thingIdList)) { + throw new SysException("物的id集合不能为空"); + } + //物关系id集合 + List rootIdList = monitoringDataReqDTO.getThingRelationList().parallelStream().map(IotThingSourceDTO::getRootId).distinct().collect(Collectors.toList()); + List iotThingSourceEntities = iotThingSourceDao.selectListExt(QueryWrapper.create() + .in(IotThingSourceEntity::getFromId, thingIdList) + .in(IotThingSourceEntity::getRootId, rootIdList) + .in(IotThingSourceEntity::getConfigType, monitoringDataReqDTO.getConfigType())); + + if (CollectionUtil.isEmpty(iotThingSourceEntities)) { + return null; + } + sourceParams.put("thingIdList", iotThingSourceEntities.stream().map(IotThingSourceEntity::getThingId).collect(Collectors.toList())); + sourceParams.put("fromIds", thingIdList); + sourceParams.put("rootIdList", rootIdList); + sourceParams.put("thingAttrCodeList", monitoringDataReqDTO.getThingAttrCodeList()); + sourceParams.put("configType", monitoringDataReqDTO.getConfigType()); + List iotThingSourceList = iotThingSourceService.getParentList(sourceParams); + if (CollectionUtils.isEmpty(iotThingSourceList)) { + return null; + } + return iotThingSourceList; + } + + /** + * 从当前时间往前推算开始时间 + */ + public Long generateTimeFromNow(MonitoringTimeDTO timeDTO, LocalDateTime currentDateTime) { + long year = ObjectUtil.isNull(timeDTO.getYear()) ? 0L : timeDTO.getYear(); + long month = ObjectUtil.isNull(timeDTO.getMonth()) ? 0L : timeDTO.getMonth(); + long week = ObjectUtil.isNull(timeDTO.getWeek()) ? 0L : timeDTO.getWeek(); + long day = ObjectUtil.isNull(timeDTO.getDay()) ? 0L : timeDTO.getDay(); + long hour = ObjectUtil.isNull(timeDTO.getHour()) ? 0L : timeDTO.getHour(); + long minute = ObjectUtil.isNull(timeDTO.getMinute()) ? 0L : timeDTO.getMinute(); + long seconds = ObjectUtil.isNull(timeDTO.getSeconds()) ? 0L : timeDTO.getSeconds(); + return getPastTimestampFromNow(currentDateTime, year, month, week, day, hour, minute, seconds); + } + + /** + * 当前时间为天 -- 即当天 + * 为周 -- 即当周 + * 为月 -- 即当月 + * 为年 -- 即当年 + */ + public Long generateIntervalTime(MonitoringTimeDTO timeDTO,LocalDateTime currentDateTime,Boolean isStart){ + long year = ObjectUtil.isNull(timeDTO.getYear()) ? 0L : timeDTO.getYear(); + long month = ObjectUtil.isNull(timeDTO.getMonth()) ? 0L : timeDTO.getMonth(); + long week = ObjectUtil.isNull(timeDTO.getWeek()) ? 0L : timeDTO.getWeek(); + long day = ObjectUtil.isNull(timeDTO.getDay()) ? 0L : timeDTO.getDay(); + long hour = ObjectUtil.isNull(timeDTO.getHour()) ? 0L : timeDTO.getHour(); + long minute = ObjectUtil.isNull(timeDTO.getMinute()) ? 0L : timeDTO.getMinute(); + //获取当年时间 + if(year != 0 ){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusYears(year-1); + return DateTimeUtils.getYearStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getYearEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + }else if(month != 0 ){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusMonths(month-1); + return DateTimeUtils.getMonthStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getMonthEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + }else if(week != 0){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusWeeks(week-1); + return DateTimeUtils.getWeekStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getWeekEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + }else if(day != 0){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusDays(day-1); + return DateTimeUtils.getDayStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getDayEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + }else if(hour!=0){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusHours(hour-1); + return DateTimeUtils.getHourStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getHourEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + }else if(minute!=0){ + if(isStart){ + LocalDateTime localDateTime = currentDateTime.minusMinutes(minute-1); + return DateTimeUtils.getMinuteStart(localDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + }else{ + return DateTimeUtils.getMinuteEnd(currentDateTime).atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + } + //最后一秒是默认当前时间的最后的时间戳 + return LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + + /** + * 计算过去的时间戳 + * + * @param years 要减去的年数 + * @param months 要减去的月数 + * @param weeks 要减去的周数 + * @param days 要减去的天数 + * @param hours 要减去的小时数 + * @param minutes 要减去的分钟数 + * @param seconds 要减去的秒数 + * @return 过去的时间戳(13位) + */ + public long getPastTimestampFromNow(LocalDateTime currentDateTime ,long years, long months, long weeks, long days, long hours, long minutes, long seconds) { + // 计算过去的时间 + LocalDateTime targetDateTime = + currentDateTime + .minusYears(years) + .minusMonths(months) + .minusWeeks(weeks) + .minusDays(days) + .minusHours(hours) + .minusMinutes(minutes) + .minusSeconds(seconds); + // 转换为13位时间戳 + return targetDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + + /** + * 获取当前时间的时间戳 13位 + */ + public long getCurrentTimestamp(LocalDateTime currentDateTime) { + // 转换为13位时间戳 + return currentDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); + } + +} diff --git a/modules/thing/src/main/java/com/thing/device/board/controller/BoardController.java b/modules/thing/src/main/java/com/thing/device/board/controller/BoardController.java new file mode 100644 index 0000000..be88a05 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/board/controller/BoardController.java @@ -0,0 +1,57 @@ +package com.thing.device.board.controller; + +import com.thing.common.core.web.response.Result; +import com.thing.device.board.dto.BoardTsKvDTO; +import com.thing.device.board.service.BoardService; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; + +/** + * 看板 + * + * @author zhenghh. 2022-04-26 + **/ +@RestController +@RequestMapping("board") +@Tag(name = "看板") +public class BoardController { + + @Autowired + private BoardService boardService; + + @GetMapping("statistics/{scene}") + @Operation(summary="数据统计") + public Result> statistics(@PathVariable String scene) { + List list = boardService.statistics(scene); + + return new Result>().ok(list); + } + + @GetMapping("company") + @Operation(summary="企业信息") + @Parameter(name = "tenantCode",description ="租户编码") + public Result company( @RequestParam Map params) { + SysTenantDetailDTO dto = boardService.company(params); + + return new Result().ok(dto); + } + + @GetMapping("company/{tenantCode}/{thingCode}/{scene}") + @Operation(summary="企业看板") + public Result> companyBoard(@PathVariable Long tenantCode, @PathVariable String thingCode, @PathVariable String scene) { + List list = boardService.companyBoard(tenantCode, thingCode, scene); + + return new Result>().ok(list); + } +} diff --git a/modules/thing/src/main/java/com/thing/device/board/dto/BoardTsKvDTO.java b/modules/thing/src/main/java/com/thing/device/board/dto/BoardTsKvDTO.java new file mode 100644 index 0000000..94819a8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/board/dto/BoardTsKvDTO.java @@ -0,0 +1,51 @@ +package com.thing.device.board.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import org.apache.commons.lang3.StringUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * @author zhenghh. 2022-05-07 + **/ +@Data +@Schema( name= "看板数据") +public class BoardTsKvDTO { + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "物属性") + private String attrKey; + + @Schema(description = "时间") + private Long ts; + + @Schema(description = "值") + private Object val; + + @Schema(description = "单位") + private String unit; + + public BoardTsKvDTO(String thingCode, String attrKey, Long ts, Object val, String unit) { + this.thingCode = thingCode; + this.attrKey = attrKey; + this.ts = ts; + this.unit = unit; + if(val instanceof String) { + String temp = (String) val; + if(StringUtils.isNotBlank(temp) && !StringUtils.equals("null", temp)) { + this.val = new BigDecimal(temp).setScale(3, RoundingMode.HALF_UP).toPlainString(); + } + } else if (val instanceof BigDecimal){ + this.val = (new BigDecimal(val.toString())).setScale(3, RoundingMode.HALF_UP).toPlainString(); + } else { + this.val = val; + } + } +} diff --git a/modules/thing/src/main/java/com/thing/device/board/service/BoardService.java b/modules/thing/src/main/java/com/thing/device/board/service/BoardService.java new file mode 100644 index 0000000..e4e3f51 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/board/service/BoardService.java @@ -0,0 +1,40 @@ +package com.thing.device.board.service; + + +import com.thing.device.board.dto.BoardTsKvDTO; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; + +import java.util.List; +import java.util.Map; + +/** + * @author zhenghh. 2022-04-26 + **/ +public interface BoardService { + + + /** + * 数据统计 + * @param scene 场景 + * @return list + */ + List statistics(String scene); + + /** + * 企业信息 + * + * @param params 参数 + * @return 企业信息 + */ + SysTenantDetailDTO company(Map params); + + /** + * 企业看板 + * + * @param tenantCode 租户编码 + * @param thingCode 企业物编码 + * @param scene 场景 + * @return list + */ + List companyBoard(Long tenantCode, String thingCode, String scene); +} diff --git a/modules/thing/src/main/java/com/thing/device/board/service/impl/BoardServiceImpl.java b/modules/thing/src/main/java/com/thing/device/board/service/impl/BoardServiceImpl.java new file mode 100644 index 0000000..2894e5f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/board/service/impl/BoardServiceImpl.java @@ -0,0 +1,266 @@ +package com.thing.device.board.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thing.common.core.enumeration.AttributeEnum; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.device.board.dto.BoardTsKvDTO; +import com.thing.device.board.service.BoardService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import com.thing.sys.tenant.service.SysTenantDetailService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.group.service.IotGroupRelationService; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@Slf4j +@Service +@RequiredArgsConstructor +public class BoardServiceImpl implements BoardService { + + private final SysTenantDetailService sysTenantDetailService; + private final IotGroupRelationService groupRelationService; + private final TskvService tsKvService; + private final ThingManageContextService contextService; + + /** + * 获取企业物 + * + * @return 企业物编码 + */ + private SysTenantDetailDTO getCompany(Long tenantCode) { + SysTenantDetailDTO result = sysTenantDetailService.getDetail(tenantCode == null ? TenantContext.getTenantCode(SecurityUser.getUser()) : tenantCode); + if (result == null) { + throw new SysException("未找到对应企业"); + } + return result; + } + + /** + * 获取物实体 + * + * @return 企业物编码 + */ + private IotThingEntityDTO getThingTenant(Long tenantCode, String thingCode) { + Optional optional = contextService.findEntityByCode(thingCode, tenantCode, true); + if (optional.isEmpty()) { + throw new SysException("未找到企业物"); + } + return optional.get(); + } + + /** + * 企业信息 + * + * @param params 参数 + * @return 企业信息 + */ + @Override + public SysTenantDetailDTO company(Map params) { + String tenantCode = (String) params.get("tenantCode"); + return getCompany(StringUtils.isBlank(tenantCode) ? null : Long.parseLong(tenantCode)); + } + + /** + * 获取属性单位 + * + * @return map + */ + private Map getUnitMap(Long tenantCode) { + Map params = Maps.newHashMapWithExpectedSize(2); + params.put("tenantCode", tenantCode); + params.put("entityCodeList", AttributeEnum.getAllEnums()); + return groupRelationService.getAttrExtendDataMapByTenantCode(params); + } + + /** + * 数据统计 + * + * @return list + */ + @Override + public List statistics(String scene) { + SysTenantDetailDTO company = getCompany(null); + String thingCode = company.getThingCode(); + if (StringUtils.isBlank(thingCode)) { + throw new SysException("未找到企业物"); + } + IotThingEntityDTO thingTenant = getThingTenant(company.getId(), thingCode); + //单位 + Map unitMap = getUnitMap(company.getId()); + + List keyList = Stream.of(AttributeEnum.CO2.name(), AttributeEnum.D2.name(), AttributeEnum.A29.name()) + .map(item -> item.concat(AttributeTypeEnum.yy.name())).collect(Collectors.toList()); + List boardTsKvLastList = getBoardTsKvLastList(thingTenant, keyList, unitMap); + //节约用电量 + boardTsKvLastList.add(savingElectricity(thingTenant, unitMap)); + return boardTsKvLastList; + } + + /** + * 节约用电量 + * + * @param thingTenant 物实体 + * @param unitMap 单位 + * @return 节约用电量 + */ + private BoardTsKvDTO savingElectricity(IotThingEntityDTO thingTenant, Map unitMap) { + long endTime = System.currentTimeMillis(); + long beginTime = DateTimeUtils.getYearBeginTimestamp(endTime); + BigDecimal bigDecimal = getBoardTsKvList(thingTenant, CollectionUtil.newArrayList(AttributeEnum.AC122.name()), beginTime, endTime, TimeType.MONTH, unitMap) + .parallelStream().filter(item -> item.getVal() != null && StringUtils.isNotBlank(item.getVal().toString()) && !StringUtils.equals("null", item.getVal().toString())) + .map(item -> new BigDecimal(item.getVal().toString())).reduce(BigDecimal::add).orElse(null); + return new BoardTsKvDTO(thingTenant.getCode(), AttributeEnum.AC122.name(), endTime, bigDecimal, unitMap.get(AttributeEnum.AC122.name())); + } + + /** + * 获取最新数据 + * + * @param thingTenant 物实体 + * @param keyList 物属性列表 + * @param unitMap 单位 + * @return list + */ + private List getBoardTsKvLastList(IotThingEntityDTO thingTenant, List keyList, Map unitMap) { + List lastTimeSeries = tsKvService.getLastTimeSeries(new ThingAttrDTO(thingTenant.getId(), thingTenant.getCode(), keyList)); + return convertBoardTsKv(unitMap, lastTimeSeries); + } + + /** + * 获取区间数据 + * + * @param thingTenant 物实体 + * @param keyList 物属性列表 + * @return list + */ + private List getBoardTsKvList(IotThingEntityDTO thingTenant, List keyList, Long startTime, Long endTime, TimeType granularity, Map unitMap) { + List timeSeries = tsKvService.getTimeSeries(new ThingAttrDTO(thingTenant.getId(), thingTenant.getCode(), keyList), startTime, endTime, granularity); + return convertBoardTsKv(unitMap, timeSeries); + } + + /** + * 数据转换 + * + * @param unitMap 单位 + * @param timeSeries 数据列表 + * @return list + */ + private List convertBoardTsKv(Map unitMap, List timeSeries) { + if (CollectionUtil.isEmpty(timeSeries)) { + return Lists.newArrayList(); + } + return timeSeries.stream() + .map(item -> new BoardTsKvDTO(item.getThingCode(), item.getThingAttr(), item.getTs(), item.getVal(), getUnit(item.getThingAttr(), unitMap))) + .collect(Collectors.toList()); + } + + /** + * 获取单位 + * + * @param key 属性 + * @param unitMap 单位集合 + * @return 单位 + */ + private String getUnit(String key, Map unitMap) { + String unit = ""; + if (unitMap != null) { + for (AttributeTypeEnum value : AttributeTypeEnum.values()) { + key = key.replace(value.name(), ""); + } + unit = unitMap.get(key); + } + return unit; + } + + /** + * 企业看板 + * + * @param tenantCode 租户编码 + * @param thingCode 企业物编码 + * @param scene 场景 + * @return list + */ + @Override + public List companyBoard(Long tenantCode, String thingCode, String scene) { + IotThingEntityDTO thingTenant = getThingTenant(tenantCode, thingCode); + //单位 + Map unitMap = getUnitMap(tenantCode); + + List keyList = Stream.of(AttributeEnum.AC100.name(), AttributeEnum.AC101.name(), AttributeEnum.D6.name(), + AttributeEnum.D5.name(), AttributeEnum.D1.name(), AttributeEnum.A16.name(), AttributeEnum.AC121.name()) + .collect(Collectors.toList()); + List latestData = getBoardTsKvLastList(thingTenant, keyList, unitMap); + latestData = getTemperature(latestData, thingCode, unitMap); + + long nowTime = System.currentTimeMillis(); + //当天 + long dayBeginTime = DateTimeUtils.getDayBeginTimestamp(nowTime); + List dayKeyList = Stream.of(AttributeEnum.D2.name(), AttributeEnum.A29.name(), AttributeEnum.CO2.name()) + .map(item -> item.concat(AttributeTypeEnum.dd.name())).collect(Collectors.toList()); + List dayData = getBoardTsKvList(thingTenant, dayKeyList, dayBeginTime, dayBeginTime + 1000, TimeType.DAY, unitMap); + latestData.addAll(dayData); + //当月 + long monthBeginTime = DateTimeUtils.getMonthBeginTimestamp(nowTime); + List monthData = getBoardTsKvList(thingTenant, CollectionUtil.newArrayList(AttributeEnum.CO2.name().concat(AttributeTypeEnum.mm.name())), monthBeginTime, monthBeginTime + 1000, TimeType.DAY, unitMap); + latestData.addAll(monthData); + //当年 + long yearBeginTime = DateTimeUtils.getYearBeginTimestamp(nowTime); + List yearData = getBoardTsKvList(thingTenant, CollectionUtil.newArrayList(AttributeEnum.CO2.name().concat(AttributeTypeEnum.yy.name())), yearBeginTime, yearBeginTime + 1000, TimeType.DAY, unitMap); + latestData.addAll(yearData); + //日均流量 总量/min + BigDecimal flow = dayData.parallelStream().filter(item -> StringUtils.equals(AttributeEnum.D2.name().concat(AttributeTypeEnum.dd.name()), item.getAttrKey())) + .map(item -> new BigDecimal(item.getVal().toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); + Object ac106 = BigDecimal.ZERO.equals(flow) ? "null" : flow.divide(BigDecimal.valueOf((double) (nowTime - dayBeginTime) / 60000), 3, RoundingMode.HALF_UP); + latestData.add(new BoardTsKvDTO(thingCode, AttributeEnum.AC106.name(), nowTime, ac106, unitMap.get(AttributeEnum.AC106.name()))); + //日均功率 + BigDecimal power = dayData.parallelStream().filter(item -> StringUtils.equals(AttributeEnum.A29.name().concat(AttributeTypeEnum.dd.name()), item.getAttrKey())) + .map(item -> new BigDecimal(item.getVal().toString())).reduce(BigDecimal::add).orElse(BigDecimal.ZERO); + Object ac108 = BigDecimal.ZERO.equals(power) ? "null" : power.divide(BigDecimal.valueOf((double) (nowTime - dayBeginTime) / 60000 / 60), 3, RoundingMode.HALF_UP); + latestData.add(new BoardTsKvDTO(thingCode, AttributeEnum.AC108.name(), nowTime, ac108, unitMap.get(AttributeEnum.AC108.name()))); + return latestData; + + } + + /** + * 获取露点温度 + * 获取主管道上的露点仪温度;若未接入设备,则手动输入 + * + * @param latestData 数据列表 + * @param thingCode 物编码 + * @param unitMap 单位 + */ + private List getTemperature(List latestData, String thingCode, Map unitMap) { + BoardTsKvDTO d5 = latestData.parallelStream() + .filter(item -> StringUtils.equals(item.getAttrKey(), AttributeEnum.D5.name())) + .findFirst().orElse(new BoardTsKvDTO(thingCode, AttributeEnum.D5.name(), System.currentTimeMillis(), null, unitMap.get(AttributeEnum.D5.name()))); + if (d5.getVal() == null) { + latestData.parallelStream().filter(item -> StringUtils.equals(item.getAttrKey(), AttributeEnum.AC121.name())) + .findFirst().ifPresent(item -> d5.setVal(item.getVal())); + } + latestData = latestData.parallelStream() + .filter(item -> !StringUtils.equals(AttributeEnum.D5.name(), item.getAttrKey()) && !StringUtils.equals(AttributeEnum.AC121.name(), item.getAttrKey())) + .collect(Collectors.toList()); + latestData.add(d5); + return latestData; + } +} diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/controller/IotDeviceManagementController.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/controller/IotDeviceManagementController.java new file mode 100644 index 0000000..72059e7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/controller/IotDeviceManagementController.java @@ -0,0 +1,131 @@ +package com.thing.device.deviceManagement.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.control.service.IotDeviceControlService; +import com.thing.device.deviceManagement.dto.DeviceManagementParam; +import com.thing.device.deviceManagement.dto.DeviceManagementReq; +import com.thing.device.deviceManagement.dto.IotDeviceManagementDTO; +import com.thing.device.deviceManagement.service.IotDeviceManagementService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** +* 设备监控属性配置信息 +* +* @author xc +* @since 3.0 2024-04-09 +*/ +@RestController +@RequestMapping("v2/deviceManagement/iotdevicemanagement") +@Tag(name="设备监控属性配置信息") +@RequiredArgsConstructor +public class IotDeviceManagementController { + + private final IotDeviceManagementService iotDeviceManagementService; + + private final IotDeviceControlService iotDeviceControlService; + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotDeviceManagementService.getPageData(params, IotDeviceManagementDTO.class); + page.getList().forEach(temp->{ + temp.setCtlInfo(iotDeviceControlService.getById(temp.getCtlId())); + try { + temp.setSourceThingId(temp.getCtlInfo().getThingId()); + } catch (Exception e) { + temp.setSourceThingId(null); + } + }); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotDeviceManagementDTO data = iotDeviceManagementService.getByIdAs(id, IotDeviceManagementDTO.class); + if(ObjectUtils.isNotEmpty(data.getCtlId())){ + data.setCtlInfo(iotDeviceControlService.getById(data.getCtlId())); + try { + data.setSourceThingId(data.getCtlInfo().getThingId()); + } catch (Exception e) { + data.setSourceThingId(null); + } + } + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotDeviceManagementDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotDeviceManagementService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDeviceManagementDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDeviceManagementService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDeviceManagementService.batchDelete(ids); + return new Result<>(); + } + + + + @PostMapping("getAttrInfoByRelationAndThingId") + @Operation(summary="按类型分组得属性信息") + public Result getAttrInfoByThingRelation(@RequestBody DeviceManagementParam param){ + return new Result().ok(iotDeviceManagementService.getAttrInfoByThingRelation(param)); + } + + + + + + @GetMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = iotDeviceManagementService.listAs(params, IotDeviceManagementDTO.class); + //ExcelUtils.exportExcelToTarget(response, null, "设备监控属性配置信息", list, IotDeviceManagementExcel.class); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementParam.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementParam.java new file mode 100644 index 0000000..3baca4a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementParam.java @@ -0,0 +1,15 @@ +package com.thing.device.deviceManagement.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class DeviceManagementParam { + + @Schema(description = "物关系id") + private Long thingRelationId; + @Schema(description = "物实体id") + private Long thingId; + +} diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementReq.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementReq.java new file mode 100644 index 0000000..f0e981a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/DeviceManagementReq.java @@ -0,0 +1,18 @@ +package com.thing.device.deviceManagement.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class DeviceManagementReq { + + @Schema(description = "历史数据属性列表") + private List bottomAttr; + @Schema(description = "最新数据属性列表") + private List topLeftAttr; + @Schema(description = "控制命令列表") + private List ctlAttr; + +} diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/IotDeviceManagementDTO.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/IotDeviceManagementDTO.java new file mode 100644 index 0000000..77bf8e0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/dto/IotDeviceManagementDTO.java @@ -0,0 +1,58 @@ +package com.thing.device.deviceManagement.dto; + +import com.thing.control.entity.IotDeviceControlEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 设备监控属性配置信息 +* +* @author xc +* @since 3.0 2024-04-09 +*/ +@Data +public class IotDeviceManagementDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "物关系id") + private Long thingRelationId; + @Schema(description = "物实体id") + private Long thingId; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物名称") + private String thingName; + @Schema(description = "物属性编码") + private String thingAttrCode; + @Schema(description = "物属性名称") + private String thingAttrName; + @Schema(description = "物属性单位") + private String thingAttrUnit; + @Schema(description = "序号") + private String sort; + @Schema(description = "控制命令id 控制命令类型得时候,命令id传入这里") + private Long ctlId; + @Schema(description = "属性标题/控制命令标题,这里有值图例,按钮,控制名称展示这里,没有展示属性名称,或者控制名称") + private String title; + @Schema(description = "图标图片") + private String img; + @Schema(description = "图例类型/自定义方法函数 列如 1 折线图,2柱状图") + private String viewType; + @Schema(description = "配置类型 1历史数据图例,2最新值图标,3控制") + private String configType; + @Schema(description = "上下限JSON{\"up\":\"\",\"down\":\"\"}") + private String thingAttrBoundary; + @Schema(description = "英文逗号分割,如2,3,4 表示2天3小时4分钟") + private String timeInfo; + @Schema(description = "控制详情,只有 configType是3得 控制得,这里才会有数据") + private IotDeviceControlEntity ctlInfo; + + @Schema(description = "源属性id") + private Long sourceThingId; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/entity/IotDeviceManagementEntity.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/entity/IotDeviceManagementEntity.java new file mode 100644 index 0000000..0dbe922 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/entity/IotDeviceManagementEntity.java @@ -0,0 +1,87 @@ +package com.thing.device.deviceManagement.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 设备监控属性配置信息 + * + * @author xc + * @since 3.0 2024-04-09 + */ +@Data +@EqualsAndHashCode(callSuper=false) +@Table("iot_device_management") +public class IotDeviceManagementEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @Id + private Long id; + /** + * 物关系id + */ + private Long thingRelationId; + /** + * 物实体id + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 物名称 + */ + private String thingName; + /** + * 物属性编码 + */ + private String thingAttrCode; + /** + * 物属性名称 + */ + private String thingAttrName; + /** + * 物属性单位 + */ + private String thingAttrUnit; + /** + * 序号 + */ + private String sort; + /** + * 控制命令id + */ + private Long ctlId; + /** + * 属性标题/控制命令标题 + */ + private String title; + /** + * 图标图片 + */ + private String img; + /** + * 图例类型/自定义方法函数 + */ + private String viewType; + /** + * 配置类型 1图例,2按钮,3控制 + */ + private String configType; + /** + * 上下限JSON{"up":"","down":""} + */ + private String thingAttrBoundary; + + /** + * 英文逗号分割,如2,3,4 表示2天3小时4分钟 + */ + private String timeInfo; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/excel/IotDeviceManagementExcel.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/excel/IotDeviceManagementExcel.java new file mode 100644 index 0000000..725a925 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/excel/IotDeviceManagementExcel.java @@ -0,0 +1,50 @@ +package com.thing.device.deviceManagement.excel;//package com.thing.device.deviceManagement.excel; +// +//import com.alibaba.excel.annotation.ExcelProperty; +//import com.alibaba.excel.annotation.write.style.ColumnWidth; +//import com.alibaba.excel.annotation.write.style.ContentRowHeight; +//import com.alibaba.excel.annotation.write.style.HeadRowHeight; +//import lombok.Data; +// +///** +// * 设备监控属性配置信息 +// * +// * @author xc +// * @since 3.0 2024-04-09 +// */ +//@Data +//@ContentRowHeight(20) +//@HeadRowHeight(20) +//@ColumnWidth(25) +//public class IotDeviceManagementExcel { +// @ExcelProperty(value = "id", index = 0) +// private Long id; +// @ExcelProperty(value = "物关系id", index = 1) +// private Long thingRelationId; +// @ExcelProperty(value = "物实体id", index = 2) +// private Long thingId; +// @ExcelProperty(value = "物编码", index = 3) +// private String thingCode; +// @ExcelProperty(value = "物名称", index = 4) +// private String thingName; +// @ExcelProperty(value = "物属性编码", index = 5) +// private String thingAttrCode; +// @ExcelProperty(value = "物属性名称", index = 6) +// private String thingAttrName; +// @ExcelProperty(value = "物属性单位", index = 7) +// private String thingAttrUnit; +// @ExcelProperty(value = "序号", index = 8) +// private String sort; +// @ExcelProperty(value = "控制命令id", index = 9) +// private Long ctlId; +// @ExcelProperty(value = "属性标题/控制命令标题", index = 10) +// private String title; +// @ExcelProperty(value = "图标图片", index = 11) +// private String img; +// @ExcelProperty(value = "图例类型/自定义方法函数", index = 12) +// private String viewType; +// @ExcelProperty(value = "配置类型 1图例,2按钮,3控制", index = 13) +// private String configType; +// @ExcelProperty(value = "上下限JSON{"up":"","down":""}", index = 14) +// private String thingAttrBoundary; +//} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/mapper/IotDeviceManagementMapper.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/mapper/IotDeviceManagementMapper.java new file mode 100644 index 0000000..3c1ace4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/mapper/IotDeviceManagementMapper.java @@ -0,0 +1,16 @@ +package com.thing.device.deviceManagement.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.device.deviceManagement.entity.IotDeviceManagementEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 设备监控属性配置信息 +* +* @author xc +* @since 3.0 2024-04-09 +*/ +@Mapper +public interface IotDeviceManagementMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/service/IotDeviceManagementService.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/service/IotDeviceManagementService.java new file mode 100644 index 0000000..870966a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/service/IotDeviceManagementService.java @@ -0,0 +1,17 @@ +package com.thing.device.deviceManagement.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.device.deviceManagement.dto.DeviceManagementParam; +import com.thing.device.deviceManagement.dto.DeviceManagementReq; +import com.thing.device.deviceManagement.entity.IotDeviceManagementEntity; + +/** + * 设备监控属性配置信息 + * + * @author xc + * @since 3.0 2024-04-09 + */ +public interface IotDeviceManagementService extends IBaseService { + + DeviceManagementReq getAttrInfoByThingRelation(DeviceManagementParam param); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/deviceManagement/service/impl/IotDeviceManagementServiceImpl.java b/modules/thing/src/main/java/com/thing/device/deviceManagement/service/impl/IotDeviceManagementServiceImpl.java new file mode 100644 index 0000000..6df2d7b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/deviceManagement/service/impl/IotDeviceManagementServiceImpl.java @@ -0,0 +1,80 @@ +package com.thing.device.deviceManagement.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.control.service.IotDeviceControlService; +import com.thing.device.deviceManagement.dto.DeviceManagementParam; +import com.thing.device.deviceManagement.dto.DeviceManagementReq; +import com.thing.device.deviceManagement.dto.IotDeviceManagementDTO; +import com.thing.device.deviceManagement.entity.IotDeviceManagementEntity; +import com.thing.device.deviceManagement.mapper.IotDeviceManagementMapper; +import com.thing.device.deviceManagement.service.IotDeviceManagementService; +import com.thing.sys.oss.cloud.OSSFactory; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 设备监控属性配置信息 + * + * @author xc + * @since 3.0 2024-04-09 + */ +@Service +public class IotDeviceManagementServiceImpl extends BaseServiceImpl implements IotDeviceManagementService { + + @Resource + private IotDeviceControlService iotDeviceControlService; + + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + return wrapper; + } + + + @Override + public DeviceManagementReq getAttrInfoByThingRelation(DeviceManagementParam param) { + DeviceManagementReq result = new DeviceManagementReq(); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotDeviceManagementEntity::getThingRelationId,param.getThingRelationId()); + wrapper.eq(IotDeviceManagementEntity::getThingId,param.getThingId()); + List deviceManagementEntityList = mapper.selectListByQueryAs(wrapper,IotDeviceManagementDTO.class); + if(ObjectUtil.isEmpty(deviceManagementEntityList)){ + return new DeviceManagementReq(); + } + deviceManagementEntityList.forEach(temp->{ + String img = temp.getImg(); + if(StringUtils.isNotEmpty(img)){ + temp.setImg(OSSFactory.splice(img)); + } + }); + Map> groupedByConfigType = deviceManagementEntityList.stream() + .collect(Collectors.groupingBy(IotDeviceManagementDTO::getConfigType)); + groupedByConfigType.forEach((configType, entities) -> { + if(configType.equals("1")){ + result.setBottomAttr(entities); + }else if(configType.equals("2")){ + result.setTopLeftAttr(entities); + }else { + entities.forEach(temp->{ + temp.setCtlInfo(iotDeviceControlService.getById(temp.getCtlId())); + try { + temp.setSourceThingId(temp.getCtlInfo().getThingId()); + } catch (Exception e) { + temp.setSourceThingId(null); + } + }); + result.setCtlAttr(entities); + } + }); + return result; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/controller/IotDataEntryController.java b/modules/thing/src/main/java/com/thing/device/entry/controller/IotDataEntryController.java new file mode 100644 index 0000000..4ebba4a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/controller/IotDataEntryController.java @@ -0,0 +1,128 @@ +package com.thing.device.entry.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.device.entry.dto.IotDataEntryDTO; +import com.thing.device.entry.service.IotDataEntryService; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + + +import java.util.Map; + +/** +* 数据录入 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-05-13 +*/ +@RestController +@RequestMapping("device/iotdataentry") +@Tag(name="数据录入") +@RequiredArgsConstructor +public class IotDataEntryController { + private final IotDataEntryService iotDataEntryService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "thingKeyword", description = "设备编号/设备名称"), + @Parameter(name = "attrKeyword", description = "属性名称/属性编码"), + @Parameter(name = "beginTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public Result> page( @RequestParam Map params){ + PageData page = iotDataEntryService.getPageData(params, IotDataEntryDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotDataEntryDTO data = iotDataEntryService.getByIdAs(id, IotDataEntryDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotDataEntryDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotDataEntryService.operationIotDataEntry(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotDataEntryDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotDataEntryService.operationIotDataEntry(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotDataEntryService.deleteIotDataEntry(ids); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "thingKeyword", description = "设备编号/设备名称"), + @Parameter(name = "attrKeyword", description = "属性名称/属性编码"), + @Parameter(name = "beginTime", description = "开始时间"), + @Parameter(name = "endTime", description = "结束时间") + }) + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + iotDataEntryService.export(Lists.newArrayList(ids), params, response); + } + + @PostMapping("exportTemplate") + @Operation(summary="导出模板") + @LogOperation("导出模板") + public void exportTemplate(HttpServletResponse response) { + iotDataEntryService.exportTemplate(response); + } + + @PostMapping("importDataEntry") + @Operation(summary="数据导入") + public Result importDataEntry(HttpServletRequest request, @RequestParam("file") MultipartFile file){ + iotDataEntryService.importDataEntry(file, request); + return new Result<>(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/dto/IotDataEntryDTO.java b/modules/thing/src/main/java/com/thing/device/entry/dto/IotDataEntryDTO.java new file mode 100644 index 0000000..358b49f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/dto/IotDataEntryDTO.java @@ -0,0 +1,53 @@ +package com.thing.device.entry.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 数据录入 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-05-13 +*/ +@Data +@Schema( name= "数据录入") +public class IotDataEntryDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "设备编号") + private String thingCode; + @Schema(description = "设备名称") + private String thingName; + @Schema(description = "属性编号") + private String attrCode; + @Schema(description = "属性名称") + private String attrName; + @Schema(description = "属性值") + private String attrValue; + @Schema(description = "数据时间") + private Long ts; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/entity/IotDataEntryEntity.java b/modules/thing/src/main/java/com/thing/device/entry/entity/IotDataEntryEntity.java new file mode 100644 index 0000000..f09e021 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/entity/IotDataEntryEntity.java @@ -0,0 +1,51 @@ +package com.thing.device.entry.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 数据录入 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-13 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_data_entry") +public class IotDataEntryEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 设备编号 + */ + private String thingCode; + /** + * 设备名称 + */ + private String thingName; + /** + * 属性编号 + */ + private String attrCode; + /** + * 属性名称 + */ + private String attrName; + /** + * 属性值 + */ + private String attrValue; + /** + * 数据时间 + */ + private Long ts; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryExcel.java b/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryExcel.java new file mode 100644 index 0000000..ee8f1b4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryExcel.java @@ -0,0 +1,35 @@ +package com.thing.device.entry.excel; + + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.common.core.utils.DateTimeUtils; + +import lombok.Data; + +@Data +public class IotDataEntryExcel { + + @Excel(name = "设备编号", orderNum = "1") + private String thingCode; + @Excel(name = "设备名称", orderNum = "2") + private String thingName; + @Excel(name = "属性编号", orderNum = "3") + private String attrCode; + @Excel(name = "属性名称", orderNum = "4") + private String attrName; + @Excel(name = "属性值", orderNum = "5") + private String attrValue; + @Excel(name = "数据时间", orderNum = "6") + private String ts; + + /** + * 导出 + * @param ts + */ + public void setTs(Long ts) { + this.ts = DateTimeUtils.timestampToDate(ts, DateTimeUtils.DATE_TIME_PATTERN_STR); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryImportExcel.java b/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryImportExcel.java new file mode 100644 index 0000000..b63b014 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/excel/IotDataEntryImportExcel.java @@ -0,0 +1,24 @@ +package com.thing.device.entry.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; + +import com.thing.common.orm.dto.BaseDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = false) +public class IotDataEntryImportExcel extends BaseDTO { + @Excel(name = "设备编号", orderNum = "1") + private String thingCode; + @Excel(name = "设备名称", orderNum = "2") + private String thingName; + @Excel(name = "属性编号", orderNum = "3") + private String attrCode; + @Excel(name = "属性名称", orderNum = "4") + private String attrName; + @Excel(name = "属性值", orderNum = "5") + private String attrValue; + @Excel(name = "数据时间", orderNum = "6") + private String ts; +} diff --git a/modules/thing/src/main/java/com/thing/device/entry/mapper/IotDataEntryMapper.java b/modules/thing/src/main/java/com/thing/device/entry/mapper/IotDataEntryMapper.java new file mode 100644 index 0000000..a6bf3d3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/mapper/IotDataEntryMapper.java @@ -0,0 +1,16 @@ +package com.thing.device.entry.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.device.entry.entity.IotDataEntryEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 数据录入 +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-05-13 +*/ +@Mapper +public interface IotDataEntryMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/service/IotDataEntryService.java b/modules/thing/src/main/java/com/thing/device/entry/service/IotDataEntryService.java new file mode 100644 index 0000000..aa033bc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/service/IotDataEntryService.java @@ -0,0 +1,43 @@ +package com.thing.device.entry.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.device.entry.dto.IotDataEntryDTO; +import com.thing.device.entry.entity.IotDataEntryEntity; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 数据录入 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-13 + */ +public interface IotDataEntryService extends IBaseService { + /** + * 导出 + */ + void export(List idList, Map params, HttpServletResponse response); + + /** + * 导出模板 + */ + void exportTemplate(HttpServletResponse response); + + /** + * 导入 + */ + void importDataEntry(MultipartFile file, HttpServletRequest request); + + /** + * 新增或修改IotDataEntry + */ + void operationIotDataEntry(IotDataEntryDTO iotDataEntryDTO); + + @Transactional(rollbackFor = Exception.class) + void deleteIotDataEntry(Long[] ids); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/entry/service/impl/IotDataEntryServiceImpl.java b/modules/thing/src/main/java/com/thing/device/entry/service/impl/IotDataEntryServiceImpl.java new file mode 100644 index 0000000..d34e957 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/entry/service/impl/IotDataEntryServiceImpl.java @@ -0,0 +1,149 @@ +package com.thing.device.entry.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.device.entry.dto.IotDataEntryDTO; +import com.thing.device.entry.entity.IotDataEntryEntity; +import com.thing.device.entry.excel.IotDataEntryExcel; +import com.thing.device.entry.excel.IotDataEntryImportExcel; +import com.thing.device.entry.mapper.IotDataEntryMapper; +import com.thing.device.entry.service.IotDataEntryService; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.thing.device.entry.entity.table.IotDataEntryEntityTableDef.IOT_DATA_ENTRY_ENTITY; + +/** + * 数据录入 + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-13 + */ +@Service +public class IotDataEntryServiceImpl extends BaseServiceImpl implements IotDataEntryService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String thingKeyword = (String) params.get("thingKeyword"); + String attrKeyword = (String) params.get("attrKeyword"); + Long beginTime = Long.parseLong((String) params.get("beginTime")); + Long endTime = Long.parseLong((String) params.get("endTime")); + if (StringUtils.isNotBlank(thingKeyword)){ + wrapper.and( + IOT_DATA_ENTRY_ENTITY + .THING_NAME + .like(thingKeyword) + .or(IOT_DATA_ENTRY_ENTITY.THING_CODE.like(thingKeyword))); + } + if (StringUtils.isNotBlank(attrKeyword)){ + wrapper.and( + IOT_DATA_ENTRY_ENTITY + .ATTR_CODE + .like(attrKeyword) + .or(IOT_DATA_ENTRY_ENTITY.ATTR_NAME.like(attrKeyword))); + } + wrapper.between(IotDataEntryEntity::getTs, beginTime, endTime); + return wrapper; + } + + + @Override + public void export(List idList, Map params, HttpServletResponse response) { + List iotDataEntryDTOList = listAs(params, IotDataEntryDTO.class); + if (CollectionUtil.isNotEmpty(idList)) iotDataEntryDTOList = iotDataEntryDTOList.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + String titleName = (String) params.get("titleName"); + Optional.ofNullable(titleName).orElseThrow(() -> new SysException("表名不能为空")); + List iotDataEntryExcel = ConvertUtils.sourceToTarget(iotDataEntryDTOList, IotDataEntryExcel.class); + ExcelUtils.exportExcel(iotDataEntryExcel, titleName, titleName, IotDataEntryExcel.class, titleName.concat(".xls"), response); + } + + @Override + public void exportTemplate(HttpServletResponse response) { + ExcelUtils.exportExcel(Lists.newArrayList(), "数据录入", null, IotDataEntryExcel.class, "数据录入.xls", response); + } + + @Override + @DataFilter + @Transactional(rollbackFor = Exception.class) + public void importDataEntry(MultipartFile file, HttpServletRequest request){ + //校验文件名 + if(!StringUtils.endsWith(file.getOriginalFilename(), "xls") && !StringUtils.endsWith(file.getOriginalFilename(), "xlsx")){ + throw new SysException("文件格式不合法,请选择Excel文件"); + } + List iotDataEntryImportExcelList = ExcelUtils.importExcel(file, 1, 1, IotDataEntryImportExcel.class, 0); + if (CollectionUtil.isEmpty(iotDataEntryImportExcelList) || CollectionUtil.isEmpty(iotDataEntryImportExcelList.stream().filter(item -> StringUtils.isNotBlank(item.getThingCode())).collect(Collectors.toList()))) throw new SysException("导入数据为空或模板有误!"); + for (IotDataEntryImportExcel iotDataEntryImportExcel : iotDataEntryImportExcelList) { + if (StringUtils.isBlank(iotDataEntryImportExcel.getThingCode())) continue; + IotDataEntryDTO iotDataEntryDTO = ConvertUtils.sourceToTarget(iotDataEntryImportExcel, IotDataEntryDTO.class); + Optional.ofNullable(iotDataEntryImportExcel.getTs()).orElseThrow(() -> new SysException("时间不能为空!")); + iotDataEntryDTO.setTs(DateTimeUtils.dateToTimestamp(iotDataEntryImportExcel.getTs(), DateTimeUtils.DATE_TIME_PATTERN_STR)); + operationIotDataEntry(iotDataEntryDTO); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @DataFilter + public void operationIotDataEntry(IotDataEntryDTO iotDataEntryDTO) { + IotDataEntryDTO iotDataEntry = getIotDataEntryDTO(iotDataEntryDTO.getThingCode(), iotDataEntryDTO.getAttrCode(), iotDataEntryDTO.getTs()); + if (StringUtils.isNotBlank(iotDataEntryDTO.getAttrValue())){ + String finalAttrValue; + try { + finalAttrValue = new BigDecimal(iotDataEntryDTO.getAttrValue()).setScale(2, RoundingMode.HALF_UP).toString(); + iotDataEntryDTO.setAttrValue(finalAttrValue); + } catch (NumberFormatException ignored) {} + } + if (ObjectUtil.isNotNull(iotDataEntry)){ + iotDataEntryDTO.setId(iotDataEntry.getId()); + updateDto(iotDataEntryDTO); + }else { + iotDataEntryDTO.setId(null); + saveDto(iotDataEntryDTO); + } + } + + private IotDataEntryDTO getIotDataEntryDTO(String thingCode, String attrCode, Long ts) { + IotDataEntryEntity iotDataEntryEntity = mapper.selectOneByQuery(QueryWrapper.create() + .eq(IotDataEntryEntity::getThingCode, thingCode, StringUtils.isNotBlank(thingCode)) + .eq(IotDataEntryEntity::getAttrCode, attrCode, StringUtils.isNotBlank(attrCode)) + .eq(IotDataEntryEntity::getTs, ts, ObjectUtil.isNotNull(ts)) + .limit(1)); + return ConvertUtils.sourceToTarget(iotDataEntryEntity, IotDataEntryDTO.class); + } + + + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteIotDataEntry(Long[] ids){ + List iotDataEntryList = mapper.selectListByQuery(QueryWrapper.create() + .in(IotDataEntryEntity::getId, Lists.newArrayList(ids))); + if(CollectionUtil.isEmpty(iotDataEntryList)) { + return; + } + batchDelete(ids); + } + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/menu/controller/IotThingMenuConfigController.java b/modules/thing/src/main/java/com/thing/device/menu/controller/IotThingMenuConfigController.java new file mode 100644 index 0000000..f330eb7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/controller/IotThingMenuConfigController.java @@ -0,0 +1,107 @@ +package com.thing.device.menu.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.service.IotThingMenuConfigService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@RestController +@RequestMapping("device/iotthingmenuconfig") +@Tag(name = "物关系菜单配置管理模块") +@RequiredArgsConstructor +public class IotThingMenuConfigController { + + private final IotThingMenuConfigService iotThingMenuConfigService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + //@RequiresPermissions("device:iotthingmenuconfig:page") + public Result> page(@RequestParam Map params) { + PageData page = iotThingMenuConfigService.getPageData(params, IotThingMenuConfigDTO.class); + return new Result>().ok(page); + } + + @GetMapping("getIotThingMenuConfigExtendDTO") + @Operation(summary = "获取菜单对应的关系列表") + public Result getIotThingMenuConfigExtendDTO(@RequestParam String menuId, @RequestParam String configType) { + IotThingMenuConfigDTO data = iotThingMenuConfigService.getIotThingMenuConfigExtendDTO(menuId, configType); + if (ObjectUtil.isNotNull(data) && ObjectUtil.isNotNull(data.getViewConfig())) { + JSONObject object = JSONObject.parseObject(data.getViewConfig()); + if (object != null) { + JSONObject object1 = JSONObject.parseObject(object.getString("aggregationTimeRange")); + if (object1 != null) { + long resultSecond = 0L; + long minute = object1.getLongValue("minute"); + long hour = object1.getLongValue("hour"); + if (minute != 0) { + resultSecond = resultSecond + (minute * 60); + } + if (hour != 0) { + resultSecond = resultSecond + (hour * 60 * 60); + } + if (resultSecond == 0) { + resultSecond = 900L; + } + data.setSecond(resultSecond); + } + } + } + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@Validated @RequestBody IotThingMenuConfigDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotThingMenuConfigService.addIotThingMenuConfig(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改-默认图表配置") + @LogOperation("修改") + public Result update(@RequestBody IotThingMenuConfigDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotThingMenuConfigService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotThingMenuConfigService.batchDelete(ids); + return new Result<>(); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/menu/dto/IotThingMenuConfigDTO.java b/modules/thing/src/main/java/com/thing/device/menu/dto/IotThingMenuConfigDTO.java new file mode 100644 index 0000000..2d8db7c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/dto/IotThingMenuConfigDTO.java @@ -0,0 +1,90 @@ +package com.thing.device.menu.dto; + +import com.alibaba.fastjson.JSON; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema(name = "device") +public class IotThingMenuConfigDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + @Schema(description = "菜单ID") + private String menuId; + @Schema(description = "菜单配置信息{thingRelationConfig:[]}") + private String menuConfig; + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + private String configType; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "监控分析页面配置json串") + private String viewConfig = "{\"dateTypeOption\":[{\"label\":\"日\",\"value\":\"date\"},{\"label\":\"月\",\"value\":\"month\"},{\"label\":\"年\",\"value\":\"year\"}],\"dateTypeCheck\":[\"date\",\"month\",\"year\"]}"; + @Schema(description = "监控分析页面配置得聚合间隔得秒值") + private Long second; + + public List getConfigList() { + if (StringUtils.isBlank(menuConfig)) { + return Collections.emptyList(); + } + return JSON.parseArray(menuConfig, MenuConfig.class); + } + + @Data + @Accessors(chain = true) + @NoArgsConstructor + @AllArgsConstructor + public static class MenuConfig { + private String id; + private Integer order; + private Boolean isSelected; + private Integer sort; + /** + * 该字段作为备用,可用于对配置按需求分组 + */ + private Long groupId; + + public String toJsonString() { + return JSON.toJSONString(this); + } + } + + private int nextOrder(int order, List inValidOrderList) { + if (order < 1) { + order = 1; + } + if (inValidOrderList.contains(order)) { + return nextOrder(++order, inValidOrderList); + } + return order; + } + +} diff --git a/modules/thing/src/main/java/com/thing/device/menu/entity/IotThingMenuConfigEntity.java b/modules/thing/src/main/java/com/thing/device/menu/entity/IotThingMenuConfigEntity.java new file mode 100644 index 0000000..a95a1ba --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/entity/IotThingMenuConfigEntity.java @@ -0,0 +1,40 @@ +package com.thing.device.menu.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 15:00 2022/5/7 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("iot_thing_menu_config") +public class IotThingMenuConfigEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 菜单ID + */ + private String menuId; + /** + * 菜单配置信息{thingRelationConfig:[]} + */ + private String menuConfig; + /** + * 配置类型 例如:监控分析配置,用量分析配置 + */ + private String configType; + + /** + *监控分析页面配置json串 + */ + private String viewConfig; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/menu/mapper/IotThingMenuConfigMapper.java b/modules/thing/src/main/java/com/thing/device/menu/mapper/IotThingMenuConfigMapper.java new file mode 100644 index 0000000..75750aa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/mapper/IotThingMenuConfigMapper.java @@ -0,0 +1,15 @@ +package com.thing.device.menu.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 15:00 2022/5/7 + */ +@Mapper +public interface IotThingMenuConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/menu/service/IotThingMenuConfigService.java b/modules/thing/src/main/java/com/thing/device/menu/service/IotThingMenuConfigService.java new file mode 100644 index 0000000..519de73 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/service/IotThingMenuConfigService.java @@ -0,0 +1,14 @@ +package com.thing.device.menu.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; + +public interface IotThingMenuConfigService extends IBaseService { + + void addIotThingMenuConfig(IotThingMenuConfigDTO dto); + + IotThingMenuConfigDTO getIotThingMenuConfigExtendDTO(String menuId, String configType); + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/menu/service/impl/IotThingMenuConfigServiceImpl.java b/modules/thing/src/main/java/com/thing/device/menu/service/impl/IotThingMenuConfigServiceImpl.java new file mode 100644 index 0000000..7737273 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/menu/service/impl/IotThingMenuConfigServiceImpl.java @@ -0,0 +1,51 @@ +package com.thing.device.menu.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; +import com.thing.device.menu.mapper.IotThingMenuConfigMapper; +import com.thing.device.menu.service.IotThingMenuConfigService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.Objects; + +@Service +public class IotThingMenuConfigServiceImpl extends BaseServiceImpl implements IotThingMenuConfigService { + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + // wrapper.eq(IotThingMenuConfigEntity::getTenantCode, TenantContext.getTenantCode(SecurityUser.getUser())); + wrapper.eq(IotThingMenuConfigEntity::getTenantCode, UserContext.getRealTenantCode()); + return wrapper; + } + + + @Override + public void addIotThingMenuConfig(IotThingMenuConfigDTO iotThingMenuConfigDTO) { + IotThingMenuConfigDTO iotThingMenuConfigExtendDTO = getIotThingMenuConfigExtendDTO(iotThingMenuConfigDTO.getMenuId(), iotThingMenuConfigDTO.getConfigType()); + if (!Objects.isNull(iotThingMenuConfigExtendDTO)) { + throw new SysException("不能重复添加菜单配置:" + iotThingMenuConfigExtendDTO.getMenuId()); + } + saveDto(iotThingMenuConfigDTO); + } + + + @Override + public IotThingMenuConfigDTO getIotThingMenuConfigExtendDTO(String menuId, String configType) { + return mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingMenuConfigEntity::getMenuId, menuId, StringUtils.isNotBlank(menuId)) + .eq(IotThingMenuConfigEntity::getConfigType, configType, StringUtils.isNotBlank(configType)) + .eq(IotThingMenuConfigEntity::getTenantCode, UserContext.getRealTenantCode()) + , IotThingMenuConfigDTO.class); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/source/controller/IotThingSourceController.java b/modules/thing/src/main/java/com/thing/device/source/controller/IotThingSourceController.java new file mode 100644 index 0000000..acd4939 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/controller/IotThingSourceController.java @@ -0,0 +1,137 @@ +package com.thing.device.source.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.device.source.dto.*; +import com.thing.device.source.param.IotThingSourceParamDTO; +import com.thing.device.source.service.IotThingSourceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +@RestController +@RequestMapping("device/iotthingsource") +@Tag(name = "监控分析、用量分析设置管理模块") +@RequiredArgsConstructor +public class IotThingSourceController { + + private final IotThingSourceService iotThingSourceService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "fromId", description = "源物id"), + @Parameter(name = "thingId", description = "目标物id"), + @Parameter(name = "rootId", description = "根主键"), + @Parameter(name = "configType", description = "配置类型 例如:监控分析配置,用量分析配置"), + @Parameter(name = "thingAttrCodeType", description = "物属性code类型(yy mm hh)"), + @Parameter(name = "dataTreatingMark", description = "是否为数据处理") + }) + public Result> page(@RequestParam Map params) { + PageData page = iotThingSourceService.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "fromId", description = "源物id"), + @Parameter(name = "thingId", description = "目标物id"), + @Parameter(name = "rootId", description = "根主键"), + @Parameter(name = "configType", description = "配置类型 例如:监控分析配置,用量分析配置"), + @Parameter(name = "thingAttrCodeType", description = "物属性code类型(yy mm hh)"), + @Parameter(name = "dataTreatingMark", description = "是否为数据处理") + }) + public Result> list(@RequestParam Map params) { + List list = iotThingSourceService.list(params); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary = "单个数据源及子数据源列表") + public Result get(@PathVariable("id") Long id) { + return new Result().ok(iotThingSourceService.findById(id)); + } + + @PostMapping + @Operation(summary = "保存/新增数据源") + @LogOperation("保存") + public Result save(@RequestBody IotThingSourceDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + iotThingSourceService.save(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingSourceDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + iotThingSourceService.update(dto); + return new Result<>(); + } + + + @PostMapping("batchSyncOrUpdate") + @Operation(summary = "批量新增(同步或者更新)数据源配置") + @LogOperation("批量新增数据源配置") + public Result batchSyncOrUpdate(@RequestBody IotThingSourceParamDTO params) { + ValidatorUtils.validateEntity(params, AddGroup.class); + iotThingSourceService.batchSyncOrUpdate(params); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + iotThingSourceService.delete(ids); + return new Result<>(); + } + + @PostMapping("copy") + @Operation(summary = "复制属性配置") + public Result copy(@RequestBody IotThingSourceParamDTO iotThingSourceDTO) { + iotThingSourceService.copy(iotThingSourceDTO); + return new Result<>(); + } + + @PostMapping("sourceDetail") + @Operation(summary = "属性详情列表") + public Result> sourceDetail(@RequestBody IotThingSourceReqDTO iotThingSourceDTO) { + List list = iotThingSourceService.sourceDetail(iotThingSourceDTO); + return new Result>().ok(list); + } + + @PostMapping("getAttrListByThingRelationDTONew") + @Operation(summary = "根据物关系id获取数据源属性列表(小程序用,无需rootId)") + public Result> getAttrListByThingRelationDTONew(@Validated @RequestBody IotThingSourceGetAttrDTO iotThingSourceGetAttrDTO) { + List attrList = iotThingSourceService.getAttrListByThingRelationDTONew(iotThingSourceGetAttrDTO); + return new Result>().ok(attrList); + } + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAddChildDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAddChildDTO.java new file mode 100644 index 0000000..2c1547d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAddChildDTO.java @@ -0,0 +1,17 @@ +package com.thing.device.source.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +@Data +@EqualsAndHashCode(callSuper = true) +public class IotThingSourceAddChildDTO extends IotThingSourceDTO { + + @Schema(description = "子数据源列表(可不传)") + private List childSourceList; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrExtendDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrExtendDTO.java new file mode 100644 index 0000000..2da58c2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrExtendDTO.java @@ -0,0 +1,48 @@ +package com.thing.device.source.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class IotThingSourceAttrExtendDTO implements Serializable { + + @Serial + private static final long serialVersionUID = -1763474679143629004L; + + @Schema(description = "属性相关配置入参") + private String jsonParams; + + @Schema(description = "物属性code") + @NotBlank(message ="物属性编码不能为空", groups = DefaultGroup.class) + private String thingAttrCode; + + @Schema(description = "物属性名称") + @NotBlank(message ="物属性名称不能为空", groups = DefaultGroup.class) + private String thingAttrName; + + //@NotBlank(message ="物属性类型不能为空", groups = DefaultGroup.class) + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + + @Schema(description = "组id") + private Long groupId; + @Schema(description = "颜色") + private String thingIcon; + + @Schema(description = "颜色") + private String thingAttrUnit; + + @Schema(description = "字典顺序") + private Integer sort; +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrRespDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrRespDTO.java new file mode 100644 index 0000000..39da39e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceAttrRespDTO.java @@ -0,0 +1,27 @@ +package com.thing.device.source.dto; + +import com.thing.common.orm.dto.BaseDTO; +import lombok.Data; + +@Data +public class IotThingSourceAttrRespDTO extends BaseDTO { + + private Long id; + + private Long thingId; + + private Long rootId; + + private String thingAttrName; + + private String thingAttrCode; + + private String timeHorizonType; + + private String thingAttrUnit; + + private String thingAttrBoundary; + + private Integer sort; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceDTO.java new file mode 100644 index 0000000..cace7d1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceDTO.java @@ -0,0 +1,123 @@ +package com.thing.device.source.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.groups.Default; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "监控分析、用量分析设置数据传输实体类") +public class IotThingSourceDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + @NotNull(message = "物主键不能为空", groups = Default.class) + @Schema(description = "物id") + private Long thingId; + @Schema(description = "物名称(数据源名称)") + @NotBlank(message = "数据源名称不能为空", groups = Default.class) + private String thingName; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物属性名称") + @NotBlank(message = "物属性名称不能为空", groups = Default.class) + private String thingAttrName; + @NotBlank(message = "物属性code不能为空", groups = Default.class) + @Schema(description = "物属性code") + private String thingAttrCode; + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + @Schema(description = "物属性数据类型") + private String thingAttrType; + @Schema(description = "物属性单位") + private String thingAttrUnit; + @Schema(description = "颜色") + private String thingIcon; + @Schema(description = "物序号(子数据源数据处理)") + private String thingSerial; + @Schema(description = "thingAttrIcon") + private String thingAttrIcon; + @Schema(description = "时间范围类型") + private String timeHorizonType; + @Schema(description = "物属性值范围(上限、下限、临界等)") + private String thingAttrBoundary; + @Schema(description = "采集频率") + private Long gatherFrequency; + @NotNull(message = "根主键不能为空", groups = Default.class) + @Schema(description = "根主键") + private Long rootId; + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + private String configType; + @Schema(description = "是否为数据处理") + private String dataTreatingMark; + @Schema(description = "数据处理(前端使用)") + private String dataDealConfig; + @Schema(description = "是否展示初值终值,0 展示 1 不展示") + private String showStatus; + @Schema(description = "序号") + private Long sort; + @Schema(description = "父级物id") + private Long fromId; + @Schema(description = "父级物code") + private String fromCode; + @Schema(description = "父级物名称") + private String fromName; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "拓展字段") + private String extendData; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "数据处理规则js") + private String dataRule; +// @JsonIgnore +// private Long thingTenantId; +// /** 批量新增 同步 或者 更新 功能 **/ +// @Schema(description = "物实体类型") +// private String thingType; +// @Schema(description = "物实体id(同步 或者 更新 ,属性复制 )") +// private List thingIds; +// @Schema(description = "分组名称") +// private List groupNames; +// @Schema(description = "物属性id集合") +// private List attrIds; +// @Schema(description = "物属性编码集合") +// private List attrCodes; +// @Schema(description = "是否同步-- true: 同步按钮 - 删除对应物在当前配置页下的所有数据后再新增; false: 更新按钮 - 不删除原先数据") +// private Boolean sync; +// /** 属性复制 功能 **/ +// @Schema(description = "主键 (属性复制功能)集合") +// private List thingSourceId; +// @Schema(description = "目标物集合") +// private List thingTreeDTOS; +// @Schema(description = "属性编码类型(hh,dd,mm,勾选几个传几个)") +// private List attrCodesTypes; + + + + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceGetAttrDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceGetAttrDTO.java new file mode 100644 index 0000000..042e09e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceGetAttrDTO.java @@ -0,0 +1,21 @@ +package com.thing.device.source.dto; + +import com.thing.common.orm.dto.BaseDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class IotThingSourceGetAttrDTO extends BaseDTO { + + @Schema(description = "配置类型") + private String configType; + + @Schema(description = "属性code类型") + private String thingAttrCodeType; + + @Schema(description = "物关系Id集合") + private List iotThingSourceRelationDTOList; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceListDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceListDTO.java new file mode 100644 index 0000000..9201efb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceListDTO.java @@ -0,0 +1,84 @@ +package com.thing.device.source.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.groups.Default; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "监控分析、用量分析设置数据传输实体类") +public class IotThingSourceListDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + @NotNull(message = "物主键不能为空", groups = Default.class) + @Schema(description = "物id") + private Long thingId; + @Schema(description = "物名称(数据源名称)") + @NotBlank(message = "数据源名称不能为空", groups = Default.class) + private String thingName; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物属性名称") + @NotBlank(message = "物属性名称不能为空", groups = Default.class) + private String thingAttrName; + @NotBlank(message = "物属性code不能为空", groups = Default.class) + @Schema(description = "物属性code") + private String thingAttrCode; + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + @Schema(description = "物属性数据类型") + private String thingAttrType; + @Schema(description = "物属性单位") + private String thingAttrUnit; + @Schema(description = "颜色") + private String thingIcon; + @Schema(description = "物序号(子数据源数据处理)") + private String thingSerial; + @Schema(description = "thingAttrIcon") + private String thingAttrIcon; + @Schema(description = "时间范围类型") + private String timeHorizonType; + @Schema(description = "物属性值范围(上限、下限、临界等)") + private String thingAttrBoundary; + @Schema(description = "采集频率") + private Long gatherFrequency; + @NotNull(message = "根主键不能为空", groups = Default.class) + @Schema(description = "根主键") + private Long rootId; + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + private String configType; + @Schema(description = "是否为数据处理") + private String dataTreatingMark; + @Schema(description = "数据处理(前端使用)") + private String dataDealConfig; + @Schema(description = "是否展示初值终值,0 展示 1 不展示") + private String showStatus; + @Schema(description = "序号") + private Long sort; + @Schema(description = "父级物id") + private Long fromId; + @Schema(description = "父级物code") + private String fromCode; + @Schema(description = "父级物名称") + private String fromName; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "拓展字段") + private String extendData; + @Schema(description = "数据处理规则js") + private String dataRule; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourcePidDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourcePidDTO.java new file mode 100644 index 0000000..9127241 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourcePidDTO.java @@ -0,0 +1,30 @@ +package com.thing.device.source.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +@EqualsAndHashCode(callSuper = true) +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "监控分析、用量分析设置数据传输实体类") +public class IotThingSourcePidDTO extends IotThingSourceDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "维护子父数据源关系id") + private Long pid; + + @JsonIgnore + private Long thingTenantId; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceRelationDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceRelationDTO.java new file mode 100644 index 0000000..fd40b91 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceRelationDTO.java @@ -0,0 +1,42 @@ +package com.thing.device.source.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.orm.dto.BaseDTO; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +public class IotThingSourceRelationDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 4311395265785093075L; + @Schema(description = "物id") + @NotNull(message = "物主键不能为空", groups = DefaultGroup.class) + private Long thingId; + + @Schema(description = "根主键") + @NotNull(message = "根主键不能为空", groups = DefaultGroup.class) + private Long rootId; + + @Schema(description = "物名称(数据源名称)") + private String thingName; + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "数据源id") + private Long fromId; + + @Schema(description = "数据源code") + private String fromCode; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceReqDTO.java b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceReqDTO.java new file mode 100644 index 0000000..c49ec1e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/dto/IotThingSourceReqDTO.java @@ -0,0 +1,37 @@ +package com.thing.device.source.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/11/22 11:21 + * Description: 数据源查询请求 + */ +@Data +@Accessors(chain = true) +public class IotThingSourceReqDTO { + + private Long thingId; + + private Long fromId; + + private Long rootId; + + private List fromIds; + + @Schema(description = "配置类型") + private String configType; + + @Schema(description = "属性code类型") + private String thingAttrCodeType; + + @Schema(description = "物关系Id集合") + private List iotThingSourceRelationDTOList; + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/entity/IotThingSourceEntity.java b/modules/thing/src/main/java/com/thing/device/source/entity/IotThingSourceEntity.java new file mode 100644 index 0000000..ffac98a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/entity/IotThingSourceEntity.java @@ -0,0 +1,86 @@ +package com.thing.device.source.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("iot_thing_source") +public class IotThingSourceEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + private Long thingId; + + private String thingName; + + private String thingCode; + + private String thingAttrName; + + private String thingAttrCode; + + private String thingAttrCodeType; + + private String thingAttrType; + + private String thingAttrUnit; + + private String thingIcon; + + private String thingSerial; + + private String thingAttrIcon; + + private String timeHorizonType; + + private String thingAttrBoundary; + + private Long gatherFrequency; + + private Long rootId; + + private String configType; + + private String dataTreatingMark; + + private String dataDealConfig; + + private String showStatus; + + private Long sort; + + private Long fromId; + + private String fromCode; + + private String fromName; + + private String remark; + + private String extendData; + +// private Long tenantCode; +// +// private Long companyId; +// +// private Long deptId; +// +// private Long creator; +// +// private Long createDate; +// +// private Long updater; +// +// private Long updateDate; + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/device/source/mapper/IotThingSourceMapper.java b/modules/thing/src/main/java/com/thing/device/source/mapper/IotThingSourceMapper.java new file mode 100644 index 0000000..bd0edb3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/mapper/IotThingSourceMapper.java @@ -0,0 +1,36 @@ +package com.thing.device.source.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.device.source.dto.IotThingSourceDTO; +import com.thing.device.source.dto.IotThingSourcePidDTO; +import com.thing.device.source.entity.IotThingSourceEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * @Author: yangYang + * @Description: + * @Date: Create in 14:55 2022/4/28 + */ +@Mapper +public interface IotThingSourceMapper extends PowerBaseMapper { + + /** + * 获取数据源列表 + * @param params 入参 + * @return list + */ + List getParentList(Map params); + /** + * 获取子数据源列表 + * + * @param params 入参 + * @return list + */ + List getChildList(Map params); + + List getRootIdsByThingIdAndConfigTypeAndTenantCode(@Param("thingId") long thingId, @Param("configType") String configType, @Param("tenantCode") Long tenantCode); +} diff --git a/modules/thing/src/main/java/com/thing/device/source/param/IotThingSourceParamDTO.java b/modules/thing/src/main/java/com/thing/device/source/param/IotThingSourceParamDTO.java new file mode 100644 index 0000000..b681682 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/param/IotThingSourceParamDTO.java @@ -0,0 +1,128 @@ +package com.thing.device.source.param; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.thing.relation.detail.dto.ThingTreeDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.groups.Default; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@Accessors(chain = true) +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "监控分析、用量分析设置数据传输实体类") +public class IotThingSourceParamDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + @Schema(description = "维护子父数据源关系id") + private Long pid; + @NotNull(message = "物主键不能为空", groups = Default.class) + @Schema(description = "物id") + private Long thingId; + @Schema(description = "物名称(数据源名称)") + @NotBlank(message = "数据源名称不能为空", groups = Default.class) + private String thingName; + @Schema(description = "物编码") + private String thingCode; + @Schema(description = "物属性名称") + @NotBlank(message = "物属性名称不能为空", groups = Default.class) + private String thingAttrName; + @NotBlank(message = "物属性code不能为空", groups = Default.class) + @Schema(description = "物属性code") + private String thingAttrCode; + @Schema(description = "物属性code类型(yy mm hh)") + private String thingAttrCodeType; + @Schema(description = "物属性数据类型") + private String thingAttrType; + @Schema(description = "物属性单位") + private String thingAttrUnit; + @Schema(description = "颜色") + private String thingIcon; + @Schema(description = "物序号(子数据源数据处理)") + private String thingSerial; + @Schema(description = "thingAttrIcon") + private String thingAttrIcon; + @Schema(description = "时间范围类型") + private String timeHorizonType; + @Schema(description = "物属性值范围(上限、下限、临界等)") + private String thingAttrBoundary; + @Schema(description = "采集频率") + private Long gatherFrequency; + @NotNull(message = "根主键不能为空", groups = Default.class) + @Schema(description = "根主键") + private Long rootId; + @Schema(description = "配置类型 例如:监控分析配置,用量分析配置") + private String configType; + @Schema(description = "是否为数据处理") + private String dataTreatingMark; + @Schema(description = "数据处理(前端使用)") + private String dataDealConfig; + @Schema(description = "是否展示初值终值,0 展示 1 不展示") + private String showStatus; + @Schema(description = "序号") + private Long sort; + @Schema(description = "父级物id") + private Long fromId; + @Schema(description = "父级物code") + private String fromCode; + @Schema(description = "父级物名称") + private String fromName; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "拓展字段") + private String extendData; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "数据处理规则js") + private String dataRule; + @JsonIgnore + private Long thingTenantId; + /** 批量新增 同步 或者 更新 功能 **/ + @Schema(description = "物实体类型") + private String thingType; + @Schema(description = "物实体id(同步 或者 更新 ,属性复制 )") + private List thingIds; + @Schema(description = "分组名称") + private List groupNames; + @Schema(description = "物属性id集合") + private List attrIds; + @Schema(description = "物属性编码集合") + private List attrCodes; + @Schema(description = "是否同步-- true: 同步按钮 - 删除对应物在当前配置页下的所有数据后再新增; false: 更新按钮 - 不删除原先数据") + private Boolean sync; + /** 属性复制 功能 **/ + @Schema(description = "主键 (属性复制功能)集合") + private List thingSourceId; + @Schema(description = "目标物集合") + private List thingTreeDTOS; + @Schema(description = "属性编码类型(hh,dd,mm,勾选几个传几个)") + private List attrCodesTypes; + + + + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/service/IotThingSourceService.java b/modules/thing/src/main/java/com/thing/device/source/service/IotThingSourceService.java new file mode 100644 index 0000000..0ddd580 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/service/IotThingSourceService.java @@ -0,0 +1,44 @@ +package com.thing.device.source.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.device.source.dto.*; +import com.thing.device.source.entity.IotThingSourceEntity; +import com.thing.device.source.param.IotThingSourceParamDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface IotThingSourceService extends IBaseService { + + PageData pageList(Map params); + + List list(Map params); + + List findAllByFromIdAndThingIdAndConfigType(Long fromId, List thingIds, String configType, Long rootId, Long tenantCode); + + List findAllByFromIdAndThingIdAndConfigTypeAndAttrCode(Long fromId, List thingIds, String configType, Long rootId, Collection attrCodes); + + List findAllByFromIdAndThingIdAndConfigType(List fromIds, List thingIds, String configType, List rootIds); + + IotThingSourceDTO findById(Long id); + + void save(IotThingSourceDTO iotThingSourceDTO); + + void update(IotThingSourceDTO iotThingSourceDTO); + + void batchSyncOrUpdate(IotThingSourceParamDTO params); + + void copy(IotThingSourceParamDTO iotThingSourceDTO); + + void delete(Long[] ids); + + List getParentList(Map sourceParams); + + List getChildList(Map param); + + List sourceDetail(IotThingSourceReqDTO iotThingSourceDTO); + + List getAttrListByThingRelationDTONew(IotThingSourceGetAttrDTO iotThingSourceGetAttrDTO); +} diff --git a/modules/thing/src/main/java/com/thing/device/source/service/impl/IotThingSourceServiceImpl.java b/modules/thing/src/main/java/com/thing/device/source/service/impl/IotThingSourceServiceImpl.java new file mode 100644 index 0000000..f8fcb92 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/service/impl/IotThingSourceServiceImpl.java @@ -0,0 +1,531 @@ +package com.thing.device.source.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.AttributeTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.device.menu.dto.IotThingMenuConfigDTO; +import com.thing.device.menu.entity.IotThingMenuConfigEntity; +import com.thing.device.menu.mapper.IotThingMenuConfigMapper; +import com.thing.device.source.dto.*; +import com.thing.device.source.entity.IotThingSourceEntity; +import com.thing.device.source.mapper.IotThingSourceMapper; +import com.thing.device.source.param.IotThingSourceParamDTO; +import com.thing.device.source.service.IotThingSourceService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.relation.detail.dto.ThingTreeDTO; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.max; +import static com.thing.device.source.entity.table.IotThingSourceEntityTableDef.IOT_THING_SOURCE_ENTITY; + +@Service +@RequiredArgsConstructor +public class IotThingSourceServiceImpl extends BaseServiceImpl implements IotThingSourceService { + + private final ThingManageContextService thingManageContextService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper queryWrapper = new QueryWrapper(); + + Long thingId = MapUtil.getLong(params, "thingId"); + Long rootId = MapUtil.getLong(params, "rootId"); + Long fromId = MapUtil.getLong(params, "fromId"); + String configType = MapUtil.getStr(params, "configType"); + String thingAttrCodeType = MapUtil.getStr(params, "thingAttrCodeType"); + String dataTreatingMark = MapUtil.getStr(params, "dataTreatingMark"); + queryWrapper + .eq(IotThingSourceEntity::getFromId, fromId, Objects::nonNull) + .eq(IotThingSourceEntity::getThingId, thingId, Objects::nonNull) + .eq(IotThingSourceEntity::getRootId, rootId, Objects::nonNull) + .eq(IotThingSourceEntity::getConfigType, configType, StringUtils::isNotBlank) + .eq(IotThingSourceEntity::getThingAttrCodeType, thingAttrCodeType, StringUtils::isNotBlank) + .eq(IotThingSourceEntity::getDataTreatingMark, dataTreatingMark, StringUtils.isNotBlank(dataTreatingMark)); + return queryWrapper; + } + + @Override + public PageData pageList(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, "create_date", false); + Page paginate = mapper.paginateAs(new Page<>( + MapUtil.getInt(params, "page", 1), + MapUtil.getInt(params, "limit", 10)),queryWrapper,IotThingSourceListDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public List list(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, "create_date", false); + return mapper.selectListByQueryAs(queryWrapper, IotThingSourceDTO.class); + } + + @Override + public List findAllByFromIdAndThingIdAndConfigType(Long fromId, List thingIds, String configType, Long rootId, Long tenantCode) { + // 在httpContext中,tenantCode只能正确地取出一次,前面的逻辑取过,再取时会取到不期望的值,因此通过参数传递 + // 后期可以考虑在过滤器中通过requestWrapper改写相关逻辑 + Long realTenantCode = Optional.ofNullable(tenantCode).orElse(UserContext.getRealTenantCode()); + return mapper.selectListByQueryAs( + new QueryWrapper() + .eq(IotThingSourceEntity::getFromId, fromId, Objects::nonNull) + .in(IotThingSourceEntity::getThingId, thingIds, CollectionUtil.isNotEmpty(thingIds)) + .eq(IotThingSourceEntity::getConfigType, configType, StringUtils::isNotEmpty) + .eq(IotThingSourceEntity::getRootId, rootId, Objects::nonNull) + .eq(IotThingSourceEntity::getTenantCode, realTenantCode), IotThingSourceDTO.class); + } + + @Override + public List findAllByFromIdAndThingIdAndConfigTypeAndAttrCode(Long fromId, List thingIds, String configType, Long rootId, Collection attrCodes) { + return mapper.selectListByQueryAs( + new QueryWrapper() + .eq(IotThingSourceEntity::getFromId, fromId, Objects::nonNull) + .in(IotThingSourceEntity::getThingId, thingIds, CollectionUtil.isNotEmpty(thingIds)) + .eq(IotThingSourceEntity::getConfigType, configType, StringUtils::isNotEmpty) + .eq(IotThingSourceEntity::getRootId, rootId, Objects::nonNull) + .in(IotThingSourceEntity::getThingAttrCode, attrCodes, CollectionUtil.isNotEmpty(attrCodes)) + .eq(IotThingSourceEntity::getTenantCode, UserContext.getRealTenantCode()), IotThingSourceDTO.class); + } + + @Override + public List findAllByFromIdAndThingIdAndConfigType(List fromIds, List thingIds, String configType, List rootIds) { + return mapper.selectListByQueryAs( + new QueryWrapper() + .in(IotThingSourceEntity::getFromId, fromIds, CollectionUtil.isNotEmpty(fromIds)) + .in(IotThingSourceEntity::getThingId, thingIds, CollectionUtil.isNotEmpty(thingIds)) + .eq(IotThingSourceEntity::getConfigType, configType, StringUtils::isNotEmpty) + .in(IotThingSourceEntity::getRootId, rootIds, CollectionUtil.isNotEmpty(rootIds)) + .eq(IotThingSourceEntity::getTenantCode, UserContext.getRealTenantCode()), IotThingSourceDTO.class); + } + + @Override + public IotThingSourceDTO findById(Long id) { + return mapper.selectOneByQueryAs(new QueryWrapper().eq(IotThingSourceEntity::getId, id), IotThingSourceDTO.class); + } + + @Override + public void save(IotThingSourceDTO iotThingSourceDTO) { + List iotThingSourceDTOS = findAllByFromIdAndThingIdAndConfigTypeAndAttrCode(iotThingSourceDTO.getFromId(), + Collections.singletonList(iotThingSourceDTO.getThingId()), iotThingSourceDTO.getConfigType(), iotThingSourceDTO.getRootId(), + Collections.singletonList(iotThingSourceDTO.getThingAttrCode())); + if (CollectionUtil.isNotEmpty(iotThingSourceDTOS)) { + throw new SysException("同一个数据源下不允许重复添加相关属性数据:" + iotThingSourceDTO.getThingAttrCode()); + } + IotThingSourceEntity iotThingSourceEntity = ConvertUtils.sourceToTarget(iotThingSourceDTO, IotThingSourceEntity.class); + Optional optional = thingManageContextService.findEntityById(iotThingSourceDTO.getFromId()); + if (optional.isEmpty()) { + throw new SysException("当前主物不存在"); + } + iotThingSourceEntity.setFromCode(optional.get().getCode()).setFromName(optional.get().getName()); + mapper.insert(iotThingSourceEntity); + } + + @Override + public void update(IotThingSourceDTO iotThingSourceDTO) { + IotThingSourceEntity iotThingSourceEntity = mapper.selectOneById(iotThingSourceDTO.getId()); + if (Objects.isNull(iotThingSourceEntity)) { + throw new SysException("该源相关配置不存在"); + } + iotThingSourceEntity.setThingId(iotThingSourceDTO.getThingId()) + .setThingAttrCode(iotThingSourceDTO.getThingAttrCode()) + .setThingCode(iotThingSourceDTO.getThingCode()) + .setThingName(iotThingSourceDTO.getThingName()) + .setSort(iotThingSourceDTO.getSort()) + .setThingAttrName(iotThingSourceDTO.getThingAttrName()) + .setThingAttrCodeType(iotThingSourceDTO.getThingAttrCodeType()) + .setThingAttrBoundary(iotThingSourceDTO.getThingAttrBoundary()) + .setThingAttrUnit(iotThingSourceDTO.getThingAttrUnit()) + .setThingIcon(iotThingSourceDTO.getThingIcon()) + .setThingSerial(iotThingSourceDTO.getThingSerial()) + .setDataTreatingMark(iotThingSourceDTO.getDataTreatingMark()) + .setDataDealConfig(iotThingSourceDTO.getDataDealConfig()); + mapper.update(iotThingSourceEntity); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchSyncOrUpdate(IotThingSourceParamDTO params) { + List thingIds = params.getThingIds(); + if (CollectionUtil.isEmpty(thingIds)) { + throw new SysException("thingIds不能为空"); + } + //若没有选择物实体,则按照物类型筛选 + List subEntityDTOList = thingManageContextService.findEntityAllById(thingIds).orElseThrow(() -> new NoSuchElementException("Entity not found")); + if (CollectionUtil.isEmpty(subEntityDTOList)) { + throw new SysException("目标物不能为空"); + } + //若没有选择分组,则按照物字典进行筛选 + List groupNames = params.getGroupNames(); + List entityIds = subEntityDTOList.stream().map(IotThingEntityDTO::getId).toList(); + //查询当前物所有属性 + Optional> optionalList = thingManageContextService.findDictRelationAllByEntityIdsAndGroupNamesAndCodes(entityIds, groupNames,null); + if (optionalList.isEmpty()) { + throw new SysException("物指标关系不存在"); + } + List dictRelationDTOList = optionalList.get(); + List list; + //组装 source 数据: 遍历属性集合,过滤相应的物实体列表组装 source实体 + List attrCodesTypes = params.getAttrCodesTypes(); + if(CollectionUtils.isEmpty(attrCodesTypes)){ + if(CollectionUtils.isNotEmpty(params.getAttrCodes())){ + dictRelationDTOList = dictRelationDTOList.stream().filter(iotThingDictRelationDTO -> + CollectionUtils.containsAny(params.getAttrCodes(), iotThingDictRelationDTO.getCode())).toList(); + } + List finalDictRelationDTOList = dictRelationDTOList; + list = subEntityDTOList.stream() + .flatMap(entityDTO -> finalDictRelationDTOList.stream() + .filter(relation -> Objects.equals(relation.getEntityId(), entityDTO.getId())) + .map(relation -> { + // 创建一个新的 IotThingSourceEntity 对象 + IotThingSourceEntity iotThingSourceEntity = new IotThingSourceEntity() + .setThingId(relation.getEntityId()) + .setThingName(relation.getEntityName()) + .setThingCode(relation.getEntityCode()) + .setThingAttrType(relation.getDataType()) + .setThingAttrCode(relation.getCode()) + .setThingAttrName(relation.getName()) + .setThingAttrCodeType("") + .setThingAttrUnit(relation.getUnit()) + .setThingIcon(params.getThingIcon()) + // .setThingSerial(StringUtils.isBlank(params.getThingSerial()) ? String.valueOf(increment) : params.getThingSerial()) + .setThingAttrIcon(params.getThingAttrIcon()) + .setTimeHorizonType(params.getTimeHorizonType()) + .setThingAttrBoundary(params.getThingAttrBoundary()) + .setGatherFrequency(Objects.isNull(params.getGatherFrequency()) ? 900L : params.getGatherFrequency()) + .setRootId(params.getRootId()) + .setConfigType(params.getConfigType()) + .setDataTreatingMark("0") + .setDataDealConfig(params.getDataDealConfig()) + .setShowStatus(params.getShowStatus()) + // .setSort(increment) + .setFromId(entityDTO.getId()) + .setFromCode(entityDTO.getCode()) + .setFromName(entityDTO.getName()) + .setRemark(params.getRemark()) + .setExtendData(params.getExtendData()); + iotThingSourceEntity.setTenantCode(UserContext.getRealTenantCode()) + .setCompanyId(UserContext.getRealTenantCode()) + .setDeptId(UserContext.getRealTenantCode()); + return iotThingSourceEntity; + } + ) + ).toList(); + }else{ + List attrCodes = params.getAttrCodes(); + if(CollectionUtils.isNotEmpty(attrCodes)){ + List newAttrCodes = attrCodes.stream() + .flatMap(code -> attrCodesTypes.stream().map(attrCodesType -> AttributeTypeEnum.matchEndAdapter(code, attrCodesType)+attrCodesType))//用量为了添加attrCodesType + .toList(); + dictRelationDTOList = dictRelationDTOList.stream().filter(iotThingDictRelationDTO -> + CollectionUtils.containsAny(newAttrCodes, iotThingDictRelationDTO.getCode())).toList(); + } + List finalDictRelationDTOList = dictRelationDTOList; + list = subEntityDTOList.stream() + .flatMap(entityDTO -> finalDictRelationDTOList.stream() + .filter(relation -> Objects.equals(relation.getEntityId(), entityDTO.getId())) + .map(relation -> { + // 创建一个新的 IotThingSourceEntity 对象 + Optional first = attrCodesTypes.stream().filter(codeType -> StringUtils.endsWithIgnoreCase(relation.getCode(), codeType)).findFirst(); + IotThingSourceEntity iotThingSourceEntity = new IotThingSourceEntity() + .setThingId(relation.getEntityId()) + .setThingName(relation.getEntityName()) + .setThingCode(relation.getEntityCode()) + .setThingAttrType(relation.getDataType()) + .setThingAttrCode(relation.getCode()) + .setThingAttrName(relation.getName()) + .setThingAttrCodeType(first.orElse("")) + .setThingAttrUnit(relation.getUnit()) + .setThingIcon(params.getThingIcon()) + // .setThingSerial(StringUtils.isBlank(params.getThingSerial()) ? String.valueOf(increment) : params.getThingSerial()) + .setThingAttrIcon(params.getThingAttrIcon()) + .setTimeHorizonType(params.getTimeHorizonType()) + .setThingAttrBoundary(params.getThingAttrBoundary()) + .setGatherFrequency(Objects.isNull(params.getGatherFrequency()) ? 900L : params.getGatherFrequency()) + .setRootId(params.getRootId()) + .setConfigType(params.getConfigType()) + .setDataTreatingMark("0") + .setDataDealConfig(params.getDataDealConfig()) + .setShowStatus(params.getShowStatus()) + // .setSort(increment) + .setFromId(entityDTO.getId()) + .setFromCode(entityDTO.getCode()) + .setFromName(entityDTO.getName()) + .setRemark(params.getRemark()) + .setExtendData(params.getExtendData()); + iotThingSourceEntity.setTenantCode(UserContext.getRealTenantCode()) + .setCompanyId(UserContext.getRealTenantCode()) + .setDeptId(UserContext.getRealTenantCode()); + return iotThingSourceEntity; + } + ) + ).toList(); + } + //同步删除原先的配置 : 同步删除原先的配置, 更新 增量添加新配置 + if (params.getSync()) { + mapper.deleteByQuery( + new QueryWrapper() + .in(IotThingSourceEntity::getFromId, entityIds) + .eq(IotThingSourceEntity::getConfigType, params.getConfigType()) + .eq(IotThingSourceEntity::getRootId, params.getRootId()) + .eq(IotThingSourceEntity::getTenantCode, UserContext.getRealTenantCode()) + ); + } else { + List iotThingSourceDTOS = findAllByFromIdAndThingIdAndConfigType(entityIds, null, params.getConfigType(), Collections.singletonList(params.getRootId())); + if (CollectionUtil.isNotEmpty(iotThingSourceDTOS)) { + list = list.stream().filter(source -> iotThingSourceDTOS.stream().anyMatch( + iotThingSourceEntity -> + !(iotThingSourceEntity.getThingId().equals(source.getThingId()) && + iotThingSourceEntity.getFromId().equals(source.getFromId()) && + iotThingSourceEntity.getConfigType().equals(source.getConfigType()) && + StringUtils.equals(iotThingSourceEntity.getThingAttrCode() + iotThingSourceEntity.getThingAttrCodeType(), + source.getThingAttrCode() + iotThingSourceEntity.getThingAttrCodeType()) + )) + ).toList(); + } + } + if (CollectionUtil.isNotEmpty(list)) { + Map> listMap = list.stream().collect(Collectors.groupingBy(IotThingSourceEntity::getFromId)); + listMap.forEach((k, v) -> { + //获取最大的sort + QueryWrapper queryWrapper = QueryWrapper + .create() + .select(max(IOT_THING_SOURCE_ENTITY.SORT)) + .eq(IotThingSourceEntity::getFromId, k) + .eq(IotThingSourceEntity::getConfigType, params.getConfigType()) + .eq(IotThingSourceEntity::getRootId, params.getRootId()); + Long sort = mapper.selectOneByQueryAs(queryWrapper, Long.class); + AtomicLong maxSort = new AtomicLong(Objects.isNull(sort) ? 0L : sort); + v.forEach(source -> { + long increment = maxSort.incrementAndGet(); + source.setSort(increment); + source.setThingSerial(String.valueOf(increment)); + }); + }); + mapper.insertBatch(list); + } + } + + @Override + public void copy(IotThingSourceParamDTO iotThingSourceDTO) { + List thingSourceIds = iotThingSourceDTO.getThingSourceId(); + List iotThingSourceEntities = mapper.selectListByQuery( + new QueryWrapper() + .in(IotThingSourceEntity::getId, + thingSourceIds)); + if (CollectionUtil.isEmpty(iotThingSourceEntities)) { + throw new SysException("没有找到源数据记录"); + } + List thingTreeDTOS = iotThingSourceDTO.getThingTreeDTOS(); + if (CollectionUtil.isEmpty(thingTreeDTOS)) { + throw new SysException("没有找到复制目标数据记录"); + } + List thingIds = thingTreeDTOS.stream().map(ThingTreeDTO::getToId).toList(); + Optional> optionalList = thingManageContextService.findEntityAllById(thingIds); + if (optionalList.isEmpty()) { + throw new SysException("没有找到复制目标数据记录"); + } + List list = optionalList.get().stream() + .flatMap(iotThingEntityDTO -> iotThingSourceEntities.stream() + .map(iotThingSourceEntity -> new IotThingSourceEntity() + .setThingId(iotThingEntityDTO.getId()) + .setThingName(iotThingEntityDTO.getName()) + .setThingCode(iotThingEntityDTO.getCode()) + .setThingAttrName(iotThingSourceEntity.getThingAttrName()) + .setThingAttrType(iotThingSourceEntity.getThingAttrType()) + .setThingAttrCode(iotThingSourceEntity.getThingAttrCode()) + .setThingAttrCodeType(iotThingSourceEntity.getThingAttrCodeType()) + .setThingAttrUnit(iotThingSourceEntity.getThingAttrUnit()) + .setThingAttrIcon(iotThingSourceEntity.getThingAttrIcon()) + .setThingAttrBoundary(iotThingSourceEntity.getThingAttrBoundary()) + .setThingSerial(iotThingSourceEntity.getThingSerial()) + .setTimeHorizonType(iotThingSourceEntity.getTimeHorizonType()) + .setGatherFrequency(iotThingSourceEntity.getGatherFrequency()) + .setRootId(iotThingSourceEntity.getRootId()) + .setConfigType(iotThingSourceEntity.getConfigType()) + .setDataTreatingMark(iotThingSourceEntity.getDataTreatingMark()) + .setDataDealConfig(iotThingSourceEntity.getDataDealConfig()) + .setShowStatus(iotThingSourceEntity.getShowStatus()) + .setSort(iotThingSourceEntity.getSort()) + .setRemark(iotThingSourceEntity.getRemark()) + .setExtendData(iotThingSourceEntity.getExtendData()) + .setFromId(iotThingEntityDTO.getId()) + .setFromName(iotThingEntityDTO.getName()) + .setFromCode(iotThingEntityDTO.getCode()))) + .toList(); + if (CollectionUtil.isNotEmpty(list)) { + mapper.insertBatch(list); + } + } + + @Override + public void delete(Long[] ids) { + mapper.deleteByQuery( + new QueryWrapper() + .in(IotThingSourceEntity::getId, + Arrays.asList(ids), CollectionUtil.isNotEmpty(Arrays.asList(ids))) + ); + } + + @Override + public List getParentList(Map sourceParams) { + return mapper.getParentList(sourceParams); + } + + @Override + @DataFilter(completeDataMark = false, tableAlias = "itt") + public List getChildList(Map param) { + return mapper.getChildList(param); + } + + @Override + public List sourceDetail(IotThingSourceReqDTO iotThingSourceDTO) { + List iotThingSourceRelationDTOList = iotThingSourceDTO.getIotThingSourceRelationDTOList(); + if (CollectionUtil.isEmpty(iotThingSourceRelationDTOList)) { + return null; + } + List thingIds = iotThingSourceRelationDTOList.stream().map(IotThingSourceRelationDTO::getThingId).toList(); + List rootIds = iotThingSourceRelationDTOList.stream().map(IotThingSourceRelationDTO::getRootId).distinct().toList(); + + List iotThingSourceDTOS = mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingSourceEntity::getFromId, thingIds, CollectionUtil.isNotEmpty(thingIds)) + .in(IotThingSourceEntity::getRootId, rootIds, CollectionUtil.isNotEmpty(rootIds)) + .eq(IotThingSourceEntity::getConfigType, iotThingSourceDTO.getConfigType()) + .eq(IotThingSourceEntity::getThingAttrCodeType, iotThingSourceDTO.getThingAttrCodeType(), StringUtils.isNotBlank(iotThingSourceDTO.getThingAttrCodeType())) + .eq(IotThingSourceEntity::getTenantCode, UserContext.getRealTenantCode()) + .orderBy(IotThingSourceEntity::getSort,true) + , IotThingSourceDTO.class + ); + // 根据 thingAttrName 和 thingAttrCode 去重 + Set seen = new LinkedHashSet<>(); + return iotThingSourceDTOS.stream() + .filter(dto -> seen.add(dto.getThingAttrName() + "-" + dto.getThingAttrCode())) + .toList(); + } + + @Autowired + private IotThingMenuConfigMapper iotThingMenuConfigMapper; + + + @Override + public List getAttrListByThingRelationDTONew(IotThingSourceGetAttrDTO iotThingSourceGetAttrDTO) { + if (CollectionUtil.isEmpty(iotThingSourceGetAttrDTO.getIotThingSourceRelationDTOList())) { + return null; + } + //取类型的root配置 + List configTypes = new ArrayList<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + configTypes.add(iotThingSourceGetAttrDTO.getConfigType()); + List configList = iotThingMenuConfigMapper.selectListExt( + new QueryWrapper() + .in(IotThingMenuConfigEntity::getConfigType, configTypes) + .eq(IotThingMenuConfigEntity::getTenantCode, tenantCode)); + if (CollectionUtil.isEmpty(configList)){ + return null; + } + IotThingMenuConfigEntity entity = configList.get(0); + List menuConfigs = new ArrayList<>(); + if (StringUtils.isNotBlank(entity.getConfigType())){ + List newMenuConfigs = JSON.parseArray(entity.getMenuConfig(), IotThingMenuConfigDTO.MenuConfig.class); + if (CollectionUtil.isNotEmpty(newMenuConfigs)){ + for (IotThingMenuConfigDTO.MenuConfig menuConfig:newMenuConfigs){ +// if (menuConfig.getIsSelected()){ + menuConfigs.add(menuConfig); +// } + } + } + } + if (CollectionUtil.isEmpty(menuConfigs)){ + return null; + } + menuConfigs = menuConfigs.stream().sorted(Comparator.comparing(IotThingMenuConfigDTO.MenuConfig::getSort)).collect(Collectors.toList()); + //获取物对应的rootId + for (IotThingSourceRelationDTO sourceRelationDTO:iotThingSourceGetAttrDTO.getIotThingSourceRelationDTOList()){ + List rootIds = mapper.getRootIdsByThingIdAndConfigTypeAndTenantCode(sourceRelationDTO.getThingId(),iotThingSourceGetAttrDTO.getConfigType(),tenantCode); + for (IotThingMenuConfigDTO.MenuConfig menuConfig:menuConfigs){ + if (rootIds.contains(Long.parseLong(menuConfig.getId()))){ + sourceRelationDTO.setRootId(Long.parseLong(menuConfig.getId())); + break; + } + } + } + + List iotThingSourceList = getIotThingSourceListByRootAndThingIdWithAttrCode(iotThingSourceGetAttrDTO.getIotThingSourceRelationDTOList(), null, iotThingSourceGetAttrDTO.getConfigType(), iotThingSourceGetAttrDTO.getThingAttrCodeType()); + if (CollectionUtil.isEmpty(iotThingSourceList)) { + return null; + } + List iotThingSourceAttrList = ConvertUtils.sourceToTarget(iotThingSourceList, IotThingSourceAttrRespDTO.class); + return iotThingSourceAttrList.stream().filter(distinctByKey(IotThingSourceAttrRespDTO::getThingAttrCode)).sorted(Comparator.comparing(IotThingSourceAttrRespDTO::getSort)).collect(Collectors.toList()); + } + + /** + * 自定义函数去重 + * + * @param keyExtractor + * @param + * @return + */ + private Predicate distinctByKey(Function keyExtractor) { + Map seen = new ConcurrentHashMap<>(); + return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; + } + + + /** + * 数据源查询根据rootId和thingId集合以及属性集合和对应属性类型 + * + * @param iotThingSourceRelationList 关系集合 + * @param thingAttrCodeList 属性集合 + * @param configType 配置类型 + * @param thingAttrCodeType 属性code类型 + * @return + */ + @DataFilter + public List getIotThingSourceListByRootAndThingIdWithAttrCode(List iotThingSourceRelationList, + List thingAttrCodeList, + String configType, + String thingAttrCodeType) { + List thingIdList = iotThingSourceRelationList.stream().map(IotThingSourceRelationDTO::getThingId).collect(Collectors.toList()); + List rootIdList = iotThingSourceRelationList.stream().map(IotThingSourceRelationDTO::getRootId).collect(Collectors.toList()); + + List iotThingSourceEntityList = mapper.selectListExt(QueryWrapper.create() + // .in(CollectionUtil.isNotEmpty(thingIdList), IotThingSourceEntity::getThingId, thingIdList) + .in(IotThingSourceEntity::getFromId, thingIdList,CollectionUtil.isNotEmpty(thingIdList)) + .in(IotThingSourceEntity::getRootId, rootIdList,CollectionUtil.isNotEmpty(rootIdList)) + .in(IotThingSourceEntity::getThingAttrCode, thingAttrCodeList,CollectionUtil.isNotEmpty(thingAttrCodeList)) + .eq(IotThingSourceEntity::getThingAttrCodeType, thingAttrCodeType,StringUtils.isNotBlank(thingAttrCodeType)) + .eq(IotThingSourceEntity::getConfigType, configType,StringUtils.isNotBlank(configType)) + ); + + + return CollectionUtil.isEmpty(iotThingSourceEntityList) ? null : ConvertUtils.sourceToTarget(iotThingSourceEntityList, IotThingSourceDTO.class); + } + + + +} diff --git a/modules/thing/src/main/java/com/thing/device/source/thread/ThingSourceExecutor.java b/modules/thing/src/main/java/com/thing/device/source/thread/ThingSourceExecutor.java new file mode 100644 index 0000000..2ffd815 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/device/source/thread/ThingSourceExecutor.java @@ -0,0 +1,23 @@ +package com.thing.device.source.thread; + +import com.thing.common.util.thread.AbstractListeningExecutor; +import org.springframework.stereotype.Component; + +/** + * Author: SiYang + * Date: 2023/12/15 16:22 + * Description: 数据源执行器 + */ +@Component +public class ThingSourceExecutor extends AbstractListeningExecutor { + + @Override + public String getTaskName() { + return "thingSourceCreateTask"; + } + + @Override + protected int getThreadPollSize() { + return 10; + } +} diff --git a/modules/thing/src/main/java/com/thing/event/TenantDetailSavedEvent.java b/modules/thing/src/main/java/com/thing/event/TenantDetailSavedEvent.java new file mode 100644 index 0000000..f789d0e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/event/TenantDetailSavedEvent.java @@ -0,0 +1,28 @@ +package com.thing.event; + +import com.thing.sys.tenant.entity.SysTenantDetailEntity; + +import lombok.Getter; +import lombok.Setter; + +import org.springframework.context.ApplicationEvent; + +import java.io.Serial; + +/** + * @author siyang + * @date 2024/6/3 09:31 + * @description 租户存储事件 + */ +@Getter +@Setter +public class TenantDetailSavedEvent extends ApplicationEvent { + @Serial private static final long serialVersionUID = -8330363330704274977L; + + private SysTenantDetailEntity tenantDetail; + + public TenantDetailSavedEvent(Object source, SysTenantDetailEntity tenantDetail) { + super(source); + this.tenantDetail = tenantDetail; + } +} diff --git a/modules/thing/src/main/java/com/thing/event/ThingRelationDetailSaveEvent.java b/modules/thing/src/main/java/com/thing/event/ThingRelationDetailSaveEvent.java new file mode 100644 index 0000000..5b61d4c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/event/ThingRelationDetailSaveEvent.java @@ -0,0 +1,30 @@ +package com.thing.event; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/01 14:39 + * Description: 新增物关系子节点事件 + */ +@Getter +@Setter +public class ThingRelationDetailSaveEvent extends ApplicationEvent { + + private Long rootId; + + private List thingIds; + + private Long tenantCode; + + public ThingRelationDetailSaveEvent(Object source, Long rootId, List thingIds, Long tenantCode) { + super(source); + this.rootId = rootId; + this.thingIds = thingIds; + this.tenantCode = tenantCode; + } +} diff --git a/modules/thing/src/main/java/com/thing/event/ThingRelationImportEvent.java b/modules/thing/src/main/java/com/thing/event/ThingRelationImportEvent.java new file mode 100644 index 0000000..bf6465b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/event/ThingRelationImportEvent.java @@ -0,0 +1,25 @@ +package com.thing.event; + +import lombok.Getter; +import lombok.Setter; +import org.springframework.context.ApplicationEvent; + +/** + * Author: SiYang + * Date: 2023/12/01 14:39 + * Description: 物关系导入事件 + */ +@Getter +@Setter +public class ThingRelationImportEvent extends ApplicationEvent { + + private Long rootId; + + private Long tenantCode; + + public ThingRelationImportEvent(Object source, Long rootId, Long tenantCode) { + super(source); + this.rootId = rootId; + this.tenantCode = tenantCode; + } +} diff --git a/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendCalculationController.java b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendCalculationController.java new file mode 100644 index 0000000..8f432a0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendCalculationController.java @@ -0,0 +1,154 @@ +package com.thing.extend.controller; + +import com.google.common.collect.Lists; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.dto.TransportExtendDictDTO; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import com.thing.extend.service.TransportExtendCalculationService; +import com.thing.extension.modbus.ModbusFunction; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 通讯配置关联计算 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@RestController +@RequestMapping("transport/extend/calculation") +@Tag(name = "通讯配置关联计算") +public class TransportExtendCalculationController { + @Autowired + private TransportExtendCalculationService transportExtendCalculationService; + + @GetMapping("list") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = "configId",description ="配置主键"), + @Parameter(name = "configuration",description ="地址位") + }) + public Result> page( @RequestParam Map params) { + List list = transportExtendCalculationService.getList(params); + + return new Result>().ok(list); + } + + @GetMapping("list/write/{configId}") + @Operation(summary="获取写类型列表") + public Result> writeList(@PathVariable Long configId) { + List list = transportExtendCalculationService.getWriteList(Lists.newArrayList(configId), null); + return new Result>().ok(list); + } + + @GetMapping("type") + @Operation(summary="类型列表") + public Result> typeList() { + List data = Arrays.stream(ExtendRelationType.values()) + .map(item -> new TransportExtendDictDTO(item.name(), item.getName())) + .collect(Collectors.toList()); + return new Result>().ok(data); + } + + @GetMapping("modbus/func") + @Operation(summary="modbus方法列表") + public Result> funcList() { + List data = Arrays.stream(ModbusFunction.values()) + .map(item -> new TransportExtendDictDTO(String.valueOf(item.getRange()), item.getName())) + .collect(Collectors.toList()); + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + TransportExtendCalculationEntity data = transportExtendCalculationService.getById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody TransportExtendCalculationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + transportExtendCalculationService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody TransportExtendCalculationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + transportExtendCalculationService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + transportExtendCalculationService.removeById(ids); + + return new Result(); + } + + @GetMapping("debug/{id}") + @Operation(summary="开启/关闭调试模式") + @LogOperation("开启/关闭调试模式") + public Result debugMode(@PathVariable Long id) { + transportExtendCalculationService.debugMode(id); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出excel") + @Parameters({ + @Parameter(name = "configId",description ="配置主键"), + @Parameter(name = "configuration",description ="地址位") + }) + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + transportExtendCalculationService.exportExcel(Lists.newArrayList(ids), params, response); + } + + @PostMapping("import/{configId}") + @Operation(summary="导入excel") + public Result importJson(@PathVariable Long configId, MultipartFile file, HttpServletRequest request) { + transportExtendCalculationService.importExcel(configId, file, request); + return new Result(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendController.java b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendController.java new file mode 100644 index 0000000..d82c64b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendController.java @@ -0,0 +1,115 @@ +package com.thing.extend.controller; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.extend.dto.TransportExtendDTO; +import com.thing.extend.dto.TransportExtendDictDTO; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.service.TransportExtendService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + + +/** + * 通讯配置 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@RestController +@RequestMapping("transport/extend") +@Tag(name = "通讯配置") +public class TransportExtendController { + @Autowired + private TransportExtendService transportExtendService; + + @GetMapping("sidebar") + @Operation(summary="左侧列表") + @Parameter(name = "name",description ="名称") + public Result>> sidebar( @RequestParam Map params) { + Map> data = transportExtendService.sidebar(params); + + return new Result>>().ok(data); + } + + @GetMapping("type") + @Operation(summary="类型列表") + public Result> typeList() { + List data = transportExtendService.typeList(); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + TransportExtendEntity data = transportExtendService.getById(id); + + return new Result().ok(data); + } + + + @GetMapping("list") + @Operation(summary="列表") + public Result> list() { + List data = transportExtendService.listTransportExtendEntity(); + return new Result>().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody TransportExtendDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + transportExtendService.save(ConvertUtils.sourceToTarget(dto,TransportExtendEntity.class)); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody TransportExtendDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + transportExtendService.updateDto(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + transportExtendService.batchDelete(ids); + + return new Result(); + } + + @PostMapping("test") + @Operation(summary="连接测试") + public Result test(@RequestBody JsonNode params) { + transportExtendService.test(params); + return new Result(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendMsgController.java b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendMsgController.java new file mode 100644 index 0000000..8eaea60 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/controller/TransportExtendMsgController.java @@ -0,0 +1,89 @@ +package com.thing.extend.controller; + +import com.google.common.collect.Lists; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.extend.dto.TransportExtendMsgDTO; +import com.thing.extend.service.TransportExtendMsgService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + + +/** + * 通讯数据日志 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@RestController +@RequestMapping("transport/extend/msg") +@Tag(name = "通讯数据日志") +public class TransportExtendMsgController { + @Autowired + private TransportExtendMsgService transportExtendMsgService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "configId",description ="配置主键"), + @Parameter(name = "configuration",description ="地址位"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public Result> page( @RequestParam Map params) { + PageData page = transportExtendMsgService.pageList(params); + + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + transportExtendMsgService.removeById(ids); + + return new Result(); + } + + @GetMapping("clear") + @Operation(summary="清空") + @LogOperation("清空") + public Result clear() { + + transportExtendMsgService.clear(); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name = "configId",description ="配置主键"), + @Parameter(name = "configuration",description ="地址位"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + transportExtendMsgService.export(Lists.newArrayList(ids), params, response); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationDTO.java b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationDTO.java new file mode 100644 index 0000000..cec3a30 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationDTO.java @@ -0,0 +1,75 @@ +package com.thing.extend.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.*; +import io.swagger.v3.oas.annotations.media.Schema; + + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 通讯配置关联计算 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/@Data +@Schema( name= "通讯配置关联计算") +public class TransportExtendCalculationDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "通讯配置主键") + @NotNull(message="通讯配置不能为空", groups = DefaultGroup.class) + private Long configId; + @Schema(description = "配置") + @NotBlank(message="配置不能为空", groups = DefaultGroup.class) + private String configuration; + @Schema(description = "配置通道地址") + private String addr; + @Schema(description = "调试") + @NotNull(message="调试不能为空", groups = DefaultGroup.class) + private Boolean debug; + @Schema(description = "读、写、读写") + @NotBlank(message="类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "计算主键") + private Long calculationId; + @Schema(description = "原始数据") + private String inputMsg; + @Schema(description = "输出结果") + private String outputMsg; + @Schema(description = "错误信息") + private String errorMsg; + @Schema(description = "是否在线") + private Boolean online; + @Schema(description = "更新时间") + private Date lastDate; + @Schema(description = "创建者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationImport.java b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationImport.java new file mode 100644 index 0000000..2f4ea60 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendCalculationImport.java @@ -0,0 +1,55 @@ +package com.thing.extend.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-12-09 + **/ +@Data +@Schema( name= "通讯配置关联计算导入") +public class TransportExtendCalculationImport { + + @Schema(description = "通讯配置主键") + @NotNull(message = "通讯配置主键不能为空", groups = DefaultGroup.class) + private Long configId; + @Schema(description = "类型") + @NotBlank(message = "类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "关联计算列表") + @NotNull(message = "关联计算列表不能为空", groups = DefaultGroup.class) + private List extend; + @Schema(description = "计算列表") + private List calculation; + + @Data + public static class ExtendCalculation { + @Schema(description = "通道") + @NotBlank(message = "通道不能为空", groups = DefaultGroup.class) + private String configuration; + @Schema(description = "计算主键") + private Long calculationId; + @Schema(description = "类型") + @NotBlank(message = "类型不能为空", groups = DefaultGroup.class) + private String type; + } + + @Data + public static class Calculation { + @Schema(description = "计算主键") + private Long id; + @Schema(description = "计算名称") + private String name; + @Schema(description = "计算类型") + private String type; + @Schema(description = "计算方法体") + private String body; + } +} diff --git a/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDTO.java b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDTO.java new file mode 100644 index 0000000..ee1c14f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDTO.java @@ -0,0 +1,78 @@ +package com.thing.extend.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Null; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 通讯配置 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Data +@NoArgsConstructor +@Schema( name= "通讯配置") +public class TransportExtendDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema(description = "类型") + @NotBlank(message="类型不能为空", groups = DefaultGroup.class) + private String type; + @Schema(description = "名称") + @NotBlank(message="名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema(description = "配置") + @NotBlank(message="配置不能为空", groups = DefaultGroup.class) + private String configuration; + @Schema(description = "是否在线") + private Boolean online; + @Schema(description = "备注") + private String remark; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + + public TransportExtendDTO(Long id, String name, String remark, Boolean online) { + this.id = id; + this.name = name; + this.remark = remark; + this.online = online; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDictDTO.java b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDictDTO.java new file mode 100644 index 0000000..31cd55b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendDictDTO.java @@ -0,0 +1,29 @@ +package com.thing.extend.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** +* 通讯配置 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Data +@NoArgsConstructor +@AllArgsConstructor +@Schema( name= "通讯配置类型") +public class TransportExtendDictDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "类型值") + private String dictValue; + @Schema(description = "类型中文") + private String dictLabel; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendMsgDTO.java b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendMsgDTO.java new file mode 100644 index 0000000..1ab006f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/dto/TransportExtendMsgDTO.java @@ -0,0 +1,55 @@ +package com.thing.extend.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 通讯数据日志 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Data +@Schema( name= "通讯数据日志") +public class TransportExtendMsgDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "通讯配置主键") + private Long configId; + @Schema(description = "通讯配置关系主键") + private Long extendCalculationId; + @Schema(description = "原始数据") + private String inputMsg; + @Schema(description = "转换后数据") + private String outputMsg; + @Schema(description = "错误信息") + private String errorMsg; + @Schema(description = "配置") + private String configuration; + @Schema(description = "创建者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long creator; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "更新者") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Long updater; + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendCalculationEntity.java b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendCalculationEntity.java new file mode 100644 index 0000000..12aa9e1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendCalculationEntity.java @@ -0,0 +1,51 @@ +package com.thing.extend.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 通讯配置关联计算 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("transport_extend_calculation") +public class TransportExtendCalculationEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 通讯配置主键 + */ + private Long configId; + /** + * 配置 + */ + private String configuration; + /** + * 配置通道地址 + */ + private String addr; + /** + * 读、写、读写 + */ + private String type; + /** + * 计算主键 + */ + private Long calculationId; + /** + * 调试 + */ + private Boolean debug; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendEntity.java b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendEntity.java new file mode 100644 index 0000000..f817e29 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendEntity.java @@ -0,0 +1,44 @@ +package com.thing.extend.entity; + + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 通讯配置 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("transport_extend") +public class TransportExtendEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 类型 + */ + private String type; + /** + * 名称 + */ + private String name; + /** + * 配置 + */ + private String configuration; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendMsgEntity.java b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendMsgEntity.java new file mode 100644 index 0000000..4b9d397 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/entity/TransportExtendMsgEntity.java @@ -0,0 +1,77 @@ +package com.thing.extend.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 通讯数据日志 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("transport_extend_msg") +public class TransportExtendMsgEntity extends BaseDateEntity { + + @Serial + private static final long serialVersionUID = 8177745727649718517L; + /** + * 通讯配置主键 + */ + private Long configId; + /** + * 通讯配置关系主键 + */ + private Long extendCalculationId; + /** + * 原始数据 + */ + private String inputMsg; + /** + * 转换后数据 + */ + private String outputMsg; + /** + * 错误信息 + */ + private String errorMsg; + + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private Boolean debug; + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private Long ts; + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private Long tenantCode; + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private Long companyId; + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private Long deptId; + /** + * 订阅返回数据使用 + */ + @Column(ignore = true) + private String origin; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/excel/TransportExtendMsgExcel.java b/modules/thing/src/main/java/com/thing/extend/excel/TransportExtendMsgExcel.java new file mode 100644 index 0000000..2f01e75 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/excel/TransportExtendMsgExcel.java @@ -0,0 +1,25 @@ +package com.thing.extend.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2023-02-01 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TransportExtendMsgExcel { + @Excel(name = "通道") + private String config; + @Excel(name = "原始数据") + private String inputMsg; + @Excel(name = "输出结果") + private String outputMsg; + @Excel(name = "错误信息") + private String errorMsg; + @Excel(name = "更新时间") + private String createDate; +} diff --git a/modules/thing/src/main/java/com/thing/extend/excel/TransportModbusExcel.java b/modules/thing/src/main/java/com/thing/extend/excel/TransportModbusExcel.java new file mode 100644 index 0000000..6c2fc4e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/excel/TransportModbusExcel.java @@ -0,0 +1,35 @@ +package com.thing.extend.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2023-03-31 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TransportModbusExcel { + @Excel(name = "*slaveId") + private String slaveId; + @Excel(name = "*函数") + private String range; + @Excel(name = "*通道(地址位)") + private String offset; + @Excel(name = "*数据类型") + private String dataType; + @Excel(name = "注册计数") + private String registerCount; + @Excel(name = "*设备编号") + private String device; + @Excel(name = "*属性") + private String key; + @Excel(name = "*采集频率(s)") + private String polling; + @Excel(name = "*类型") + private String type; + @Excel(name = "数据解析") + private String calculationName; +} diff --git a/modules/thing/src/main/java/com/thing/extend/excel/TransportMqttExcel.java b/modules/thing/src/main/java/com/thing/extend/excel/TransportMqttExcel.java new file mode 100644 index 0000000..9be5a22 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/excel/TransportMqttExcel.java @@ -0,0 +1,21 @@ +package com.thing.extend.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2023-03-31 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TransportMqttExcel { + @Excel(name = "*通道") + private String topic; + @Excel(name = "*类型") + private String type; + @Excel(name = "数据解析") + private String calculationName; +} diff --git a/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendCalculationMapper.java b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendCalculationMapper.java new file mode 100644 index 0000000..fee995a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendCalculationMapper.java @@ -0,0 +1,16 @@ +package com.thing.extend.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 通讯配置关联计算 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Mapper +public interface TransportExtendCalculationMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMapper.java b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMapper.java new file mode 100644 index 0000000..0f8f26c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMapper.java @@ -0,0 +1,17 @@ +package com.thing.extend.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.extend.entity.TransportExtendEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 通讯配置 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Mapper +public interface TransportExtendMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMsgMapper.java b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMsgMapper.java new file mode 100644 index 0000000..ef50642 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/mapper/TransportExtendMsgMapper.java @@ -0,0 +1,41 @@ +package com.thing.extend.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.extend.dto.TransportExtendMsgDTO; +import com.thing.extend.entity.TransportExtendMsgEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Update; + +import java.util.List; +import java.util.Map; + +/** +* 通讯数据日志 +* +* @author zhh zhh +* @since 3.0 2022-12-05 +*/ +@Mapper +public interface TransportExtendMsgMapper extends PowerBaseMapper { + + /** + * 分页查询 + * @param params 查询参数 + * @return list + */ + List getList(Map params); + + /** + * 获取最新值 + * @param params 查询参数 + * @return list + */ + List getLastList(Map params); + + /** + * 清空表 + */ + @Update("truncate table transport_extend_msg") + void clear(); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/TransportExtendCalculationService.java b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendCalculationService.java new file mode 100644 index 0000000..21f5a99 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendCalculationService.java @@ -0,0 +1,80 @@ +package com.thing.extend.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 通讯配置关联计算 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +public interface TransportExtendCalculationService extends IBaseService { + + /** + * 列表查询 + * + * @param params 查询参数 + * @return list + */ + List getList(Map params); + + /** + * 删除 + * 调用前先销毁 Extension + * + * @param configIds 扩展主键 + */ + void deleteByConfigId(List configIds); + + /** + * 获取配置关系 + * + * @param configId 扩展主键 + * @return list + */ + List selectListByConfigId(Long configId); + + /** + * 导出excel + * + * @param idList 主键列表 + * @param params 查询参数 + * @param response response + */ + void exportExcel(List idList, Map params, HttpServletResponse response); + + /** + * 导入json + * + * @param configId 扩展主键 + * @param file excel文件 + * @param request request + */ + void importExcel(Long configId, MultipartFile file, HttpServletRequest request); + + /** + * 开启/关闭调试模式 + * + * @param id 主键 + */ + void debugMode(Long id); + + /** + * 获取写类型列表 + * + * @param configIdList 配置主键集合 + * @param idList 主键集合 + * @return list + */ + List getWriteList(List configIdList, List idList); + + void save(TransportExtendCalculationDTO dto); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/TransportExtendMsgService.java b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendMsgService.java new file mode 100644 index 0000000..7cff615 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendMsgService.java @@ -0,0 +1,60 @@ +package com.thing.extend.service; + + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.extend.dto.TransportExtendMsgDTO; +import com.thing.extend.entity.TransportExtendMsgEntity; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +/** + * 通讯数据日志 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +public interface TransportExtendMsgService extends IBaseService { + + /** + * 分页查询 + * @param params 查询参数 + * @return page + */ + PageData pageList(Map params); + + /** + * 获取最新值 + * @param extendCalculationIds 扩展计算关系主键 + * @return list + */ + List getLastList(List extendCalculationIds); + + /** + * 获取最新值 + * @param extendCalculationId 扩展计算关系主键 + * @return TransportExtendMsgEntity + */ + TransportExtendMsgEntity getLastByRelationId(Long extendCalculationId); + /** + * 导出列表 + * @param ids 主键 + * @param params 查询参数 + * @param response response + */ + void export(List ids, Map params, HttpServletResponse response); + + /** + * 清空日志 + */ + void clear(); + + /** + * 删除 + * @param configIdList 通讯主键 + */ + void deleteByConfigId(List configIdList); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/TransportExtendService.java b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendService.java new file mode 100644 index 0000000..a2f01a8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/TransportExtendService.java @@ -0,0 +1,52 @@ +package com.thing.extend.service; + + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.orm.service.IBaseService; +import com.thing.extend.dto.TransportExtendDTO; +import com.thing.extend.dto.TransportExtendDictDTO; +import com.thing.extend.entity.TransportExtendEntity; + +import java.util.List; +import java.util.Map; + +/** + * 通讯配置 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +public interface TransportExtendService extends IBaseService { + + /** + * 左侧列表 + * + * @param params 查询参数 + * @return list + */ + Map> sidebar(Map params); + + /** + * 类型列表 + * + * @return list + */ + List typeList(); + + /** + * 连接测试 + * + * @param params 参数 + */ + void test(JsonNode params); + + List listTransportExtendEntity(); + + /** + * 初始化扩展 + * + * @param extend 扩展 + * @param calculations 计算列表 + */ + //void initExtend(TransportExtendDTO extend, List calculations); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendCalculationServiceImpl.java b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendCalculationServiceImpl.java new file mode 100644 index 0000000..aab0318 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendCalculationServiceImpl.java @@ -0,0 +1,419 @@ +package com.thing.extend.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.control.entity.IotDeviceControlEntity; +import com.thing.control.mapper.IotDeviceControlMapper; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.entity.TransportExtendMsgEntity; +import com.thing.extend.excel.TransportModbusExcel; +import com.thing.extend.excel.TransportMqttExcel; +import com.thing.extend.mapper.TransportExtendCalculationMapper; +import com.thing.extend.mapper.TransportExtendMapper; +import com.thing.extend.service.TransportExtendCalculationService; +import com.thing.extend.service.TransportExtendMsgService; +import com.thing.extension.Extension; +import com.thing.extension.ExtensionManager; +import com.thing.extension.ExtensionType; +import com.thing.extension.ExtensionUtil; +import com.thing.extension.modbus.ModbusFunction; +import com.thing.modules.entity.ScriptInfoEntity; +import com.thing.modules.service.ScriptInfoService; +import com.thing.sys.biz.entity.DictData; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class TransportExtendCalculationServiceImpl extends BaseServiceImpl implements TransportExtendCalculationService { + + private final TransportExtendMsgService extendMsgService; + private final TransportExtendMapper transportExtendDao; + private final ScriptInfoService scriptInfoService; + private final ExtensionManager extensionManager; + private final IotDeviceControlMapper deviceControlDao; + private final SysDictTypeService dictTypeService; + private final ThingManageContextService thingManageContextService; + + @Override + public QueryWrapper getWrapper(Map params) { + Long configId = StringUtils.isNotBlank((String) params.get("configId")) ? Long.parseLong((String) params.get("configId")) : -1L; + String configuration = (String) params.get("configuration"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(TransportExtendCalculationEntity::getConfigId, configId) + .like(TransportExtendCalculationEntity::getAddr, configuration,StringUtils.isNotBlank(configuration)); + return wrapper; + } + + /** + * 列表查询 + * + * @param params 查询参数 + * @return list + */ + @Override + public List getList(Map params) { + List list = listAs(params, TransportExtendCalculationEntity.class); + List dtoList = ConvertUtils.sourceToTarget(list,TransportExtendCalculationDTO.class, ConvertUtils.TypeConvertEnum.Long_to_Date); + return dtoList.stream() + .peek( + item -> { + TransportExtendMsgEntity msg = extendMsgService.getLastByRelationId(item.getId()); + if (Objects.nonNull(msg)) { + item.setInputMsg(msg.getInputMsg()); + item.setOutputMsg(msg.getOutputMsg()); + item.setErrorMsg(msg.getErrorMsg()); + item.setLastDate(new Date(msg.getCreateDate())); + } + }) + .sorted( + Comparator.comparing(TransportExtendCalculationDTO::getCreateDate) + .reversed()) + .collect(Collectors.toList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(TransportExtendCalculationDTO dto) { + ExtensionType extensionType = validateConfiguration(dto); + TransportExtendCalculationEntity entity = ConvertUtils.sourceToTarget(dto, TransportExtendCalculationEntity.class); + save(entity); + //读修改、写移除 + if (ExtendRelationType.getRead().contains(dto.getType())) { + updateRelation(Lists.newArrayList(entity), extensionType); + } else { + deleteRelation(Lists.newArrayList(entity)); + } + } + + @Transactional(rollbackFor = Exception.class) + public void update(TransportExtendCalculationDTO dto) { + ExtensionType extensionType = validateConfiguration(dto); + if (StringUtils.equals(ExtendRelationType.READ.name(), dto.getType())) { + long selectCount = deviceControlDao.selectCountByQuery(QueryWrapper.create().eq(IotDeviceControlEntity::getRelationId, dto.getId())); + if (selectCount > 0) { + throw new SysException("已绑定设备控制,无法修改类型"); + } + } + super.updateDto(dto); + //读修改、写移除 + if (ExtendRelationType.getRead().contains(dto.getType())) { + updateRelation(Lists.newArrayList(ConvertUtils.sourceToTarget(dto, TransportExtendCalculationEntity.class)), extensionType); + } else { + deleteRelation(Lists.newArrayList(ConvertUtils.sourceToTarget(dto, TransportExtendCalculationEntity.class))); + } + } + + public void delete(Long[] ids) { + List idList = Arrays.asList(ids); + List calculationList = mapper.selectListByIds(idList); + batchDelete(ids); + deleteRelation(calculationList); + } + + /** + * 删除 + * 调用前先销毁 Extension + * + * @param configIds 扩展主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByConfigId(List configIds) { + if (CollectionUtil.isEmpty(configIds)) { + return; + } + mapper.deleteByQuery(QueryWrapper.create().in(TransportExtendCalculationEntity::getConfigId, configIds)); + } + + /** + * 获取配置关系 + * + * @param configId 扩展主键 + * @return list + */ + @Override + public List selectListByConfigId(Long configId) { + List list = mapper.selectListByQuery(QueryWrapper.create() + .eq(TransportExtendCalculationEntity::getConfigId, configId)); + return ConvertUtils.sourceToTarget(list, TransportExtendCalculationDTO.class); + } + + /** + * 开启/关闭调试模式 + * + * @param id 主键 + */ + @Override + public void debugMode(Long id) { + TransportExtendCalculationEntity entity = mapper.selectOneById(id); + if (Objects.isNull(entity)) { + throw new SysException("未找到数据"); + } + ExtensionType extensionType = ExtensionType.getTransportExtendType(entity.getType()); + if (Objects.isNull(extensionType)) { + throw new SysException("不支持的类型: " + entity.getType()); + } + //取反 + boolean debug = BooleanUtils.isNotTrue(entity.getDebug()); + entity.setDebug(debug); + mapper.update(entity); + updateRelation(Lists.newArrayList(entity), extensionType); + } + + /** + * 获取写类型列表 + * + * @param configIdList 配置主键集合 + * @param idList 主键集合 + * @return list + */ + @Override + public List getWriteList(List configIdList, List idList) { + List list = mapper.selectListByQuery(QueryWrapper.create() + .in(TransportExtendCalculationEntity::getConfigId, configIdList,CollectionUtil.isNotEmpty(configIdList)) + .in(TransportExtendCalculationEntity::getId, idList,CollectionUtil.isNotEmpty(idList)) + .in(TransportExtendCalculationEntity::getType, ExtendRelationType.getWrite())); + return ConvertUtils.sourceToTarget(list, TransportExtendCalculationDTO.class); + } + + @Override + public void exportExcel(List idList, Map params, HttpServletResponse response) { + String configId = (String) params.get("configId"); + if (StringUtils.isBlank(configId)) { + throw new SysException("配置主键不存在"); + } + TransportExtendEntity extendEntity = transportExtendDao.selectOneById(Long.parseLong(configId)); + if (Objects.isNull(extendEntity)) { + throw new SysException("通讯配置不存在"); + } + QueryWrapper wrapper = getWrapper(params); + wrapper.in(TransportExtendCalculationEntity::getId, idList, CollectionUtil.isNotEmpty(idList)); + List calculationEntityList = mapper.selectListByQuery(wrapper); + if (CollectionUtil.isEmpty(calculationEntityList)) { + throw new SysException("数据不存在"); + } + //数据解析 + List calculationIds = calculationEntityList.stream().map(TransportExtendCalculationEntity::getCalculationId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); + List calculationList = CollectionUtil.isEmpty(calculationIds) ? Lists.newArrayList() : scriptInfoService.selectBatchIds(calculationIds); + if (calculationIds.size() != calculationList.size()) { + throw new SysException("数据解析信息存在缺失"); + } + if (ExtensionType.MQTT_CLIENT.equals(ExtensionType.getTransportExtendType(extendEntity.getType()))) { + List collect = calculationEntityList.stream().map(item -> { + TransportMqttExcel excel = new TransportMqttExcel(); + excel.setType(ExtendRelationType.valueOf(item.getType()).getName()); + JsonNode jsonNode = JacksonUtil.toJsonNode(item.getConfiguration()); + excel.setTopic(jsonNode.get(ExtensionUtil.TOPIC).asText()); + if (Objects.nonNull(item.getCalculationId())) { + calculationList.stream() + .filter(calc -> Objects.equals(calc.getId(), item.getCalculationId())) + .findFirst() + .ifPresent(calc -> excel.setCalculationName(calc.getName())); + } + return excel; + }).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "通讯配置", null, TransportMqttExcel.class, "通讯配置列表.xls", response); + } else if (ExtensionType.MODBUS_TCP.equals(ExtensionType.getTransportExtendType(extendEntity.getType()))) { + //数据类型字典 + List dictList = dictTypeService.getDictDataByType("modbus_data_type"); + List collect = calculationEntityList.stream().map(item -> { + TransportModbusExcel excel = new TransportModbusExcel(); + excel.setType(ExtendRelationType.valueOf(item.getType()).getName()); + JsonNode jsonNode = JacksonUtil.toJsonNode(item.getConfiguration()); + excel.setSlaveId(jsonNode.get(ExtensionUtil.SLAVE_ID).asText()); + excel.setRange(ModbusFunction.getByRange(jsonNode.get(ExtensionUtil.RANGE).asInt()).getName()); + excel.setOffset(jsonNode.get(ExtensionUtil.OFFSET).asText()); + dictList.parallelStream() + .filter(dict -> StringUtils.equals(dict.getDictValue(), jsonNode.get(ExtensionUtil.DATA_TYPE).asText())) + .findFirst().ifPresent(dict -> excel.setDataType(dict.getDictLabel())); + excel.setRegisterCount(jsonNode.get(ExtensionUtil.REGISTER_COUNT).asText()); + excel.setDevice(jsonNode.get(ExtensionUtil.DEVICE).asText()); + excel.setKey(jsonNode.get(ExtensionUtil.KEY).asText()); + excel.setPolling(jsonNode.get(ExtensionUtil.POLLING).asText()); + if (Objects.nonNull(item.getCalculationId())) { + calculationList.stream() + .filter(calc -> Objects.equals(calc.getId(), item.getCalculationId())) + .findFirst() + .ifPresent(calc -> excel.setCalculationName(calc.getName())); + } + return excel; + }).collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, "通讯配置", null, TransportModbusExcel.class, "通讯配置列表.xls", response); + } else { + throw new SysException(extendEntity.getType() + "类型不支持"); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void importExcel(Long configId, MultipartFile file, HttpServletRequest request) { + TransportExtendEntity extendEntity = transportExtendDao.selectOneById(configId); + if (Objects.isNull(extendEntity)) { + throw new SysException("通讯配置不存在"); + } + List collect = Lists.newArrayList(); + ExtensionType extensionType = ExtensionType.getTransportExtendType(extendEntity.getType()); + if (ExtensionType.MQTT_CLIENT.equals(extensionType)) { + List sheetData = ExcelUtils.importExcel(file, 1, 1, TransportMqttExcel.class, 0); + if (CollectionUtil.isNotEmpty(sheetData)) { + List calcNames = sheetData.parallelStream().map(TransportMqttExcel::getCalculationName).filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList()); + List existList = scriptInfoService.selectByName(calcNames, extendEntity.getTenantCode(), extendEntity.getCompanyId(), extendEntity.getDeptId()); + collect.addAll(convertMqtt(configId, sheetData, existList)); + } + } else if (ExtensionType.MODBUS_TCP.equals(extensionType)) { + List sheetData = ExcelUtils.importExcel(file, 1, 1, TransportModbusExcel.class, 0); + if (CollectionUtil.isNotEmpty(sheetData)) { + List calcNames = sheetData.parallelStream().map(TransportModbusExcel::getCalculationName).filter(StringUtils::isNotBlank).distinct().collect(Collectors.toList()); + List existList = scriptInfoService.selectByName(calcNames, extendEntity.getTenantCode(), extendEntity.getCompanyId(), extendEntity.getDeptId()); + collect.addAll(Objects.requireNonNull(convertModbus(configId, sheetData, existList))); + } + } else { + throw new SysException(extendEntity.getType() + "类型不支持"); + } + //保存 + if (CollectionUtil.isNotEmpty(collect)) { + mapper.insertBatch(collect); + updateRelation(collect, extensionType); + } + } + + private List convertModbus(Long configId, List sheetData, List existList) { + //设备列表 + List thingCodes = sheetData.stream().map(TransportModbusExcel::getDevice).distinct().collect(Collectors.toList()); + List thingList = thingManageContextService.findEntityAllByCode(thingCodes, UserContext.getRealTenantCode(), true).orElse(Collections.emptyList()); + if (CollectionUtil.isEmpty(thingList)) { + throw new SysException("设备信息未找到"); + } + //数据类型字典 + List dictList = dictTypeService.getDictDataByType("modbus_data_type"); + //属性列表 + List thingIdList = thingList.stream().map(IotThingEntityDTO::getId).collect(Collectors.toList()); + Optional> optional = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(thingIdList, null); + if(optional.isEmpty()){ + return null; + } + List iotThingDictRelationDTOS = optional.get(); + Map> thingAttrMap = iotThingDictRelationDTOS.stream() + .collect(Collectors.groupingBy(IotThingDictRelationParamDTO::getEntityId,Collectors.mapping(IotThingDictRelationParamDTO::getCode,Collectors.toList()))); + return sheetData.parallelStream().map(sheet -> { + TransportExtendCalculationEntity entity = new TransportExtendCalculationEntity(); + entity.setConfigId(configId); + entity.setType(ExtendRelationType.getByName(sheet.getType())); + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.put(ExtensionUtil.SLAVE_ID, sheet.getSlaveId()); + jsonNodes.put(ExtensionUtil.RANGE, ModbusFunction.getByName(sheet.getRange())); + jsonNodes.put(ExtensionUtil.OFFSET, sheet.getOffset()); + jsonNodes.put(ExtensionUtil.DATA_TYPE, dictList.parallelStream() + .filter(dict -> StringUtils.equals(dict.getDictLabel(), sheet.getDataType())) + .findFirst().orElseThrow(() -> new SysException(sheet.getDataType() + "数据类型未匹配")).getDictValue()); + jsonNodes.put(ExtensionUtil.POLLING, sheet.getPolling()); + jsonNodes.put(ExtensionUtil.REGISTER_COUNT, StringUtils.defaultIfBlank(sheet.getRegisterCount(), "")); + jsonNodes.put(ExtensionUtil.WRITE, entity.getType()); + jsonNodes.put(ExtensionUtil.DEVICE, thingList.parallelStream() + .filter(item -> StringUtils.equals(item.getCode(), sheet.getDevice())) + .findFirst().orElseThrow(() -> new SysException(sheet.getDevice() + "设备未匹配")).getCode()); + if (!thingAttrMap.containsKey(sheet.getDevice()) || !thingAttrMap.get(sheet.getDevice()).contains(sheet.getKey())) { + throw new SysException(sheet.getKey() + "设备属性未匹配"); + } + jsonNodes.put(ExtensionUtil.KEY, sheet.getKey()); + entity.setConfiguration(JacksonUtil.toString(jsonNodes)); + ExtensionUtil.validateExtendRelation(ExtensionType.MODBUS_TCP.name(), ConvertUtils.sourceToTarget(entity, TransportExtendCalculationDTO.class)); + entity.setAddr(sheet.getOffset()); + entity.setDebug(false); + if (StringUtils.isNotBlank(sheet.getCalculationName())) { + ScriptInfoEntity scriptInfoEntity = existList.parallelStream() + .filter(script -> StringUtils.equals(script.getName(), sheet.getCalculationName())) + .findFirst().orElseThrow(() -> new SysException(sheet.getCalculationName() + "未匹配")); + entity.setCalculationId(scriptInfoEntity.getId()); + } + return entity; + }).collect(Collectors.toList()); + } + + private List convertMqtt(Long configId, List sheetData, List existList) { + return sheetData.parallelStream().map(sheet -> { + TransportExtendCalculationEntity entity = new TransportExtendCalculationEntity(); + entity.setConfigId(configId); + entity.setType(ExtendRelationType.getByName(sheet.getType())); + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.put(ExtensionUtil.TOPIC, sheet.getTopic()); + entity.setConfiguration(JacksonUtil.toString(jsonNodes)); + ExtensionUtil.validateExtendRelation(ExtensionType.MQTT_CLIENT.name(), ConvertUtils.sourceToTarget(entity, TransportExtendCalculationDTO.class)); + entity.setAddr(sheet.getTopic()); + entity.setDebug(false); + if (StringUtils.isNotBlank(sheet.getCalculationName())) { + ScriptInfoEntity scriptInfoEntity = existList.parallelStream() + .filter(script -> StringUtils.equals(script.getName(), sheet.getCalculationName())) + .findFirst().orElseThrow(() -> new SysException(sheet.getCalculationName() + "未匹配")); + entity.setCalculationId(scriptInfoEntity.getId()); + } + return entity; + }).collect(Collectors.toList()); + } + + private void deleteRelation(List calculationList) { + calculationList.stream() + .collect(Collectors.groupingBy(TransportExtendCalculationEntity::getConfigId, Collectors.mapping(TransportExtendCalculationEntity::getId, Collectors.toList()))) + .forEach((configId, list) -> { + Extension extension = extensionManager.getExtension(configId); + if (Objects.nonNull(extension)) { + extension.removeRelation(list); + } + }); + } + + private void updateRelation(List calculationList, ExtensionType extensionType) { + calculationList.stream() + //只有读类型才需要订阅 + .filter(item -> ExtendRelationType.getRead().contains(item.getType())) + .collect(Collectors.groupingBy(TransportExtendCalculationEntity::getConfigId)) + .forEach((configId, list) -> { + Extension extension = extensionManager.getExtension(configId); + if (Objects.nonNull(extension)) { + extension.updateRelation(ExtensionUtil.convertRelation(list, extensionType)); + } + }); + } + + /** + * 校验configuration + * + * @param dto dto + */ + private ExtensionType validateConfiguration(TransportExtendCalculationDTO dto) { + TransportExtendEntity extendEntity = transportExtendDao.selectOneById(dto.getConfigId()); + ExtensionType extensionType = ExtensionType.getTransportExtendType(extendEntity.getType()); + if (Objects.isNull(extensionType)) { + throw new SysException("不支持的类型: " + extendEntity.getType()); + } + ExtensionUtil.validateExtendRelation(extendEntity.getType(), dto); + return extensionType; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendMsgServiceImpl.java b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendMsgServiceImpl.java new file mode 100644 index 0000000..e74bd31 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendMsgServiceImpl.java @@ -0,0 +1,155 @@ +package com.thing.extend.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.extend.mapper.TransportExtendMapper; +import com.thing.extend.mapper.TransportExtendMsgMapper; +import com.thing.extend.dto.TransportExtendMsgDTO; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.entity.TransportExtendMsgEntity; +import com.thing.extend.excel.TransportExtendMsgExcel; +import com.thing.extend.service.TransportExtendMsgService; +import com.thing.extension.ExtensionType; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 通讯数据日志 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@Service +@RequiredArgsConstructor +public class TransportExtendMsgServiceImpl extends BaseServiceImpl implements TransportExtendMsgService { + + private final TransportExtendMapper transportExtendDao; + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + private void getParams(Map params) { + //转换成like + paramsToLike(params, "configuration"); + String configId = (String) params.get("configId"); + params.put("configId", StringUtils.isNotBlank(configId) ? Long.parseLong(configId) : -1L); + } + + /** + * 分页查询 + * + * @param params 查询参数 + * @return page + */ + @Override + public PageData pageList(Map params) { + getParams(params); + + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, "create_date", false) + ); + return getPageData(page, TransportExtendMsgDTO.class); + + } + + /** + * 获取最新值 + * + * @param extendCalculationIds 扩展计算关系主键 + * @return list + */ + @Override + public List getLastList(List extendCalculationIds) { + if (CollectionUtil.isEmpty(extendCalculationIds)) { + return Lists.newArrayList(); + } + Map params = Maps.newHashMapWithExpectedSize(1); + params.put("extendCalculationIds", extendCalculationIds); + return mapper.getLastList(params); + } + + /** + * 获取最新值 + * + * @param extendCalculationId 扩展计算关系主键 + * @return TransportExtendMsgEntity + */ + @Override + public TransportExtendMsgEntity getLastByRelationId(Long extendCalculationId) { + return mapper.selectOneByQuery(QueryWrapper.create() + .eq(TransportExtendMsgEntity::getExtendCalculationId, extendCalculationId) + .orderBy(TransportExtendMsgEntity::getCreateDate).desc().limit(1)); + } + + /** + * 清空日志 + */ + @Override + public void clear() { + mapper.clear(); + } + + /** + * 删除 + * + * @param configIdList 通讯主键 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByConfigId(List configIdList) { + if (CollectionUtil.isEmpty(configIdList)) { + return; + } + mapper.deleteByQuery(QueryWrapper.create().in(TransportExtendMsgEntity::getConfigId, configIdList)); + } + + /** + * 导出列表 + * + * @param ids 主键 + * @param params 查询参数 + * @param response response + */ + @Override + public void export(List ids, Map params, HttpServletResponse response) { + getParams(params); + if(CollectionUtil.isNotEmpty(ids)) { + params.put("ids", ids); + } + TransportExtendEntity extendEntity = transportExtendDao.selectOneById((Long) params.get("configId")); + if (Objects.isNull(extendEntity)) { + throw new SysException("未找到通讯配置信息"); + } + if (Objects.isNull(extendEntity.getType()) || Objects.isNull(ExtensionType.getTransportExtendType(extendEntity.getType()))) { + throw new SysException("暂不支持的导出类型: " + extendEntity.getType()); + } + List list = mapper.getList(params); + List collect = list.parallelStream() + .map(item -> new TransportExtendMsgExcel(item.getConfiguration(), item.getInputMsg(), item.getOutputMsg(), item.getErrorMsg(), DateUtil.formatDateTime(item.getCreateDate()))) + .sorted(Comparator.comparing(TransportExtendMsgExcel::getCreateDate).reversed()) + .collect(Collectors.toList()); + ExcelUtils.exportExcel(collect, extendEntity.getName() + "消息日志", null, TransportExtendMsgExcel.class, extendEntity.getName() + "消息日志.xls", response); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendServiceImpl.java b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendServiceImpl.java new file mode 100644 index 0000000..503ec61 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extend/service/impl/TransportExtendServiceImpl.java @@ -0,0 +1,155 @@ +package com.thing.extend.service.impl; + +import cn.hutool.core.collection.CollectionUtil; + +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.extend.dto.TransportExtendDTO; +import com.thing.extend.dto.TransportExtendDictDTO; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.mapper.TransportExtendMapper; +import com.thing.extend.service.TransportExtendCalculationService; +import com.thing.extend.service.TransportExtendMsgService; +import com.thing.extend.service.TransportExtendService; +import com.thing.extension.ExtensionManager; +import com.thing.extension.ExtensionProvider; +import com.thing.extension.ExtensionType; +import com.thing.extension.ExtensionUtil; + +import com.thing.sys.security.context.UserContext; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 通讯配置 + * + * @author zhh zhh + * @since 3.0 2022-12-05 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class TransportExtendServiceImpl extends BaseServiceImpl implements TransportExtendService { + + private final TransportExtendCalculationService calculationService; + private final TransportExtendMsgService extendMsgService; + private final ExtensionManager extensionManager; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like(TransportExtendEntity::getName, name,StringUtils.isNotBlank(name)); + wrapper.eq(TransportExtendEntity::getTenantCode, UserContext.getTenantCode()); + return wrapper; + } + + public void save(TransportExtendDTO dto) { + ExtensionUtil.validateExtend(dto.getType(), dto.getConfiguration()); + dto.setName(StringUtils.trim(dto.getName())); + super.saveDto(dto); + extensionManager.create(dto.getId()); + } + + public void update(TransportExtendDTO dto) { + TransportExtendEntity extendEntity = mapper.selectOneById(dto.getId()); + if (Objects.isNull(extendEntity)) { + throw new SysException("未找到数据"); + } + ExtensionUtil.validateExtend(dto.getType(), dto.getConfiguration()); + dto.setName(StringUtils.trim(dto.getName())); + super.updateDto(dto); + extensionManager.reload(dto.getId()); + } + + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + List configIdList = Arrays.asList(ids); + for (Long id : ids) { + extensionManager.destroy(id); + } + calculationService.deleteByConfigId(configIdList); + extendMsgService.deleteByConfigId(configIdList); + super.removeById(ids); + } + + /** + * 左侧列表 + * + * @param params 查询参数 + * @return list + */ + @Override + public Map> sidebar(Map params) { + List list = this.listAs(params,TransportExtendEntity.class); + Map statusMap = extensionManager.getExtensionStatus(); + return CollectionUtil.isEmpty(list) + ? Maps.newHashMap() + : list.stream() + .collect( + Collectors.groupingBy( + item -> ExtensionType.getName(item.getType()), + LinkedHashMap::new, + Collectors.mapping( + item -> + new TransportExtendDTO( + item.getId(), + item.getName(), + item.getRemark(), + BooleanUtils.isTrue(statusMap.get(item.getId()))), + Collectors.toList()))); + } + + /** + * 类型列表 + * + * @return list + */ + @Override + public List typeList() { + return Arrays.stream(ExtensionType.values()) + .map(item -> new TransportExtendDictDTO(item.name(), item.getName())) + .collect(Collectors.toList()); + } + + /** + * 连接测试 + * + * @param params 参数 + */ + @Override + public void test(JsonNode params) { + try { + JsonNode configuration = params.get("configuration"); + JsonNode type = params.get("type"); + if (Objects.isNull(configuration) || Objects.isNull(type)) { + throw new SysException("参数缺失"); + } + ExtensionUtil.validateExtend(type.asText(), configuration.toString()); + ExtensionProvider provider = extensionManager.getProvider(type.asText()) + .orElseThrow(() -> new SysException("不支持的类型" + type.asText())); + provider.testExtension(configuration); + } catch (Exception e) { + log.error("测试出现问题: {}", e.getMessage()); + throw new SysException("测试出现问题: " + e.getMessage()); + } + + } + @Override + public List listTransportExtendEntity() { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(TransportExtendEntity::getTenantCode, UserContext.getTenantCode()); + return this.list(wrapper); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/extension/DefaultExtensionManager.java b/modules/thing/src/main/java/com/thing/extension/DefaultExtensionManager.java new file mode 100644 index 0000000..83cf6c2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/DefaultExtensionManager.java @@ -0,0 +1,226 @@ +package com.thing.extension; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.util.thread.ThingThreadFactory; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import com.thing.extend.entity.TransportExtendEntity; +import com.thing.extend.mapper.TransportExtendCalculationMapper; +import com.thing.extend.mapper.TransportExtendMapper; +import com.thing.transport.api.TransportService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import javax.annotation.PreDestroy; +import java.util.*; +import java.util.concurrent.*; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2023-03-07 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class DefaultExtensionManager implements ExtensionManager, BeanPostProcessor, CommandLineRunner { + /** + * 扩展缓存 + */ + private final Map store = new ConcurrentHashMap<>(); + /** + * 扩展支持缓存 + */ + private final Map providerSupport = new ConcurrentHashMap<>(); + + private final TransportService transportService; + private final TransportExtendMapper extendDao; + private final TransportExtendCalculationMapper extendCalculationDao; + + private ScheduledFuture executorFuture; + private final ScheduledExecutorService schedulerExecutor = Executors.newSingleThreadScheduledExecutor(ThingThreadFactory.forName("extension-scheduler")); + + /** + * 获取扩展 + * + * @param id 扩展主键 + * @return 扩展对象 + */ + @Override + public Extension getExtension(Long id) { + return store.computeIfAbsent(id, (key) -> create(extendDao.selectOneById(id))); + } + + + /** + * 获取全部扩展 + * + * @return list + */ + @Override + public Collection getExtensions() { + return store.values(); + } + + /** + * 获取在线离线 + * + * @return map + */ + @Override + public Map getExtensionStatus() { + return store.values().stream().collect(Collectors.toMap(Extension::getId, Extension::online)); + } + + /** + * 获取全部扩展提供商 + * + * @return 提供商列表 + */ + @Override + public List getProviders() { + return new ArrayList<>(providerSupport.values()); + } + + /** + * 根据类型获取提供商 + * + * @param type 类型 + * @return 提供商 + */ + @Override + public Optional getProvider(String type) { + return Optional.ofNullable(providerSupport.get(type)); + } + + /** + * 创建 + * + * @param id 扩展主键 + */ + @Override + public void create(Long id) { + reload(id); + } + + /** + * 重新加载 + * + * @param id 扩展主键 + */ + @Override + public void reload(Long id) { + store.compute(id, (key, old) -> { + if (Objects.nonNull(old)) { + old.destroy(); + } + return create(extendDao.selectOneById(id)); + }); + } + + /** + * 销毁 + * + * @param id 扩展主键 + */ + @Override + public void destroy(Long id) { + Extension extension = store.get(id); + if (Objects.nonNull(extension)) { + extension.destroy(); + } + store.remove(id); + } + + /** + * 新建扩展 + * + * @param extendEntity 配置 + * @return Extension + */ + private Extension create(TransportExtendEntity extendEntity) { + if (Objects.isNull(extendEntity)) { + throw new SysException("数据未找到"); + } + ExtensionProvider provider = + getProvider(extendEntity.getType()) + .orElseThrow( + () -> new IllegalArgumentException(extendEntity.getType() + " unsupported")); + return provider.createExtension( + extendEntity.getId(), + new AuthParam( + extendEntity.getTenantCode(), + extendEntity.getCompanyId(), + extendEntity.getDeptId()), + JacksonUtil.toJsonNode(extendEntity.getConfiguration()), + getListByConfigId(extendEntity.getId(), extendEntity.getType()), + transportService); + } + + private void register(ExtensionProvider provider) { + this.providerSupport.put(provider.getType().name(), provider); + } + + @Override + public Object postProcessAfterInitialization(@Nonnull Object bean, @Nonnull String beanName) throws BeansException { + if (bean instanceof ExtensionProvider) { + register((ExtensionProvider) bean); + } + return bean; + } + //通讯配置,扫描配置的链接并加载 + @Override + @Order(99) + public void run(String... args) { + List list = extendDao.selectListByQuery(QueryWrapper.create()); + for (TransportExtendEntity extendEntity : list) { + try { + store.put(extendEntity.getId(), create(extendEntity)); + } catch (Exception e) { + log.error("通讯配置,扫描配置的链接并加载错误: ", e); + } + } + //15s检查 + executorFuture = schedulerExecutor.scheduleWithFixedDelay(() -> { + try { + store.values() + .parallelStream() + .filter(item -> !item.online()) + .forEach(item -> { + log.info("{} reconnect...", item.getId()); + reload(item.getId()); + }); + } catch (Exception e) { + log.error("mqttClient reconnect error :{}", e.getMessage()); + } + }, 30, 15, TimeUnit.SECONDS); + } + + @PreDestroy + private void destroy() { + if (this.executorFuture != null) { + this.executorFuture.cancel(false); + } + store.values().parallelStream().forEach(Extension::destroy); + } + + private List getListByConfigId(Long configId, String type) { + ExtensionType extensionType = ExtensionType.getTransportExtendType(type); + if (extensionType == null) { + throw new UnsupportedOperationException(type + "类型不支持"); + } + //获取所有读配置 + List list = extendCalculationDao.selectListByQuery(QueryWrapper.create() + .eq(TransportExtendCalculationEntity::getConfigId, configId) + .in(TransportExtendCalculationEntity::getType, ExtendRelationType.getRead())); + return ExtensionUtil.convertRelation(list, extensionType); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/Extension.java b/modules/thing/src/main/java/com/thing/extension/Extension.java new file mode 100644 index 0000000..9d0dfd9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/Extension.java @@ -0,0 +1,57 @@ +package com.thing.extension; + +import java.util.List; + +/** + * @author zhenghh. 2023-03-06 + **/ +public interface Extension { + + /** + * 通讯主键 + * + * @return id + */ + Long getId(); + + /** + * 获取通讯类型 + * + * @return 通讯类型 + */ + ExtensionType getType(); + + /** + * 是否在线 + * + * @return Boolean + */ + Boolean online(); + + /** + * 转换数据解析 + * + * @param relationList 通讯数据解析关系 + */ + void updateRelation(List relationList); + + /** + * 移除数据解析 + * + * @param relationIdList 通讯数据解析关系主键 + */ + void removeRelation(List relationIdList); + + /** + * 推送消息 + * + * @param publishMsg 推送信息 + * @throws Exception e + */ + void sendMsg(ExtensionPublishMsg publishMsg) throws Exception; + + /** + * 销毁 + */ + void destroy(); +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionJsonConverter.java b/modules/thing/src/main/java/com/thing/extension/ExtensionJsonConverter.java new file mode 100644 index 0000000..a787a2d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionJsonConverter.java @@ -0,0 +1,91 @@ +package com.thing.extension; + +import com.google.gson.JsonSyntaxException; +import com.google.protobuf.ByteString; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.event.AuthParam; +import com.thing.gen.queue.QueueProto; +import com.thing.gen.queue.QueueProto.TransportMsg; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 数据转换 + * + * @author zhenghh + */ +public class ExtensionJsonConverter { + + /** + * 扩展数据接收 + * + * @param id 扩展主键 + * @param payload 接收数据 + * @param ts 接收数据时间 + * @param originType 数据来源 + * @param relationList 数据关系集合 + * @param authParam 权限 + * @return TransportMsg + * @throws JsonSyntaxException e + */ + public static TransportMsg convertToExtensionProto(Long id, String payload, long ts, QueueOriginType originType, + List relationList, AuthParam authParam) { + List collect = relationList.stream() + .map(item -> { + QueueProto.ExtensionRelation.Builder builder = QueueProto.ExtensionRelation.newBuilder(); + builder.setRelationId(item.getRelationId()).setDebug(BooleanUtils.isTrue(item.getDebug())); + if (Objects.nonNull(item.getCalculationId())) { + builder.setCalculationId(item.getCalculationId()); + } + return builder.build(); + }).collect(Collectors.toList()); + return TransportMsg.newBuilder() + .setTransportExtensionMsg(QueueProto.ExtensionMsg.newBuilder() + .setPayload(ByteString.copyFrom(payload.getBytes())) + .setConfigId(id) + .setTs(ts) + .addAllRelation(collect)) + .setOrigin(originType.name()) + .setTenantCode(authParam.getTenantCode()) + .setCompanyId(authParam.getCompanyId()) + .setDeptId(authParam.getDeptId()) + .build(); + } + + + /** + * 扩展数据下发 + * + * @param id 扩展主键 + * @param publishMsg 下发数据 + * @param ts 接收数据时间 + * @param originType 数据来源 + * @param authParam 权限 + * @return TransportMsg + * @throws JsonSyntaxException e + */ + public static TransportMsg convertToControlProto(Long id, ExtensionPublishMsg publishMsg, long ts, QueueOriginType originType, AuthParam authParam) { + QueueProto.ControlMsg.Builder builder = QueueProto.ControlMsg.newBuilder() + .setConfigId(id) + .setTs(ts) + .setDebug(BooleanUtils.isTrue(publishMsg.getDebug())) + .setRelationId(publishMsg.getRelationId()) + .setInMsg(ByteString.copyFrom(publishMsg.getInMsg().getBytes())) + .setOutMsg(ByteString.copyFrom(publishMsg.getOutMsg().getBytes())); + if (StringUtils.isNotBlank(publishMsg.getErrorMsg())) { + builder.setErrorMsg(ByteString.copyFrom(publishMsg.getErrorMsg().getBytes())); + } + return TransportMsg.newBuilder() + .setControlMsg(builder) + .setOrigin(originType.name()) + .setTenantCode(authParam.getTenantCode()) + .setCompanyId(authParam.getCompanyId()) + .setDeptId(authParam.getDeptId()) + .build(); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionManager.java b/modules/thing/src/main/java/com/thing/extension/ExtensionManager.java new file mode 100644 index 0000000..95c2e19 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionManager.java @@ -0,0 +1,70 @@ +package com.thing.extension; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * @author zhenghh. 2023-03-07 + **/ +public interface ExtensionManager { + + /** + * 获取扩展 + * + * @param id 扩展主键 + * @return 扩展对象 + */ + Extension getExtension(Long id); + + /** + * 获取全部扩展 + * + * @return list + */ + Collection getExtensions(); + + /** + * 获取在线离线 + * + * @return map + */ + Map getExtensionStatus(); + + /** + * 获取全部扩展提供商 + * + * @return 提供商列表 + */ + List getProviders(); + + /** + * 根据类型获取提供商 + * + * @param type 类型 + * @return 提供商 + */ + Optional getProvider(String type); + + /** + * 创建 + * + * @param id 扩展主键 + */ + void create(Long id); + + /** + * 重新加载 + * + * @param id 扩展主键 + */ + void reload(Long id); + + /** + * 销毁 + * + * @param id 扩展主键 + */ + void destroy(Long id); +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionProvider.java b/modules/thing/src/main/java/com/thing/extension/ExtensionProvider.java new file mode 100644 index 0000000..47cbff8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionProvider.java @@ -0,0 +1,43 @@ +package com.thing.extension; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.event.AuthParam; +import com.thing.transport.api.TransportService; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * @author zhenghh. 2023-03-07 + **/ +public interface ExtensionProvider { + + /** + * 类型 + * + * @return 类型 + */ + @Nonnull + ExtensionType getType(); + + /** + * 创建通讯扩展 + * + * @param id 配置主键 + * @param authParam 企业信息 + * @param properties 配置参数 + * @param relationList 订阅参数 + * @param transportService 队列服务 + * @return Extension + */ + Extension createExtension(@Nonnull Long id, @Nonnull AuthParam authParam, @Nonnull JsonNode properties, + List relationList, TransportService transportService); + + /** + * 测试 + * + * @param properties 配置参数 + * @throws Exception e + */ + void testExtension(@Nonnull JsonNode properties) throws Exception; +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionPublishMsg.java b/modules/thing/src/main/java/com/thing/extension/ExtensionPublishMsg.java new file mode 100644 index 0000000..a7e3734 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionPublishMsg.java @@ -0,0 +1,50 @@ +package com.thing.extension; + +import com.fasterxml.jackson.databind.JsonNode; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 扩展下发消息 + * + * @author zhenghh. 2023-03-09 + **/ +@Data +public class ExtensionPublishMsg implements Serializable { + + + @Serial + private static final long serialVersionUID = -1417988154824396994L; + + /** + * 关系主键 + */ + private Long relationId; + + /** + * 下发地址配置 + */ + private JsonNode address; + + /** + * 调试 + */ + private Boolean debug; + + /** + * 数据解析入参 + */ + private String inMsg; + + /** + * 数据解析出参 最终下发的消息 + */ + private String outMsg; + + /** + * 错误参数 + */ + private String errorMsg; +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionRelation.java b/modules/thing/src/main/java/com/thing/extension/ExtensionRelation.java new file mode 100644 index 0000000..4d62f61 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionRelation.java @@ -0,0 +1,50 @@ +package com.thing.extension; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Objects; + +/** + * @author zhenghh. 2023-03-06 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ExtensionRelation implements Serializable { + + @Serial + private static final long serialVersionUID = 2399855507775699172L; + /** + * 扩展计算关系主键 + */ + private Long relationId; + /** + * 计算主键 + */ + private Long calculationId; + /** + * 是否调试 + */ + private Boolean debug; + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ExtensionRelation that = (ExtensionRelation) o; + return relationId.equals(that.relationId); + } + + @Override + public int hashCode() { + return Objects.hash(relationId); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionType.java b/modules/thing/src/main/java/com/thing/extension/ExtensionType.java new file mode 100644 index 0000000..df550d7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionType.java @@ -0,0 +1,40 @@ +package com.thing.extension; + +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @author zhenghh. 2023-03-07 + **/ +@Getter +public enum ExtensionType { + /** + * mqtt client + */ + MQTT_CLIENT("MQTT-Client"), + /** + * modbus + */ + MODBUS_TCP("MODBUS-TCP"); + + private final String name; + + ExtensionType(String name) { + this.name = name; + } + + public static String getName(String name) { + ExtensionType extensionType = getTransportExtendType(name); + return Objects.isNull(extensionType) ? "未知类型" : extensionType.getName(); + } + + public static ExtensionType getTransportExtendType(String name) { + return StringUtils.isBlank(name) ? null : + Arrays.stream(ExtensionType.values()) + .filter(item -> StringUtils.equals(item.name(), name)) + .findFirst().orElse(null); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/ExtensionUtil.java b/modules/thing/src/main/java/com/thing/extension/ExtensionUtil.java new file mode 100644 index 0000000..3240ab2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/ExtensionUtil.java @@ -0,0 +1,213 @@ +package com.thing.extension; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; +import com.serotonin.modbus4j.locator.BaseLocator; +import com.serotonin.modbus4j.locator.BinaryLocator; +import com.serotonin.modbus4j.locator.NumericLocator; +import com.serotonin.modbus4j.locator.StringLocator; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.entity.TransportExtendCalculationEntity; +import com.thing.extension.modbus.ModbusExtensionRelation; +import com.thing.extension.modbus.ModbusFunction; +import com.thing.extension.mqtt.MqttExtensionRelation; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.eclipse.paho.client.mqttv3.MqttTopic; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2023-03-13 + **/ +public class ExtensionUtil { + public static final String URI = "uri"; + public static final String TOPIC = "topic"; + public static final String USERNAME = "username"; + public static final String PASSWORD = "password"; + public static final String PORT = "port"; + /** + * modbus 设备编号 + */ + public static final String DEVICE = "device"; + /** + * modbus 属性编号 + */ + public static final String KEY = "key"; + /** + * modbus 函数代码 + * + * @see ModbusFunction + */ + public static final String RANGE = "range"; + /** + * modbus 地址标识 + */ + public static final String SLAVE_ID = "slaveId"; + /** + * modbus 通道(地址位) + */ + public static final String OFFSET = "offset"; + /** + * modbus 采集频率(秒) + */ + public static final String POLLING = "polling"; + /** + * modbus 数据类型(字典:1-bits; 2-16int; 3-16uint; 4-32int; 5-32uint; 8-32float; 10-64int; 11-64uint; 14-64float) + */ + public static final String DATA_TYPE = "dataType"; + /** + * modbus 注册计数(读取寄存器多少位数) + */ + public static final String REGISTER_COUNT = "registerCount"; + /** + * modbus 读写类型 + */ + public static final String WRITE = "write"; + + /** + * 校验扩展configuration + * + * @param type 类型 + * @param configuration dto + */ + public static void validateExtend(String type, String configuration) { + ExtensionType extensionType = ExtensionType.getTransportExtendType(type); + if (Objects.isNull(extensionType)) { + throw new SysException("不支持的类型" + type); + } + JsonNode jsonNode = JacksonUtil.toJsonNode(configuration); + JsonNode uri = jsonNode.get(URI); + if (Objects.isNull(uri) || StringUtils.isBlank(uri.asText())) { + throw new SysException("通讯地址不能为空"); + } + JsonNode port = jsonNode.get(PORT); + if (Objects.isNull(port) || StringUtils.isBlank(port.asText())) { + throw new SysException("端口不能为空"); + } + } + + /** + * 校验扩展关系configuration + * + * @param type 类型 + * @param dto dto + */ + public static void validateExtendRelation(String type, TransportExtendCalculationDTO dto) { + JsonNode jsonNode = JacksonUtil.toJsonNode(dto.getConfiguration()); + if (StringUtils.equals(type, ExtensionType.MQTT_CLIENT.name())) { + validate(jsonNode, TOPIC); + //校验topic是否合法 + String topicString = jsonNode.get(TOPIC).asText(); + try { + MqttTopic.validate(topicString, true); + } catch (IllegalArgumentException e) { + throw new SysException("通道" + topicString + "不合法"); + } + dto.setAddr(topicString); + } + if (StringUtils.equals(type, ExtensionType.MODBUS_TCP.name())) { + validate(jsonNode, DEVICE); + validate(jsonNode, KEY); + validate(jsonNode, RANGE); + validate(jsonNode, SLAVE_ID); + validate(jsonNode, OFFSET); + validate(jsonNode, POLLING); + validate(jsonNode, DATA_TYPE); + validate(jsonNode, WRITE); + dto.setAddr(jsonNode.get(OFFSET).asText()); + if (StringUtils.equals(ExtendRelationType.WRITE.name(), dto.getType()) ) { + ModbusFunction function = ModbusFunction.getByRange(jsonNode.get(RANGE).asInt()); + if(!ModbusFunction.CoilStatus.equals(function) && !ModbusFunction.HoldingRegister.equals(function)) { + throw new SysException(function.getName() + "不支持写入"); + } + } + } + } + + public static void validate(JsonNode configuration, String attr) { + JsonNode jsonNode = configuration.get(attr); + if (Objects.isNull(jsonNode) || jsonNode.isNull() || StringUtils.isBlank(jsonNode.asText())) { + throw new SysException("属性" + attr + "不能为空"); + } + } + + public static boolean compareConfig(String type, JsonNode relationConfig, String addr) { + if (StringUtils.equals(type, ExtensionType.MQTT_CLIENT.name())) { + return StringUtils.equals(addr, relationConfig.get(TOPIC).asText()); + } else if (StringUtils.equals(type, ExtensionType.MODBUS_TCP.name())) { + return StringUtils.equals(addr, relationConfig.get(OFFSET).asText()); + } + return false; + } + + public static List convertRelation(List list, ExtensionType extensionType) { + if (CollectionUtil.isEmpty(list)) { + return Lists.newArrayList(); + } + return list.parallelStream() + .map(item -> { + JsonNode jsonNode = JacksonUtil.toJsonNode(item.getConfiguration()); + return switch (extensionType) { + case MQTT_CLIENT -> + new MqttExtensionRelation(item.getId(), item.getCalculationId(), item.getDebug(), jsonNode.get(TOPIC).asText()); + case MODBUS_TCP -> + new ModbusExtensionRelation(item.getId(), item.getCalculationId(), item.getDebug(), jsonNode.get(DEVICE).asText(), + jsonNode.get(KEY).asText(), jsonNode.get(POLLING).asLong(), getBaseLocator(jsonNode, ExtendRelationType.valueOf(item.getType()))); + }; + }).collect(Collectors.toList()); + } + + public static BaseLocator getBaseLocator(JsonNode jsonNode, ExtendRelationType relationType) { + try { + int slaveId = jsonNode.get(SLAVE_ID).asInt(); + int range = jsonNode.get(RANGE).asInt(); + int dataType = jsonNode.get(DATA_TYPE).asInt(); + int offset = jsonNode.get(OFFSET).asInt(); + JsonNode registerCountJson = jsonNode.get(REGISTER_COUNT); + int registerCount = Objects.isNull(registerCountJson) || StringUtils.isBlank(registerCountJson.asText()) ? -1 : registerCountJson.asInt(); + + if (range > 0 && range < 5 && dataType > 0 && dataType < 34) { + if (dataType == 1) { + if(ExtendRelationType.WRITE.equals(relationType)) { + return new BinaryLocator(slaveId, range, offset); + } + return registerCount < 0 ? new BinaryLocator(slaveId, range, offset) : new BinaryLocator(slaveId, range, offset, registerCount); + } else if (dataType == 18 || dataType == 19) { + if (registerCount < 0 || registerCount > 15) { + throw new SysException("数据类型为char或varchar时, 注册计数必填且范围在0~15之间"); + } + return new StringLocator(slaveId, range, offset, dataType, registerCount, StandardCharsets.UTF_8); + } else { + return new NumericLocator(slaveId, range, offset, dataType); + } + } + return null; + } catch (Exception e) { + throw new SysException("数据异常: " + e.getMessage()); + } + } + + public static Object getMsg(JsonNode config, String inMsg) { + int range = config.get(ExtensionUtil.RANGE).asInt(); + int dataType = config.get(ExtensionUtil.DATA_TYPE).asInt(); + ModbusFunction modbusFunction = ModbusFunction.getByRange(range); + switch (modbusFunction) { + case CoilStatus: + return Boolean.parseBoolean(inMsg); + case HoldingRegister: + return (dataType == 18 || dataType == 19) ? inMsg : NumberUtils.createNumber(inMsg); + case InputStatus: + case InputRegisters: + default: + throw new SysException(modbusFunction.getName() + "不支持写入"); + } + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusConnectOptions.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusConnectOptions.java new file mode 100644 index 0000000..7064d45 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusConnectOptions.java @@ -0,0 +1,18 @@ +package com.thing.extension.modbus; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ModbusConnectOptions { + + private String uri; + + private int port; +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtension.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtension.java new file mode 100644 index 0000000..de2a3d8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtension.java @@ -0,0 +1,216 @@ +package com.thing.extension.modbus; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.serotonin.modbus4j.ModbusMaster; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.exception.SysException; +import com.thing.common.util.thread.ThingThreadFactory; +import com.thing.extension.*; +import com.thing.gen.queue.QueueProto; +import com.thing.transport.api.TransportService; +import com.thing.transport.api.TransportServiceCallback; +import com.thing.transport.api.session.TransportSessionUtil; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZoneOffset; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Slf4j +public class ModbusExtension implements Extension { + + private final Long id; + private final AuthParam authParam; + private final TransportService transportService; + private final List relationList = new CopyOnWriteArrayList<>(); + + private ExecutorService schedulerExecutor; + private ModbusMaster modbusMaster = null; + private boolean stop = false; + + public ModbusExtension(Long id, AuthParam authParam, ModbusConnectOptions modbusConnectOptions, + TransportService transportService, List relationList) { + this.id = id; + this.authParam = authParam; + this.transportService = transportService; + this.relationList.addAll(relationList); + this.schedulerExecutor = Executors.newSingleThreadExecutor(ThingThreadFactory.forName("transport-modbus")); + try { + this.modbusMaster = ModbusUtils.getTcpMaster(modbusConnectOptions.getUri(), modbusConnectOptions.getPort()); + //开始轮询 + stop = false; + startPolling(); + log.info("MODBUS-TCP: {}:{} 初始化成功", modbusConnectOptions.getUri(), modbusConnectOptions.getPort()); + } catch (Exception e) { + log.info("MODBUS-TCP: {}:{} 初始化失败", modbusConnectOptions.getUri(), modbusConnectOptions.getPort()); + } + } + + /** + * 通讯主键 + * + * @return id + */ + @Override + public Long getId() { + return id; + } + + /** + * 获取通讯类型 + * + * @return 通讯类型 + */ + @Override + public ExtensionType getType() { + return ExtensionType.MODBUS_TCP; + } + + /** + * 是否在线 + * + * @return Boolean + */ + @Override + public Boolean online() { + return Objects.nonNull(modbusMaster) && modbusMaster.isConnected(); + } + + /** + * 转换数据解析 + * + * @param relationList 通讯数据解析关系 + */ + @Override + public void updateRelation(List relationList) { + List updateList = this.relationList.stream().filter(relationList::contains).collect(Collectors.toList()); + this.relationList.removeAll(updateList); + //修改时间 + List saveList = updateTime(relationList.stream().filter(item -> !this.relationList.contains(item)).map(item -> (ModbusExtensionRelation) item).collect(Collectors.toList()), LocalDateTime.now()); + this.relationList.addAll(saveList); + } + + /** + * 移除数据解析 + * + * @param relationIdList 通讯数据解析关系主键 + */ + @Override + public void removeRelation(List relationIdList) { + List removeList = this.relationList.stream().filter(item -> relationIdList.contains(item.getRelationId())).collect(Collectors.toList()); + this.relationList.removeAll(removeList); + } + + /** + * 推送消息 + * + * @param publishMsg 推送信息 + * @throws Exception e + */ + @Override + public void sendMsg(ExtensionPublishMsg publishMsg) throws Exception { + if (publishMsg.getAddress() == null) { + throw new SysException("协议通道配置不存在"); + } + if (StringUtils.isBlank(publishMsg.getOutMsg())) { + throw new SysException("下发消息不存在"); + } + JsonNode address = publishMsg.getAddress(); + String write = address.get(ExtensionUtil.WRITE).asText(); + ExtendRelationType extendRelationType = ExtendRelationType.valueOf(write); + if (/*!ExtendRelationType.ALL.equals(extendRelationType) && */!ExtendRelationType.WRITE.equals(extendRelationType)) { + throw new SysException("请选择写类型协议通道"); + } + //写入 + ModbusUtils.write(this.modbusMaster, address, publishMsg.getOutMsg()); + //日志 + transportService.process(TransportSessionUtil.createSession(id.toString()), + ExtensionJsonConverter.convertToControlProto(id, publishMsg, System.currentTimeMillis(), QueueOriginType.MODBUS_TCP, authParam), + TransportServiceCallback.EMPTY); + log.info("MODBUS-TCP: {}-slaveId: {}-offset: {} publish message: {}", id, address.get(ExtensionUtil.SLAVE_ID).asInt(), address.get(ExtensionUtil.OFFSET).asInt(), publishMsg.getOutMsg()); + } + + /** + * 销毁 + */ + @Override + public void destroy() { + this.stop = true; + if (Objects.nonNull(this.schedulerExecutor)) { + this.schedulerExecutor.shutdown(); + this.schedulerExecutor = null; + } + if (Objects.nonNull(this.modbusMaster)) { + this.modbusMaster.setConnected(false); + this.modbusMaster.destroy(); + this.modbusMaster = null; + } + } + + + /** + * 开始轮询 + */ + private void startPolling() { + this.schedulerExecutor.submit(() -> { + while (!stop) { + try { + if (CollectionUtil.isNotEmpty(this.relationList)) { + //todo 改为异步 + LocalDateTime now = LocalDateTime.now(); + long nowTs = now.toEpochSecond(ZoneOffset.ofHours(8)); + List modbusList = this.relationList.parallelStream() + .map(item -> (ModbusExtensionRelation) item).collect(Collectors.toList()); + //时间相等 + List collect = modbusList.parallelStream() + .filter(item -> Objects.equals(nowTs, item.getNextTime()) && Objects.nonNull(item.getBaseLocator())).collect(Collectors.toList()); + //修改时间 + updateTime(modbusList, now); + if (CollectionUtil.isNotEmpty(collect)) { + long milliTs = nowTs * 1000; + collect.parallelStream().forEach(item -> { + QueueProto.TransportMsg transportMsg = ModbusUtils.readValue(id, modbusMaster, milliTs, item, authParam); + if(Objects.nonNull(transportMsg)) { + transportService.process(TransportSessionUtil.createSession(item.getRelationId().toString()), transportMsg, TransportServiceCallback.EMPTY); + } + }); + } + } + TimeUnit.MILLISECONDS.sleep(500); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + private List updateTime(List relationList, LocalDateTime nowDateTime) { + long nowTs = nowDateTime.toEpochSecond(ZoneOffset.ofHours(8)); + return relationList.parallelStream() + .peek(extensionRelation -> { + if (Objects.nonNull(extensionRelation.getNextTime()) && extensionRelation.getNextTime() <= nowTs) { + extensionRelation.setNextTime(extensionRelation.getNextTime() + extensionRelation.getPolling()); + } else { + LocalDateTime nowHourTime = nowDateTime.withMinute(0).withSecond(0).withNano(0); + long second = nowHourTime.toEpochSecond(ZoneOffset.ofHours(8)); + Duration duration = Duration.between(nowHourTime, nowDateTime); + long nextTime = second + (duration.toSeconds() / extensionRelation.getPolling() * extensionRelation.getPolling()); + extensionRelation.setNextTime(nextTime >= nowDateTime.toEpochSecond(ZoneOffset.ofHours(8)) ? nextTime : nextTime + extensionRelation.getPolling()); + } + }).collect(Collectors.toList()); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionProvider.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionProvider.java new file mode 100644 index 0000000..4073f43 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionProvider.java @@ -0,0 +1,75 @@ +package com.thing.extension.modbus; + +import com.fasterxml.jackson.databind.JsonNode; +import com.serotonin.modbus4j.ModbusMaster; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.exception.SysException; +import com.thing.extension.*; +import com.thing.transport.api.TransportService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.Objects; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Component +public class ModbusExtensionProvider implements ExtensionProvider { + /** + * 类型 + * + * @return 类型 + */ + @Nonnull + @Override + public ExtensionType getType() { + return ExtensionType.MODBUS_TCP; + } + + /** + * 创建通讯扩展 + * + * @param id 配置主键 + * @param authParam 企业信息 + * @param properties 配置参数 + * @param relationList 订阅参数 + * @param transportService 队列服务 + * @return Extension + */ + @Override + public Extension createExtension(@Nonnull Long id, @Nonnull AuthParam authParam, @Nonnull JsonNode properties, List relationList, TransportService transportService) { + return new ModbusExtension(id, authParam, getModbusConnectOptions(properties), transportService, relationList); + } + + /** + * 测试 + * + * @param properties 配置参数 + * @throws Exception e + */ + @Override + public void testExtension(@Nonnull JsonNode properties) throws Exception { + ModbusConnectOptions modbusConnectOptions = getModbusConnectOptions(properties); + ModbusMaster tcpMaster = ModbusUtils.getTcpMaster(modbusConnectOptions.getUri(), modbusConnectOptions.getPort()); + tcpMaster.destroy(); + } + + /** + * json转对象 + * + * @param configuration 配置 + * @return ModbusConfiguration + */ + private ModbusConnectOptions getModbusConnectOptions(JsonNode configuration) { + if (Objects.isNull(configuration.get(ExtensionUtil.URI)) || StringUtils.isBlank(configuration.get(ExtensionUtil.URI).asText())) { + throw new SysException("通讯地址不能为空"); + } + if (Objects.isNull(configuration.get(ExtensionUtil.PORT)) || StringUtils.isBlank(configuration.get(ExtensionUtil.PORT).asText())) { + throw new SysException("端口不能为空"); + } + return new ModbusConnectOptions(configuration.get(ExtensionUtil.URI).asText(), configuration.get(ExtensionUtil.PORT).asInt()); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionRelation.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionRelation.java new file mode 100644 index 0000000..39298da --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusExtensionRelation.java @@ -0,0 +1,62 @@ +package com.thing.extension.modbus; + +import com.serotonin.modbus4j.locator.BaseLocator; +import com.thing.extension.ExtensionRelation; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serial; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Getter +@ToString +public class ModbusExtensionRelation extends ExtensionRelation { + + @Serial + private static final long serialVersionUID = 3381668526693253799L; + + /** + * 设备编码 + */ + private final String device; + /** + * 属性编码 + */ + private final String key; + /** + * 拉取周期 s + */ + private final Long polling; + /** + * 请求体 + */ + private final BaseLocator baseLocator; + /** + * 下次拉取时间 s + */ + @Setter + private Long nextTime; + + public ModbusExtensionRelation(Long relationId, Long calculationId, Boolean debug, + String device, String key, Long polling, BaseLocator baseLocator) { + super(relationId, calculationId, debug); + this.device = device; + this.key = key; + this.polling = polling; + this.baseLocator = baseLocator; + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusFunction.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusFunction.java new file mode 100644 index 0000000..9e0dce5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusFunction.java @@ -0,0 +1,46 @@ +package com.thing.extension.modbus; + +import com.thing.common.core.exception.SysException; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * @author zhenghh. 2023-03-15 + **/ +public enum ModbusFunction { + + /** + * modbus方法 + */ + CoilStatus(1, "线圈"), + InputStatus(2, "离散量"), + HoldingRegister(3, "保持寄存器"), + InputRegisters(4, "输入寄存器"); + + @Getter + private final int range; + @Getter + private final String name; + + ModbusFunction(int range, String name) { + this.range = range; + this.name = name; + } + + public static ModbusFunction getByRange(int range) { + return Arrays.stream(ModbusFunction.values()) + .filter(item -> item.getRange() == range) + .findFirst() + .orElseThrow(() -> new SysException("未知类型: " + range)); + } + + public static String getByName(String name) { + int range = Arrays.stream(ModbusFunction.values()) + .filter(item -> StringUtils.equals(name, item.getName())) + .findFirst() + .orElseThrow(() -> new SysException("未知类型: " + name)).getRange(); + return String.valueOf(range); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusTsKv.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusTsKv.java new file mode 100644 index 0000000..03fc92b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusTsKv.java @@ -0,0 +1,26 @@ +package com.thing.extension.modbus; + +import com.thing.gen.queue.QueueProto; +import lombok.Getter; +import lombok.ToString; + +/** + * @author zhenghh. 2023-02-16 + **/ +@Getter +@ToString +public class ModbusTsKv { + /** + * 扩展计算关系主键 + */ + protected final String relationId; + /** + * 参数 + */ + private final QueueProto.TransportMsg transportMsg; + + public ModbusTsKv(String relationId, QueueProto.TransportMsg transportMsg) { + this.relationId = relationId; + this.transportMsg = transportMsg; + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/modbus/ModbusUtils.java b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusUtils.java new file mode 100644 index 0000000..2e9c0aa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/modbus/ModbusUtils.java @@ -0,0 +1,145 @@ +package com.thing.extension.modbus; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.serotonin.modbus4j.ModbusFactory; +import com.serotonin.modbus4j.ModbusMaster; +import com.serotonin.modbus4j.exception.ErrorResponseException; +import com.serotonin.modbus4j.exception.ModbusInitException; +import com.serotonin.modbus4j.exception.ModbusTransportException; +import com.serotonin.modbus4j.ip.IpParameters; +import com.serotonin.modbus4j.locator.BaseLocator; +import com.thing.common.core.enumeration.ExtendRelationType; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.extension.ExtensionJsonConverter; +import com.thing.extension.ExtensionUtil; +import com.thing.gen.queue.QueueProto; +import org.apache.commons.lang3.StringUtils; + +import java.net.ConnectException; +import java.util.Objects; + +/** + * @author zhenghh. 2023-02-17 + **/ +public class ModbusUtils { + + private static class StaticHolder { + private static final ModbusFactory INSTANCE = new ModbusFactory(); + } + + public static ModbusFactory getInstance() { + return StaticHolder.INSTANCE; + } + + public static ModbusMaster getTcpMaster(String ip, int port) throws ModbusInitException { + return getTcpMaster(ip, port, false); + } + + /** + * 获取ModbusMaster + * + * @param ip 地址 + * @param port 端口 + * @param encapsulated 这个属性确定了协议帧是否是通过tcp封装的RTU结构,采用modbus tcp/ip时,要设为false, 采用modbus rtu over tcp/ip时,要设为true + * @return ModbusMaster + */ + public static ModbusMaster getTcpMaster(String ip, int port, boolean encapsulated) throws ModbusInitException { + IpParameters params = new IpParameters(); + params.setHost(ip); + params.setPort(port); + params.setEncapsulated(encapsulated); + ModbusMaster master = getInstance().createTcpMaster(params, true); + //设置超时时间 + master.setTimeout(1000); + //设置重连次数 + master.setRetries(1); + //初始化 + master.init(); + master.setConnected(true); + return master; + } + + /** + * 单个读 + * + * @param modbusMaster modbusMaster + * @param milliTs 时间戳 + * @param relation 请求参数 + * @return list + */ + public static QueueProto.TransportMsg readValue(Long id, ModbusMaster modbusMaster, long milliTs, ModbusExtensionRelation relation, AuthParam authParam) { + try { + if (!modbusMaster.isConnected()) { + modbusMaster.setConnected(true); + } + Object value = modbusMaster.getValue(relation.getBaseLocator()); + if (Objects.isNull(value)) { + return null; + } + String payload = convertMessage(relation, milliTs, value.toString().trim()); + if (StringUtils.isBlank(payload)) { + return null; + } + return ExtensionJsonConverter.convertToExtensionProto(id, payload, milliTs, QueueOriginType.MODBUS_TCP, Lists.newArrayList(relation), authParam); + } catch (ModbusTransportException | ErrorResponseException e) { + if (e.getCause() instanceof ConnectException) { + modbusMaster.setConnected(false); + } + e.printStackTrace(); + return null; + } + } + + private static String convertMessage(ModbusExtensionRelation relation, long milliTs, String value) { + if (StringUtils.isBlank(value)) { + return null; + } + ObjectNode result = JacksonUtil.newObjectNode(); + ObjectNode kv = JacksonUtil.newObjectNode(); + kv.put(relation.getKey(), value); + ObjectNode tsKv = JacksonUtil.newObjectNode(); + tsKv.put("ts", milliTs); + tsKv.set("values", kv); + ArrayNode jsonNodes = JacksonUtil.newArrayNode(); + jsonNodes.add(tsKv); + result.set(relation.getDevice(), jsonNodes); + return JacksonUtil.toString(result); + } + + /** + * 写 + * 只支持单地址位写入 + * + * @param modbusMaster modbusMaster + * @param config 配置 + * @param inMsg 消息 + */ + public static void write(ModbusMaster modbusMaster, JsonNode config, String inMsg) { + try { + Object msg = ExtensionUtil.getMsg(config, inMsg); + BaseLocator baseLocator = ExtensionUtil.getBaseLocator(config, ExtendRelationType.WRITE); + modbusMaster.setValue(baseLocator, msg); + //System.out.println("get: " + modbusMaster.getValue(baseLocator)); + } catch (Exception e) { + e.printStackTrace(); + throw new SysException("命令下发失败: " + e.getMessage()); + } + } + + /*public static void main(String[] args) throws Exception { + ModbusMaster tcpMaster = getTcpMaster("127.0.0.1", 502); + ObjectNode jsonNodes = JacksonUtil.newObjectNode(); + jsonNodes.put("slaveId", 1); + jsonNodes.put("range", 3); + jsonNodes.put("offset", 2); + jsonNodes.put("dataType", 2); + //jsonNodes.put("registerCount", 1); + write(tcpMaster, jsonNodes, "1"); + }*/ +} diff --git a/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtension.java b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtension.java new file mode 100644 index 0000000..a18fbe5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtension.java @@ -0,0 +1,303 @@ +package com.thing.extension.mqtt; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.google.common.collect.Lists; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.BizUtils; +import com.thing.common.util.thread.ThingThreadFactory; +import com.thing.extension.*; +import com.thing.gen.queue.QueueProto; +import com.thing.transport.api.TransportService; +import com.thing.transport.api.TransportServiceCallback; +import com.thing.transport.api.session.TransportSessionUtil; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.*; +import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; + +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2023-03-07 + **/ +@Slf4j +public class MqttExtension implements Extension { + + private final Long id; + private final AuthParam authParam; + private final TransportService transportService; + + private MqttClient mqttClient; + + private ExecutorService transportExtendExecutor; + + private final List relationList; + + public MqttExtension(Long id, AuthParam authParam, String url, MqttConnectOptions mqttConnectOptions, + List relationList, TransportService transportService) { + this.id = id; + this.authParam = authParam; + this.transportService = transportService; + this.relationList = CollectionUtil.isEmpty(relationList) ? Lists.newArrayList() : relationList; + this.transportExtendExecutor = Executors.newFixedThreadPool(50, ThingThreadFactory.forName("transport-mqtt")); + try { + this.mqttClient = new MqttClient(url, BizUtils.getClientId("mqtt_"),new MemoryPersistence()); + this.mqttClient.setCallback(new Callback(this.mqttClient)); + if (!this.mqttClient.isConnected()) { + this.mqttClient.connect(mqttConnectOptions); + } + log.info("MQTT: {}-ClientId: {} 初始化成功", this.mqttClient.getServerURI(), this.mqttClient.getClientId()); + } catch (MqttException e) { + log.info("MQTT: {}-ClientId: {} 初始化失败", this.mqttClient.getServerURI(), this.mqttClient.getClientId()); + } + } + + /** + * 通讯主键 + * + * @return id + */ + @Override + public Long getId() { + return id; + } + + /** + * 获取通讯类型 + * + * @return 通讯类型 + */ + @Override + public ExtensionType getType() { + return ExtensionType.MQTT_CLIENT; + } + + /** + * 是否在线 + * + * @return Boolean + */ + @Override + public Boolean online() { + return Objects.nonNull(mqttClient) && mqttClient.isConnected(); + } + + /** + * 转换数据解析 + * + * @param relationList 通讯数据解析关系 + */ + @Override + public void updateRelation(List relationList) { + //存在先取消订阅 + List updateList = this.relationList.stream().filter(relationList::contains) + .map(item -> (MqttExtensionRelation) item).collect(Collectors.toList()); + unsubscribe(updateList); + this.relationList.removeAll(updateList); + //重新订阅 + List saveList = relationList.stream().filter(item -> !this.relationList.contains(item)) + .map(item -> (MqttExtensionRelation) item).collect(Collectors.toList()); + subscribe(saveList); + this.relationList.addAll(saveList); + } + + /** + * 移除数据解析 + * + * @param relationIdList 通讯数据解析关系主键 + */ + @Override + public void removeRelation(List relationIdList) { + List removeList = this.relationList.stream() + .filter(item -> relationIdList.contains(item.getRelationId())) + .map(item -> (MqttExtensionRelation) item).collect(Collectors.toList()); + unsubscribe(removeList); + this.relationList.removeAll(removeList); + } + + /** + * 订阅 + * + * @param relationList 关系列表 + */ + private void subscribe(List relationList) { + if (CollectionUtil.isEmpty(relationList) || !mqttClient.isConnected()) { + return; + } + try { + List topics = relationList.stream().map(MqttExtensionRelation::getTopic).distinct().collect(Collectors.toList()); + for (String topic : topics) { + mqttClient.subscribe(topic, 0); + log.info("MQTT:{}-clientId: {}-Topic: {} 开始订阅", mqttClient.getServerURI(), mqttClient.getClientId(), topic); + } + } catch (MqttException e) { + log.error("MQTT:{}-clientId: {} 订阅失败: {}", mqttClient.getServerURI(), mqttClient.getClientId(), e.getMessage()); + } + } + + /** + * 取消订阅 + */ + private void unsubscribe(List relationList) { + if (CollectionUtil.isEmpty(relationList) || !mqttClient.isConnected()) { + return; + } + try { + String[] topics = relationList.stream().map(MqttExtensionRelation::getTopic).distinct().toArray(String[]::new); + mqttClient.unsubscribe(topics); + log.info("MQTT:{}-clientId: {}-Topic: {} 取消订阅", mqttClient.getServerURI(), mqttClient.getClientId(), StringUtils.join(topics, ",")); + } catch (MqttException e) { + log.error("MQTT:{}-clientId: {} 取消订阅失败: {}", mqttClient.getServerURI(), mqttClient.getClientId(), e.getMessage()); + } + } + + /** + * 推送消息 + * + * @param publishMsg 推送信息 + * @throws Exception e + */ + @Override + public void sendMsg(ExtensionPublishMsg publishMsg) throws Exception { + if (publishMsg.getAddress() == null) { + throw new SysException("协议通道配置不存在"); + } + JsonNode jsonNode = publishMsg.getAddress().get(ExtensionUtil.TOPIC); + if (jsonNode == null || StringUtils.isBlank(jsonNode.asText())) { + throw new SysException("协议通道地址不存在"); + } + if (StringUtils.isBlank(publishMsg.getOutMsg())) { + throw new SysException("下发消息不存在"); + } + //下发 + String topic = jsonNode.asText(); + byte[] bytes = publishMsg.getOutMsg().getBytes(StandardCharsets.UTF_8); + MqttMessage mqttMessage = new MqttMessage(bytes); + mqttMessage.setQos(0); + mqttMessage.setRetained(Boolean.FALSE); + mqttMessage.setPayload(bytes); + MqttTopic mqttTopic = this.mqttClient.getTopic(topic); + mqttTopic.publish(mqttMessage); + //日志 + transportService.process(TransportSessionUtil.createSession(this.mqttClient.getClientId() + topic), + ExtensionJsonConverter.convertToControlProto(id, publishMsg, System.currentTimeMillis(), QueueOriginType.MQTT_CLIENT, authParam), + TransportServiceCallback.EMPTY); + log.info("MQTT: {}-clientId: {}-topic: {} publish message: {}", this.mqttClient.getServerURI(), this.mqttClient.getClientId(), topic, publishMsg.getOutMsg()); + } + + /** + * 销毁 + */ + @Override + public void destroy() { + try { + if (Objects.nonNull(this.transportExtendExecutor)) { + this.transportExtendExecutor.shutdown(); + this.transportExtendExecutor = null; + } + if (Objects.isNull(this.mqttClient)) { + return; + } + if (this.mqttClient.isConnected()) { + this.mqttClient.disconnect(); + } + this.mqttClient.setCallback(null); + this.mqttClient.close(); + this.mqttClient = null; + } catch (MqttException e) { + log.error("mqtt关闭失败:{}", e.getMessage()); + } + } + + class Callback implements MqttCallbackExtended { + + private final MqttClient mqttClient; + + public Callback(MqttClient mqttClient) { + this.mqttClient = mqttClient; + } + + @SneakyThrows + @Override + public void connectComplete(boolean reconnect, String serverUri) { + List subscribeList = relationList.stream().map(item -> (MqttExtensionRelation) item).collect(Collectors.toList()); + subscribe(subscribeList); + } + + @SneakyThrows + @Override + public void connectionLost(Throwable cause) { + log.warn("MQTT:{}-clientId:{} callback connectionLost-{}", mqttClient.getServerURI(), mqttClient.getClientId(), cause.getCause()); + } + + + @Override + public void messageArrived(String topic, MqttMessage message) { + // subscribe后得到的消息会执行到这里面 + transportExtendExecutor.submit(() -> { + String payload = new String(message.getPayload(), StandardCharsets.UTF_8); + if (StringUtils.isEmpty(payload)) { + return; + } + if(!isJson(payload)){ + payload = payload.replace("\"","\\\""); + payload = "{\"msg\":\"" + payload + "\"}"; + } + try { + //匹配topic + List matchList = TopicMatcher.matchTopicKey(relationList, topic); + if (CollectionUtil.isNotEmpty(matchList)) { + try { + QueueProto.TransportMsg msg = ExtensionJsonConverter.convertToExtensionProto(id, payload, System.currentTimeMillis(), QueueOriginType.MQTT_CLIENT, matchList, authParam); + transportService.process(TransportSessionUtil.createSession(mqttClient.getClientId() + topic), + msg, + TransportServiceCallback.EMPTY); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } catch (Exception e) { + log.error("MQTT:{}-clientId: {}-Topic:{} subscribe message:{}, error: {}", mqttClient.getServerURI(), mqttClient.getClientId(), topic, payload, e.getMessage()); + e.printStackTrace(); + } + }); + } + + @Override + public void deliveryComplete(IMqttDeliveryToken token) { + + } + + + + } + /** + * 判断数据是否为json + * @param jsonStr + * @return + */ + public static boolean isJson(String jsonStr) { + JsonElement jsonElement; + try { + jsonElement = JsonParser.parseString(jsonStr); + } catch (Exception e) { + return false; + } + if (jsonElement == null) { + return false; + } + return jsonElement.isJsonObject(); + } + +} diff --git a/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionProvider.java b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionProvider.java new file mode 100644 index 0000000..b3f2f9d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionProvider.java @@ -0,0 +1,102 @@ +package com.thing.extension.mqtt; + +import cn.hutool.core.util.ObjectUtil; + +import com.fasterxml.jackson.databind.JsonNode; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.utils.BizUtils; +import com.thing.extension.*; +import com.thing.transport.api.TransportService; + +import org.apache.commons.lang3.StringUtils; +import org.eclipse.paho.client.mqttv3.MqttClient; +import org.eclipse.paho.client.mqttv3.MqttConnectOptions; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nonnull; + +/** + * @author zhenghh. 2023-03-07 + **/ +@Component +public class MqttExtensionProvider implements ExtensionProvider { + + /** + * 类型 + * + * @return 类型 + */ + @Nonnull + @Override + public ExtensionType getType() { + return ExtensionType.MQTT_CLIENT; + } + + /** + * 创建通讯扩展 + * + * @param id 配置主键 + * @param properties 配置参数 + * @param relationList 订阅参数 + * @param transportService 队列服务 + * @return Extension + */ + @Override + public Extension createExtension(@Nonnull Long id, @Nonnull AuthParam authParam, @Nonnull JsonNode properties, + List relationList, TransportService transportService) { + return new MqttExtension(id, authParam, convertUrl(properties), getMqttConnectOptions(properties), relationList, transportService); + } + + /** + * 测试 + * + * @param properties 配置参数 + * @throws Exception e + */ + @Override + public void testExtension(@Nonnull JsonNode properties) throws Exception { + MqttClient mqttClient = new MqttClient(convertUrl(properties), BizUtils.getClientId("test_")); + if (!mqttClient.isConnected()) { + mqttClient.connect(getMqttConnectOptions(properties)); + } + //关闭连接 + if (mqttClient.isConnected()) { + mqttClient.disconnect(); + mqttClient.close(); + } + } + + /** + * 获取MqttConnectOptions + * + * @param properties 配置 + * @return MqttConnectOptions + */ + private MqttConnectOptions getMqttConnectOptions(JsonNode properties) { + MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); + // 设置连接的用户名 + if (Objects.nonNull(properties.get(ExtensionUtil.USERNAME))) { + mqttConnectOptions.setUserName(properties.get(ExtensionUtil.USERNAME).asText()); + } + if (ObjectUtil.isNotNull(properties.get(ExtensionUtil.PASSWORD))) { + mqttConnectOptions.setPassword(properties.get(ExtensionUtil.PASSWORD).asText().toCharArray()); + } + //设置是否清空session,这里如果设置为false表示服务器会保留客户端的连接记录,这里设置为true表示每次连接到服务器都以新的身份连接 + mqttConnectOptions.setCleanSession(Boolean.FALSE); + // 设置超时时间 单位为秒 + mqttConnectOptions.setConnectionTimeout(0); + mqttConnectOptions.setAutomaticReconnect(Boolean.TRUE); + // 设置会话心跳时间 单位为秒 服务器会每隔1.5*20秒的时间向客户端发送个消息判断客户端是否在线,但这个方法并没有重连的机制 + mqttConnectOptions.setKeepAliveInterval(20); + return mqttConnectOptions; + } + + private String convertUrl(JsonNode properties) { + String uri = StringUtils.prependIfMissing(properties.get(ExtensionUtil.URI).asText(), "tcp://"); + JsonNode port = properties.get(ExtensionUtil.PORT); + return port != null && StringUtils.isNotBlank(port.asText()) ? uri + ":" + port.asText() : uri; + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionRelation.java b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionRelation.java new file mode 100644 index 0000000..ce2e398 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/mqtt/MqttExtensionRelation.java @@ -0,0 +1,35 @@ +package com.thing.extension.mqtt; + +import com.thing.extension.ExtensionRelation; +import lombok.Getter; +import lombok.ToString; + +import java.io.Serial; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Getter +@ToString +public class MqttExtensionRelation extends ExtensionRelation { + + @Serial + private static final long serialVersionUID = -1212127064587889720L; + + private final String topic; + + public MqttExtensionRelation(Long relationId, Long calculationId, Boolean debug, String topic) { + super(relationId, calculationId, debug); + this.topic = topic; + } + + @Override + public boolean equals(Object o) { + return super.equals(o); + } + + @Override + public int hashCode() { + return super.hashCode(); + } +} diff --git a/modules/thing/src/main/java/com/thing/extension/mqtt/TopicMatcher.java b/modules/thing/src/main/java/com/thing/extension/mqtt/TopicMatcher.java new file mode 100644 index 0000000..6067fcb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/extension/mqtt/TopicMatcher.java @@ -0,0 +1,84 @@ +package com.thing.extension.mqtt; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.thing.extension.ExtensionRelation; +import lombok.AllArgsConstructor; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-12-06 + **/ +public class TopicMatcher { + + private final static PathMatcher MATCHER = new AntPathMatcher(); + + public static List matchTopicKey(List relationList, String topic) { + if (CollectionUtil.isEmpty(relationList) || StringUtils.isBlank(topic)) { + return Lists.newArrayList(); + } + List collect = relationList.stream().map(item -> ((MqttExtensionRelation) item)).collect(Collectors.toList()); + //只需要匹配读类型 + List patterns = collect.stream().map(MqttExtensionRelation::getTopic).distinct().collect(Collectors.toList()); + String matchTopic = matchTopic(patterns, topic); + return collect.stream().filter(item -> StringUtils.equals(matchTopic, item.getTopic())).collect(Collectors.toList()); + } + + public static String matchTopic(List patterns, String topic) { + if (CollectionUtil.isEmpty(patterns) || StringUtils.isBlank(topic)) { + return topic; + } + //相等匹配 存在直接返回 + if (patterns.stream().anyMatch(pattern -> StringUtils.equals(pattern, topic))) { + return topic; + } + //路径匹配 + List matchedList = patterns.stream() + .filter(pattern -> MATCHER.match(pattern.replace("#", "**").replace("+", "*"), topic)) + .collect(Collectors.toList()); + //不存在直接返回 + if (CollectionUtil.isEmpty(matchedList)) { + return topic; + } + //匹配到一条直接返回 + if (matchedList.size() == 1) { + return matchedList.get(0); + } + //匹配到多条 区分优先级 + > # 同级下标越大优先级越高 + PatternIndex plusIndex = patterns.stream().filter(pattern -> pattern.contains("+")) + .map(pattern -> new PatternIndex(pattern, StringUtils.indexOf(pattern, "+"))) + .max(Comparator.comparing(PatternIndex::getIndex)).orElse(null); + if (Objects.nonNull(plusIndex)) { + return plusIndex.getPattern(); + } + PatternIndex patternIndex = patterns.stream().filter(pattern -> pattern.contains("#")) + .map(pattern -> new PatternIndex(pattern, StringUtils.indexOf(pattern, "#"))) + .max(Comparator.comparing(PatternIndex::getIndex)).orElse(null); + if (Objects.nonNull(patternIndex)) { + return patternIndex.getPattern(); + } + return topic; + } + + @Data + @AllArgsConstructor + static class PatternIndex { + private String pattern; + private Integer index; + } + + @Data + @AllArgsConstructor + static class MatchEntity { + private String topic; + private Long relationId; + } +} diff --git a/modules/thing/src/main/java/com/thing/listener/QueueDeviceEventListener.java b/modules/thing/src/main/java/com/thing/listener/QueueDeviceEventListener.java new file mode 100644 index 0000000..181a776 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/listener/QueueDeviceEventListener.java @@ -0,0 +1,204 @@ +package com.thing.listener; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.IdUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.GateWayStatus; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.enumeration.TemplateMark; +import com.thing.common.core.enumeration.ThingStatus; +import com.thing.common.core.event.AuthParam; +import com.thing.common.core.event.QueueDeviceEvent; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.TokenGenerator; +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.model.dto.IotThingModelDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.transport.api.adaptor.JsonConverter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Component +@RequiredArgsConstructor +public class QueueDeviceEventListener { + + private final ThingManageContextService thingManageContextService; + + private final ThingCache thingCache; + + @EventListener(QueueDeviceEvent.class) + public void onDeviceEvent(QueueDeviceEvent event) { + List msgList = event.getList(); + try { + List validMsgList = msgList.stream() + .filter(item -> Objects.nonNull(item.getTenantCode()) + || Objects.nonNull(item.getCompanyId()) + || Objects.nonNull(item.getDeptId())).toList(); + if (CollectionUtil.isEmpty(validMsgList)) { + return; + } + //新增和更新物模型信息 + Map codeOriginMap = validMsgList.parallelStream() + .collect(Collectors.toMap(QueueMsgDTO::getThingCode, + item -> (QueueOriginType.AUTO_DATA_SYNC.name().equals(item.getOrigin()) + || QueueOriginType.MANUAL_DATA_SYNC.name().equals(item.getOrigin())) + ? QueueOriginType.TB.name() : item.getOrigin(), (e1, e2) -> e1)); + //物模型 + List insertModelList = new ArrayList<>(); + //物模型插入 + checkAndSaveThingModels(codeOriginMap, insertModelList); + + if (CollectionUtils.isNotEmpty(insertModelList)) { + // 物实体 + Map> tenantThingCodeMap = + validMsgList.parallelStream() + .filter(item -> Objects.nonNull(item.getTenantCode()) + || Objects.nonNull(item.getCompanyId()) + || Objects.nonNull(item.getDeptId())) + .collect(Collectors.groupingBy( + item ->new AuthParam(item.getTenantCode(),item.getCompanyId(),item.getDeptId()), + Collectors.mapping(QueueMsgDTO::getThingCode,Collectors.toSet()))); + + List insertEntityList = new ArrayList<>(); + saveTenantThingList(tenantThingCodeMap, insertEntityList); + + insertModelList.forEach(item -> { + long count = insertEntityList.stream().filter(e -> StringUtils.equals(e.getCode(), item.getCode())).count(); + List keyMap = thingCache.findKeyMap(CacheNameEnum.THING_ENTITY, item.getCode()); + item.setAuthNum(count+CollectionUtils.size(keyMap)); + }); + thingManageContextService.saveAllModel(insertModelList); + + //更新物模板缓存 + List iotThingModelDTOS = ConvertUtils.sourceToTarget(insertModelList, IotThingModelDTO.class); + List modelList = JsonConverter.convertToJsonObjectListObjectNode(iotThingModelDTOS); + CacheInit.modelMap(modelList,thingCache); + + if(CollectionUtils.isNotEmpty(insertEntityList)){ + List distinctStudentFile = insertEntityList.stream() + .collect(Collectors.collectingAndThen( + Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getCode() + ";" + o.getTenantCode()))), ArrayList::new)); + thingManageContextService.saveEntity(distinctStudentFile); + + //更新物实体缓存 + List entityList = distinctStudentFile.stream().map(e -> { + ObjectNode keyMap = thingCache.getKeyMap(CacheNameEnum.THING_ENTITY, e.getCode()); + return new IotThingViewDTO() + .setLat(e.getLat()) + .setLon(e.getLon()) + .setTags(e.getTags()) + .setDeptIds(e.getDeptIds()) + .setOrigin(keyMap.get(CacheNameEnum.ModelField.THING_MODEL_ORIGIN.getField()).asText()) + .setTemplateMark(TemplateMark.NO.getValue()) + .setRealType("1") + .setImg(e.getImg()) + .setRemark(e.getRemark()) + .setEnableStatus(e.getEnableStatus()) + .setStatusTs(keyMap.get(CacheNameEnum.ModelField.THING_MODEL_STATUS_TS.getField()).asLong()) + .setStatus(keyMap.get(CacheNameEnum.ModelField.THING_MODEL_STATUS.getField()).asText()) + .setTenantCode(e.getTenantCode()) + .setCompanyId(e.getTenantCode()) + .setDeptId(e.getTenantCode()) + .setEntityType(e.getType()) + .setEntityName(e.getName()) + .setEntityCode(e.getCode()) + .setEntityId(e.getId()) + .setModelId(keyMap.get(CacheNameEnum.ModelField.THING_MODEL_ID.getField()).asLong()) + .setCreateDate(e.getCreateDate()); + }).toList(); + List entityJsonList = JsonConverter.convertToJsonObjectListObjectNode(entityList); + CacheInit.entityMap(entityJsonList,thingCache); + } + } + } catch (Exception e) { + log.error("设备保存失败: {}", e.getMessage(), e); + } + } + + /** + * 物管理表是否存在,不存在新增设备 + * + * @param codeOriginMap 物编码 + */ + private void checkAndSaveThingModels(Map codeOriginMap, List insertList) throws Exception { + // 使用迭代器遍历并删除不符合条件的元素 + Iterator> iterator = codeOriginMap.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + String code = entry.getKey(); + ObjectNode jsonObject = thingCache.findObjectNode(CacheNameEnum.THING_MODEL, code); + if (null != jsonObject && !jsonObject.isEmpty()) { + iterator.remove(); + } + } + codeOriginMap.forEach((thingCode, origin) -> { + IotThingModelEntity modelEntity = new IotThingModelEntity() + .setCode(thingCode) + .setToken(TokenGenerator.generateValue()) + .setGateway(GateWayStatus.NO_GATE_WAY.getValue()) + .setStatus(ThingStatus.NOT_CONNECTED.getCode()) + .setAuthNum(1L) + .setStatusTs(DateTimeUtils.getCurrentTime()) + .setOrigin(origin); + modelEntity.setId(IdUtil.getSnowflake().nextId()) + .setCreateDate(DateTimeUtils.getCurrentTime()) + .setCreateDate(DateTimeUtils.getCurrentTime()) + ; + insertList.add(modelEntity); + }); + } + + /** + * 保存物实体 + * + * @param tenantThingCodeMap 物实体 + */ + private void saveTenantThingList(Map> tenantThingCodeMap, List insertList) { + tenantThingCodeMap.forEach( + (authParam, thingCodes) -> { + if (CollectionUtil.isEmpty(thingCodes)) { + return; + } + thingCodes.removeIf(code -> { + ObjectNode keyMap = thingCache.findObjectNode(CacheNameEnum.THING_ENTITY, authParam.getTenantCode() + ":" + code); + return null != keyMap && !keyMap.isEmpty(); + }); + if (CollectionUtil.isNotEmpty(thingCodes)) { + for (String newThingCode : thingCodes) { + insertList.add(createThingEntity(newThingCode, authParam)); + } + } + }); + } + + private IotThingEntity createThingEntity(String thingCode, AuthParam authParam) { + IotThingEntity entity = new IotThingEntity(); + entity.setId(IdUtil.getSnowflake().nextId()); + entity.setCode(thingCode); + entity.setName(thingCode); + entity.setEnableStatus("1"); + entity.setRealType("1"); + entity.setTemplateMark("0"); + entity.setType("默认物类型"); + entity.setTenantCode(authParam.getTenantCode()); + entity.setCompanyId(authParam.getCompanyId()); + entity.setDeptIds(authParam.getDeptId().toString()); + return entity; + } + +} diff --git a/modules/thing/src/main/java/com/thing/screen/controller/IotEnterpriseDashboardController.java b/modules/thing/src/main/java/com/thing/screen/controller/IotEnterpriseDashboardController.java new file mode 100644 index 0000000..272637a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/controller/IotEnterpriseDashboardController.java @@ -0,0 +1,142 @@ +package com.thing.screen.controller; + +import cn.hutool.core.bean.BeanUtil; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import com.thing.screen.dto.IotEnterpriseDashboardDTO; +import com.thing.screen.service.IotEnterpriseDashboardService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** +* 主页管理 +* +* @author xc +* @since 3.0 2023-05-05 +*/ +@RestController +@RequestMapping("screen/iotenterprisedashboard") +@Tag(name="企业主页配置管理") +@RequiredArgsConstructor +public class IotEnterpriseDashboardController { + private final IotEnterpriseDashboardService iotEnterpriseDashboardService; + private final SysTenantService sysTenantService; + + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "largeScreenName",description ="名称") + }) + public Result> page( @RequestParam Map params){ + + PageData page = iotEnterpriseDashboardService.pageIotEnterpriseDashboardDTO(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotEnterpriseDashboardDTO data = iotEnterpriseDashboardService.getIotEnterpriseDashboardDTO(id); + return new Result().ok(data); + } + + @GetMapping("get") + @Operation(summary="获取当前企业的主页详情") + public Result getByCompanyId(){ + + IotEnterpriseDashboardDTO data = iotEnterpriseDashboardService.getByCompanyId(); + if(BeanUtil.isEmpty(data)){ + return new Result().error("当前企业未配置首页大屏!"); + } + + return new Result().ok(data); + } + + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotEnterpriseDashboardDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + return iotEnterpriseDashboardService.saveIotEnterpriseDashboardDTO(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotEnterpriseDashboardDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + return iotEnterpriseDashboardService.updateIotEnterpriseDashboardDTO(dto); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + return iotEnterpriseDashboardService.deleteIotEnterpriseDashboardDTO(ids); + } + + + @GetMapping("dashboardList") + @Operation(summary="获取可见权限下的所有看板列表") + @Parameters({ + @Parameter(name = "tenantCode",description ="选择的企业/租户tenantCode") + }) + public Result> dashboardList(String tenantCode){ + + List dtos = iotEnterpriseDashboardService.dashboardList(tenantCode); + + return new Result>().ok(dtos); + } + + + + @GetMapping("tenantList") + @Operation(summary="获取可见权限下的所有企业列表") + @Parameters({ + @Parameter(name = "industryType",description ="行业类型") , + @Parameter(name = "tenantName",description ="企业名称") + }) + public Result> tenantList( @RequestParam Map params) { + List list = sysTenantService.queryList(params); + list.sort(Comparator.comparing(SysTenantDTO::getTenantType).reversed()); + return new Result>().ok(list); + } + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/dto/IotEnterpriseDashboardDTO.java b/modules/thing/src/main/java/com/thing/screen/dto/IotEnterpriseDashboardDTO.java new file mode 100644 index 0000000..a6e154a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/dto/IotEnterpriseDashboardDTO.java @@ -0,0 +1,58 @@ +package com.thing.screen.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** +* 主页管理 +* +* @author xc xc +* @since 3.0 2023-05-05 +*/ +@Data +@Schema( name= "主页管理") +public class IotEnterpriseDashboardDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + @Schema(description = "id") + private Long id; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @NotNull(message="企业详情id不能为空", groups = AddGroup.class) + @NotNull(message="企业详情id不能为空", groups = UpdateGroup.class) + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + @Schema(description = "大屏名称") + private String largeScreenName; + @Schema(description = "背景图片地址") + private String largeScreenUrl; + @Schema(description = "大屏类型: 1.只有鸟瞰图 2.鸟瞰图-左、三张看板 3.鸟瞰图-右、三张看板 4.鸟瞰图、6个看板") + private String largeScreenType; + + @Schema(description = "看板详情信息JSON") + private String detailJson; + + @Schema(description = "企业名称") + private String companyName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/entity/IotEnterpriseDashboardEntity.java b/modules/thing/src/main/java/com/thing/screen/entity/IotEnterpriseDashboardEntity.java new file mode 100644 index 0000000..c663477 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/entity/IotEnterpriseDashboardEntity.java @@ -0,0 +1,48 @@ +package com.thing.screen.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 主页管理 + * + * @author xc xc + * @since 3.0 2023-05-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_enterprise_dashboard") +public class IotEnterpriseDashboardEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 大屏名称 + */ + private String largeScreenName; + /** + * 背景图片地址 + */ + private String largeScreenUrl; + /** + * 大屏类型: + * 1.只有鸟瞰图 + * 2.鸟瞰图-左、三张看板 + * 3.鸟瞰图-右、三张看板 + * 4.鸟瞰图、6个看板 + */ + private String largeScreenType; + + /** + * 详情json + */ + private String detailJson; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/mapper/IotEnterpriseDashboardMapper.java b/modules/thing/src/main/java/com/thing/screen/mapper/IotEnterpriseDashboardMapper.java new file mode 100644 index 0000000..c8984ca --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/mapper/IotEnterpriseDashboardMapper.java @@ -0,0 +1,16 @@ +package com.thing.screen.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.screen.entity.IotEnterpriseDashboardEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 主页管理 +* +* @author xc xc +* @since 3.0 2023-05-05 +*/ +@Mapper +public interface IotEnterpriseDashboardMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/service/IotEnterpriseDashboardService.java b/modules/thing/src/main/java/com/thing/screen/service/IotEnterpriseDashboardService.java new file mode 100644 index 0000000..73d92ac --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/service/IotEnterpriseDashboardService.java @@ -0,0 +1,34 @@ +package com.thing.screen.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.screen.dto.IotEnterpriseDashboardDTO; +import com.thing.screen.entity.IotEnterpriseDashboardEntity; + +import java.util.List; +import java.util.Map; + +/** + * 主页管理 + * + * @author xc xc + * @since 3.0 2023-05-05 + */ +public interface IotEnterpriseDashboardService extends IBaseService { + + PageData pageIotEnterpriseDashboardDTO(Map params); + + IotEnterpriseDashboardDTO getIotEnterpriseDashboardDTO(Long id); + + Result saveIotEnterpriseDashboardDTO(IotEnterpriseDashboardDTO dto); + + Result updateIotEnterpriseDashboardDTO(IotEnterpriseDashboardDTO dto); + + Result deleteIotEnterpriseDashboardDTO(Long[] ids); + + IotEnterpriseDashboardDTO getByCompanyId(); + + List dashboardList(String tenantCode); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/service/impl/IotEnterpriseDashboardServiceImpl.java b/modules/thing/src/main/java/com/thing/screen/service/impl/IotEnterpriseDashboardServiceImpl.java new file mode 100644 index 0000000..c860cce --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/service/impl/IotEnterpriseDashboardServiceImpl.java @@ -0,0 +1,146 @@ +package com.thing.screen.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.dashboard.dto.IotDashboardGroupDTO; +import com.thing.dashboard.mapper.IotDashboardGroupMapper; +import com.thing.screen.dto.IotEnterpriseDashboardDTO; +import com.thing.screen.entity.IotEnterpriseDashboardEntity; +import com.thing.screen.mapper.IotEnterpriseDashboardMapper; +import com.thing.screen.service.IotEnterpriseDashboardService; +import com.thing.screen.util.TenantSubsetUtilS; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.thing.screen.entity.table.IotEnterpriseDashboardEntityTableDef.IOT_ENTERPRISE_DASHBOARD_ENTITY; + +/** + * 主页管理 + * + * @author xc xc + * @since 3.0 2023-05-05 + */ +@Service +@RequiredArgsConstructor +public class IotEnterpriseDashboardServiceImpl extends BaseServiceImpl implements IotEnterpriseDashboardService { + private final TenantSubsetUtilS tenantSubsetUtil; + private final SysTenantService sysTenantService; + private final IotDashboardGroupMapper iotDashboardGroupDao; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + String name = (String) params.get("largeScreenName"); + wrapper.like("large_screen_name", name, StringUtils.isNotBlank(name)); + //切换租户/租户/企业登陆 + UserDetail userDetail = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + Long currentCompanyId = TenantContext.getCompanyId(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(currentTenantCode, userDetail.getTenantCode())) { + wrapper.in("company_id",tenantSubsetUtil.paramsAddTenantCodeList(true)); + }else { + if("admin".equals(userDetail.getUsername())){ + wrapper.and( + IOT_ENTERPRISE_DASHBOARD_ENTITY.TENANT_CODE.eq(currentTenantCode) + .or(IOT_ENTERPRISE_DASHBOARD_ENTITY.COMPANY_ID.eq(currentCompanyId))); + }else { + wrapper.in("tenant_code",tenantSubsetUtil.paramsAddTenantCodeList(true)); + } + } + return wrapper; + } + + + @Override + public PageData pageIotEnterpriseDashboardDTO(Map params) { + PageData pageData = getPageData(params, IotEnterpriseDashboardDTO.class); + pageData.getList().forEach(temp->{ + SysTenantDTO entity =sysTenantService.getTenantCode(temp.getCompanyId()); + temp.setCompanyName(entity.getTenantName()); + }); + + return pageData; + } + + @Override + public IotEnterpriseDashboardDTO getIotEnterpriseDashboardDTO(Long id) { + return getByIdAs(id, IotEnterpriseDashboardDTO.class); + } + + @Override + @Transactional + public Result saveIotEnterpriseDashboardDTO(IotEnterpriseDashboardDTO dto) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("company_id", dto.getCompanyId(), ObjectUtil.isNotEmpty(dto.getCompanyId())); + IotEnterpriseDashboardEntity entity = mapper.selectOneByQuery(wrapper); + if(ObjectUtil.isNotNull(entity)){ + dto.setId(entity.getId()); + } + + IotEnterpriseDashboardEntity entityVo =ConvertUtils.convertWithTypeAdapt(dto,IotEnterpriseDashboardEntity.class); + if(entityVo.getId()!=null){ + this.updateById(entityVo); + }else { + mapper.insert(entityVo); + } + + return new Result().ok("添加成功!"); + } + + @Override + public Result updateIotEnterpriseDashboardDTO(IotEnterpriseDashboardDTO dto) { + updateById(ConvertUtils.sourceToTarget(dto,IotEnterpriseDashboardEntity.class)); + return new Result().ok("修改成功"); + } + + @Override + public Result deleteIotEnterpriseDashboardDTO(Long[] ids) { + batchDelete(ids); + return new Result().ok("删除成功"); + + } + + @Override + public IotEnterpriseDashboardDTO getByCompanyId() { + Long companyId = TenantContext.getCompanyId(SecurityUser.getUser()); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("company_id", companyId, ObjectUtil.isNotEmpty(companyId)); + IotEnterpriseDashboardEntity entity = mapper.selectOneByQuery(wrapper); + + if(BeanUtil.isEmpty(entity)){ + return null; + } + return ConvertUtils.convertWithTypeAdapt(entity,IotEnterpriseDashboardDTO.class); + } + + @Override + public List dashboardList(String tenantCode) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("tenant_code", tenantCode, ObjectUtil.isNotEmpty(tenantCode)); + + return ConvertUtils.sourceToTarget(iotDashboardGroupDao.selectListByQuery(wrapper),IotDashboardGroupDTO.class); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/screen/util/TenantSubsetUtilS.java b/modules/thing/src/main/java/com/thing/screen/util/TenantSubsetUtilS.java new file mode 100644 index 0000000..696bccc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/screen/util/TenantSubsetUtilS.java @@ -0,0 +1,71 @@ +package com.thing.screen.util; + +import cn.hutool.core.collection.CollectionUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; +import com.thing.sys.tenant.mapper.SysTenantGroupMapper; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class TenantSubsetUtilS { + private final SysTenantGroupMapper baseDao; + + /** + * 父租户code获取子列表 + * + * @param parentCode 父租户code + * @return 子租户列表 + */ + public List getChildren(Long parentCode) { + if (parentCode == null) { + return new ArrayList<>(); + } + QueryWrapper queryWrapper = QueryWrapper.create() + .eq(SysTenantGroupEntity::getParentCode, parentCode); + List list = baseDao.selectListByQuery(queryWrapper); + if (CollectionUtil.isEmpty(list)) { + return new ArrayList<>(); + } + return list.parallelStream().map(SysTenantGroupEntity::getCode).distinct().collect(Collectors.toList()); + } + + + /** + * 不为超管 或 超管切换租户时,查询自己及下属 + * @param includeTenant 是否包含当前租户 + */ + public List paramsAddTenantCodeList( Boolean includeTenant) { + List tenantCodeList = new ArrayList<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + tenantCodeList = getChildren(tenantCode); + + //保证查不到数据 + if(CollectionUtil.isEmpty(tenantCodeList)) { + tenantCodeList.add(-1L); + } + } + if(includeTenant) { + tenantCodeList.add(tenantCode); + } + return tenantCodeList; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/aspect/LogOperationAspect.java b/modules/thing/src/main/java/com/thing/sys/biz/aspect/LogOperationAspect.java new file mode 100644 index 0000000..c075371 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/aspect/LogOperationAspect.java @@ -0,0 +1,115 @@ +package com.thing.sys.biz.aspect; + +import com.alibaba.fastjson.JSON; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.utils.IpUtils; +import com.thing.sys.log.entity.SysLogOperationEntity; +import com.thing.sys.log.enumeration.OperationStatusEnum; +import com.thing.sys.log.service.SysLogOperationService; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; + +import jakarta.servlet.http.HttpServletRequest; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; + +/** + * 操作日志,切面处理类 + * + * @author Mark sunlightcs@gmail.com + */ +@Aspect +@Component +@RequiredArgsConstructor +public class LogOperationAspect { + private final SysLogOperationService sysLogOperationService; + + @Pointcut("@annotation(com.thing.common.core.annotation.LogOperation)") + public void logPointCut() {} + + @Around("logPointCut()") + public Object around(ProceedingJoinPoint point) throws Throwable { + long beginTime = System.currentTimeMillis(); + try { + // 执行方法 + Object result = point.proceed(); + + // 执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + // 保存日志 + saveLog(point, time, OperationStatusEnum.SUCCESS.value()); + + return result; + } catch (Exception e) { + // 执行时长(毫秒) + long time = System.currentTimeMillis() - beginTime; + // 保存日志 + saveLog(point, time, OperationStatusEnum.FAIL.value()); + + throw e; + } + } + + private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) + throws Exception { + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = + joinPoint + .getTarget() + .getClass() + .getDeclaredMethod(signature.getName(), signature.getParameterTypes()); + LogOperation annotation = method.getAnnotation(LogOperation.class); + + SysLogOperationEntity log = new SysLogOperationEntity(); + if (annotation != null) { + // 注解上的描述 + log.setOperation(annotation.value()); + } + + // 登录用户信息 + UserDetail user = SecurityUser.getUser(); + log.setCreatorName(user.getUsername()); + + log.setStatus(status); + log.setRequestTime((int) time); + + // 请求相关信息 + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + + if(request != null){ + log.setIp(IpUtils.getIpAddr(request) == null ? IpUtils.getInnetIp(): IpUtils.getIpAddr(request)); + log.setInternalIp(IpUtils.getInternalIp(request) == null ? IpUtils.getInnetIp(): IpUtils.getInternalIp(request)); + }else{ + log.setIp(IpUtils.getInnetIp()); + log.setInternalIp(IpUtils.getInnetIp()); + } + String type = annotation.type(); + if(!StringUtils.equals(type,"1")){ + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setRequestUri(request.getRequestURI()); + log.setRequestMethod(request.getMethod()); + } + // 请求参数 + Object[] args = joinPoint.getArgs(); + try { + String params = JSON.toJSONString(args[0]); + log.setRequestParams(params); + } catch (Exception ignore) { + } + + // 保存到DB + sysLogOperationService.save(log); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDeptController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDeptController.java new file mode 100644 index 0000000..2385e04 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDeptController.java @@ -0,0 +1,86 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.service.SysDeptService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.HashMap; +import java.util.List; + +/** + * 部门管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/dept") +@Tag(name = "部门管理") +@RequiredArgsConstructor +public class SysDeptController { + private final SysDeptService sysDeptService; + + @GetMapping("list") + @Operation(summary="列表") + public Result> list() { + List list = sysDeptService.list(new HashMap<>(1)); + + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + SysDeptDTO data = sysDeptService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody SysDeptDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysDeptService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("sys:dept:update") + public Result update(@RequestBody SysDeptDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDeptService.update(dto); + + return new Result(); + } + + @DeleteMapping("{id}") + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("sys:dept:delete") + public Result delete(@PathVariable("id") Long id) { + //效验数据 + AssertUtils.isNull(id, "id"); + + sysDeptService.delete(id); + + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictDataController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictDataController.java new file mode 100644 index 0000000..9859c0d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictDataController.java @@ -0,0 +1,117 @@ + + +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.service.SysDictDataService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + +/** + * 字典数据 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/dict/data") +@Tag(name = "字典数据") +@RequiredArgsConstructor +public class SysDictDataController { + + private final SysDictDataService sysDictDataService; + + @GetMapping("page") + @Operation(summary="字典数据") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "dictLabel",description ="字典标签"), + @Parameter(name = "dictValue",description ="字典值") + }) + //@RequiresPermissions("sys:dict:page") + public Result> page( @RequestParam Map params) { + //字典类型 + PageData page = sysDictDataService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="字典数据") + @Parameters({ + @Parameter(name = "dictLabel",description ="字典标签"), + @Parameter(name = "dictValue",description ="字典值") + }) + public Result> list( @RequestParam Map params) { + //字典类型 + List list = sysDictDataService.list(params); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("sys:dict:info") + public Result get(@PathVariable("id") Long id) { + SysDictDataDTO data = sysDictDataService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("sys:dict:save") + public Result save(@RequestBody SysDictDataDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysDictDataService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("sys:dict:update") + public Result update(@RequestBody SysDictDataDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDictDataService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("sys:dict:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysDictDataService.delete(ids); + + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictTypeController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictTypeController.java new file mode 100644 index 0000000..23cf9ce --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysDictTypeController.java @@ -0,0 +1,112 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysDictTypeDTO; +import com.thing.sys.biz.entity.DictType; +import com.thing.sys.biz.service.SysDictTypeService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/dict/type") +@Tag(name = "字典类型") +@RequiredArgsConstructor +public class SysDictTypeController { + private final SysDictTypeService sysDictTypeService; + + @GetMapping("page") + @Operation(summary="字典类型") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "dictType",description ="字典类型"), + @Parameter(name = "dictName",description ="字典名称") + }) + @RequiresPermissions("sys:dict:page") + public Result> page( @RequestParam Map params) { + //字典类型 + PageData page = sysDictTypeService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:dict:info") + public Result get(@PathVariable("id") Long id) { + SysDictTypeDTO data = sysDictTypeService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:dict:save") + public Result save(@RequestBody SysDictTypeDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysDictTypeService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:dict:update") + public Result update(@RequestBody SysDictTypeDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysDictTypeService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:dict:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysDictTypeService.delete(ids); + + return new Result(); + } + + @GetMapping("all") + @Operation(summary="所有字典数据") + public Result> all() { + List list = sysDictTypeService.getAllList(); + + return new Result>().ok(list); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysIndustryTypeController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysIndustryTypeController.java new file mode 100644 index 0000000..cdab963 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysIndustryTypeController.java @@ -0,0 +1,109 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysIndustryTypeDTO; +import com.thing.sys.biz.service.SysIndustryTypeService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; + +/** +* 行业类型 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-01-18 +*/ +@RestController +@RequestMapping("v2/sys/industry/type") +@Tag(name="行业类型") +@RequiredArgsConstructor +public class SysIndustryTypeController { + + private final SysIndustryTypeService sysIndustryTypeService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = sysIndustryTypeService.getPageData(params, SysIndustryTypeDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + SysIndustryTypeDTO data = sysIndustryTypeService.getByIdAs(id, SysIndustryTypeDTO.class); + return new Result().ok(data); + } + + @GetMapping("getByParent/{parentCode}") + @Operation(summary="获取直属子级行业") + public Result> getByParent(@PathVariable("parentCode") String parentCode){ + List data = sysIndustryTypeService.getByParent(parentCode); + return new Result>().ok(data); + } + + @GetMapping("list") + @Operation(summary="二级行业列表") + public Result> listSysIndustryTypeDTO(){ + List data = sysIndustryTypeService.listSysIndustryTypeDTO(); + return new Result>().ok(data); + } + + + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody SysIndustryTypeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + sysIndustryTypeService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody SysIndustryTypeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + sysIndustryTypeService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + sysIndustryTypeService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysMenuController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysMenuController.java new file mode 100644 index 0000000..13f54ed --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysMenuController.java @@ -0,0 +1,183 @@ + + +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.enumeration.MenuTypeEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.HomeNavReq; +import com.thing.sys.biz.dto.SysMenuDTO; +import com.thing.sys.biz.entity.SysMenuEntity; +import com.thing.sys.biz.service.SysMenuService; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.security.service.ShiroService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 菜单管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/menu") +@Tag(name = "菜单管理") +@RequiredArgsConstructor +public class SysMenuController { + private final SysMenuService sysMenuService; + private final ShiroService shiroService; + + @GetMapping("nav") + @Operation(summary="导航") + public Result> nav(@RequestParam(required = false) String flag) { + UserDetail user = SecurityUser.getUser(); + List list = sysMenuService.getUserMenuList(user, MenuTypeEnum.MENU.value(),flag); + + return new Result>().ok(list); + } + + + @GetMapping("homeNav") + @Operation(summary="首页导航") + public Result> homeNav(@RequestParam(required = false) String flag) { + + List result= new ArrayList<>(); + + UserDetail user = SecurityUser.getUser(); + List list = sysMenuService.getUserMenuListHomeNav(user, MenuTypeEnum.MENU.value(),flag); + list.removeIf(data -> !Objects.equals(data.getModel(), "1")); + list.forEach(temp->{ + if(temp.getPid()==159127079116378112L){ + temp.setPid(0L); + } + }); + Map> groupedByModelGroup = list.stream() + .collect(Collectors.groupingBy(SysMenuEntity::getPid)); + + groupedByModelGroup.forEach((k, v) -> { + HomeNavReq req= new HomeNavReq(); + SysMenuDTO entity = sysMenuService.get(k); + if(!k.equals(0L)){ + req.setSort(entity.getSort()); + req.setTitleName(entity.getName()); + }else { + req.setSort(99); + req.setTitleName("公共服务平台"); + } + req.setMenuEntities(v); + result.add(req); + }); + result.sort(Comparator.comparingInt(HomeNavReq::getSort)); + return new Result>().ok(result); + } + @GetMapping("permissions") + @Operation(summary="权限标识") + public Result> permissions() { + UserDetail user = SecurityUser.getUser(); + Set set = shiroService.getUserPermissions(user); + + return new Result>().ok(set); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameter(name = "type",description ="菜单类型 0:菜单 1:按钮 null:全部") + public Result> list(Integer type) { + List list = sysMenuService.getAllMenuList(type); + + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:menu:info") + public Result get(@PathVariable("id") Long id) { + SysMenuDTO data = sysMenuService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:menu:save") + public Result save(@RequestBody SysMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysMenuService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:menu:update") + public Result update(@RequestBody SysMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, DefaultGroup.class); + + sysMenuService.update(dto); + + return new Result(); + } + + @DeleteMapping("{id}") + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:menu:delete") + public Result delete(@PathVariable("id") Long id) { + //效验数据 + AssertUtils.isNull(id, "id"); + + //判断是否有子菜单或按钮 + List list = sysMenuService.getListPid(id); + if (list.size() > 0) { + return new Result().error(ErrorCode.SUB_MENU_EXIST); + } + + sysMenuService.delete(id); + + return new Result(); + } + + @GetMapping("select") + @Operation(summary="角色菜单权限") + @RequiresPermissions("sys:menu:select") + public Result> select() { + UserDetail user = SecurityUser.getUser(); + List list = sysMenuService.getUserMenuList(user, null,null); + + return new Result>().ok(list); + } + + @GetMapping("select/comp") + @Operation(summary="企业角色菜单权限") + public Result> companySelect() { + List list = sysMenuService.companySelect(); + + return new Result>().ok(list); + } + + @GetMapping("selectMenu") + @Operation(summary="角色id查询菜单权限") + public Result> selectMenu(Long id) { + + List list = sysMenuService.selectMenu(id); + + return new Result>().ok(list); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysOnlineController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysOnlineController.java new file mode 100644 index 0000000..60a5b67 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysOnlineController.java @@ -0,0 +1,60 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.entity.SysOnlineEntity; +import com.thing.sys.biz.service.SysUserTokenService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + +/** + * 在线用户 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/online") +@Tag(name = "在线用户") +@RequiredArgsConstructor +public class SysOnlineController { + private final SysUserTokenService sysUserTokenService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "username",description ="用户名") + }) + @RequiresPermissions("sys:online:all") + public Result> page( @RequestParam Map params) { + PageData page = sysUserTokenService.onlinePage(params); + + return new Result>().ok(page); + } + + @PostMapping("logout") + @Operation(summary="踢出") + @RequiresPermissions("sys:online:all") + public Result logout(Long id) { + //效验数据 + AssertUtils.isNull(id, "id"); + + //退出 + sysUserTokenService.logout(id); + + return new Result(); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysParamsController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysParamsController.java new file mode 100644 index 0000000..c261126 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysParamsController.java @@ -0,0 +1,140 @@ +package com.thing.sys.biz.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysParamsDTO; +import com.thing.sys.biz.excel.SysParamsExcel; +import com.thing.sys.biz.service.SysParamsService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/params") +@Tag(name = "系统参数管理") +@RequiredArgsConstructor +public class SysParamsController { + private final SysParamsService sysParamsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "paramCode",description ="参数编码") + }) + @RequiresPermissions("sys:params:page") + public Result> page( @RequestParam Map params) { + PageData page = sysParamsService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:params:info") + public Result get(@PathVariable("id") Long id) { + SysParamsDTO data = sysParamsService.get(id); + + return new Result().ok(data); + } + + @GetMapping("type/{type}") + @Operation(summary="类型") + public Result getByType(@PathVariable("type") String type) { + String data = sysParamsService.getValue(type); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:params:save") + public Result save(@RequestBody SysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysParamsService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:params:update") + public Result update(@RequestBody SysParamsDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysParamsService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:params:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysParamsService.delete(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @RequiresPermissions("sys:params:export") + @Parameter(name = "paramCode",description ="参数编码") + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysParamsService.list(params); + List idList = Lists.newArrayList(ids); + if (CollectionUtil.isNotEmpty(idList)) + list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + List excelList = ConvertUtils.sourceToTarget(list, SysParamsExcel.class); + ExcelUtils.exportExcel(excelList, null, "参数管理", SysParamsExcel.class, "参数管理.xls", response); + } + + @GetMapping("paramCode") + @Operation(summary = "通过编码获取值") + public Result getValueByParamCode(@RequestParam String paramCode){ + return new Result().ok(sysParamsService.getValue(paramCode)); + } + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysPostController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysPostController.java new file mode 100644 index 0000000..0b4ba08 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysPostController.java @@ -0,0 +1,117 @@ + + +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysPostDTO; +import com.thing.sys.biz.entity.SysPostEntity; +import com.thing.sys.biz.service.SysPostService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 岗位管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/post") +@Tag(name = "岗位管理") +@RequiredArgsConstructor +public class SysPostController { + private final SysPostService sysPostService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + //@RequiresPermissions("sys:post:page") + public Result> page( @RequestParam Map params) { + PageData page = sysPostService.getPageData(params, SysPostDTO.class); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list() { + Map params = new HashMap<>(); + //正常岗位列表 + params.put("status", "1"); + List data = sysPostService.list(params); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("sys:post:info") + public Result get(@PathVariable("id") Long id) { + SysPostDTO data = sysPostService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("sys:post:save") + public Result save(@RequestBody SysPostDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysPostService.save(ConvertUtils.sourceToTarget(dto, SysPostEntity.class)); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("sys:post:update") + // @StatusCheck + public Result update(@RequestBody SysPostDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysPostService.getMapper().update(ConvertUtils.sourceToTarget(dto, SysPostEntity.class)); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("sys:post:delete") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysPostService.delete(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRegionController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRegionController.java new file mode 100644 index 0000000..0bf70a9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRegionController.java @@ -0,0 +1,119 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysRegionDTO; +import com.thing.sys.biz.region.RegionProvince; +import com.thing.sys.biz.service.SysRegionService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 行政区域 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/region") +@Tag(name = "行政区域") +@RequiredArgsConstructor +public class SysRegionController { + private final SysRegionService sysRegionService; + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = "pid",description ="上级ID") + }) + // @RequiresPermissions("sys:region:list") + public Result> list(@RequestParam Map params) { + List list = sysRegionService.list(params); + + return new Result>().ok(list); + } + + @GetMapping("tree") + @Operation(summary="树形数据") + public Result>> tree() { + List> list = sysRegionService.getTreeList(); + + return new Result>>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:region:info") + public Result get(@PathVariable("id") Long id) { + SysRegionDTO data = sysRegionService.get(id); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + // @RequiresPermissions("sys:region:save") + public Result save(@RequestBody SysRegionDTO dto) { + // 效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysRegionService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + // @RequiresPermissions("sys:region:update") + public Result update(@RequestBody SysRegionDTO dto) { + // 效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysRegionService.update(dto); + + return new Result(); + } + + @DeleteMapping("{id}") + @Operation(summary="删除") + @LogOperation("删除") + // @RequiresPermissions("sys:region:delete") + public Result delete(@PathVariable("id") Long id) { + // 效验数据 + AssertUtils.isNull(id, "id"); + + int count = sysRegionService.getCountByPid(id); + if (count > 0) { + throw new SysException(ErrorCode.REGION_SUB_DELETE_ERROR); + } + + sysRegionService.delete(id); + + return new Result(); + } + + @GetMapping("region") + @Operation(summary="地区列表") + public Result> region( + @RequestParam(value = "threeLevel", defaultValue = "true") boolean threeLevel) { + List list = sysRegionService.getRegion(threeLevel); + + return new Result>().ok(list); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRoleController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRoleController.java new file mode 100644 index 0000000..0e98272 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysRoleController.java @@ -0,0 +1,123 @@ +package com.thing.sys.biz.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.service.SysRoleDataScopeService; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysRoleService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 角色管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/role") +@Tag(name="角色管理") +@RequiredArgsConstructor +public class SysRoleController { + private final SysRoleService sysRoleService; + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleDataScopeService sysRoleDataScopeService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "name",description ="角色名") + }) + //@RequiresPermissions("sys:role:page") + public Result> page( @RequestParam Map params){ + PageData page = sysRoleService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + @RequiresPermissions("sys:role:list") + public Result> list(){ + List data = sysRoleService.list(new HashMap<>(1)); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("sys:role:info") + public Result get(@PathVariable("id") Long id){ + SysRoleDTO data = sysRoleService.get(id); + + //查询角色对应的菜单 + List menuIdList = sysRoleMenuService.getMenuIdList(id); + data.setMenuIdList(menuIdList); + + //查询角色对应的数据权限 + List deptIdList = sysRoleDataScopeService.getDeptIdList(id); + data.setDeptIdList(deptIdList); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("sys:role:save") + public Result save(@RequestBody SysRoleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysRoleService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("sys:role:update") + public Result update(@RequestBody SysRoleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysRoleService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("sys:role:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysRoleService.delete(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserController.java new file mode 100644 index 0000000..3642152 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserController.java @@ -0,0 +1,242 @@ +package com.thing.sys.biz.controller; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.BindDTO; +import com.thing.sys.biz.dto.PasswordDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.dto.SysUserMenuDTO; +import com.thing.sys.biz.excel.SysUserExcel; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.biz.service.SysUserMenuService; +import com.thing.sys.biz.service.SysUserPostService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.*; +import com.thing.password.PasswordUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 用户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/user") +@Tag(name="用户管理") +@RequiredArgsConstructor +public class SysUserController { + private final SysUserService sysUserService; + private final SysRoleUserService sysRoleUserService; + private final SysUserPostService sysUserPostService; + private final SysTenantService sysTenantService; + private final SysUserMenuService sysUserMenuService; + + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "username",description ="用户名"), + @Parameter(name = "keyword",description ="手机号/邮箱"), + @Parameter(name = "deptId",description ="部门ID") + }) + public Result> page( @RequestParam Map params){ + PageData page = sysUserService.page(params); + + return new Result>().ok(page); + } + + @Operation(summary="所有用户列表") + @GetMapping("list") + public Result> list(@RequestParam(required = false) String username){ + Map map = Maps.newHashMapWithExpectedSize(1); + if (StringUtils.isNotBlank(username)) map.put("username", username); + List list = sysUserService.list(map); + return new Result>().ok(list); + } + + @Operation(summary="所有用户列表") + @GetMapping("listWithOutAuth") + public Result> listWithOutAuth(){ + return new Result>().ok(sysUserService.listWithOutAuth()); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("sys:user:info") + public Result get(@PathVariable("id") Long id){ + SysUserDTO data = sysUserService.get(id); + + //用户角色列表 + List roleIdList = sysRoleUserService.getRoleIdList(id); + data.setRoleIdList(roleIdList); + + //用户岗位列表 + List postIdList = sysUserPostService.getPostIdList(id); + data.setPostIdList(postIdList); + + return new Result().ok(data); + } + + @GetMapping("super/{id}") + @Operation(summary="是否为超管") + public Result superAdmin(@PathVariable("id") Long id) { + SysUserDTO user = sysUserService.get(id); + return new Result().ok(Objects.nonNull(user) && Objects.equals(user.getSuperAdmin(), SuperAdminEnum.YES.value())); + } + + @GetMapping("info") + @Operation(summary="登录用户信息") + public Result info(){ + UserDetail user = SecurityUser.getUser(); + SysUserDTO data = ConvertUtils.sourceToTarget(user, SysUserDTO.class); + //当前用户所在租户 + SysTenantDTO tenant = sysTenantService.getTenantCode(TenantContext.getTenantCode(user)); + data.setTenantName(tenant.getTenantName()); + data.setDeptId(tenant.getTenantCode()); + Long tcode = UserContext.getTenantCode(); + SysUserMenuDTO userMenuDTO =sysUserMenuService.queryUrlByTenantcode(tcode); + if (ObjectUtil.isNotNull(userMenuDTO)){ + data.setUrl(userMenuDTO.getUrl()); + data.setLogo(StringUtils.isNotBlank(userMenuDTO.getLogo()) ? userMenuDTO.getLogo() : data.getLogo()); + data.setName(StringUtils.isNotBlank(userMenuDTO.getName()) ? userMenuDTO.getName() : data.getName()); + data.setShowLogo(userMenuDTO.getShowLogo()); + }else { + SysUserMenuDTO sysUserMenuDTO = sysUserMenuService.getSysUserMenuByUserId(data.getId()); + if (ObjectUtil.isNotNull(sysUserMenuDTO)){ + data.setLogo(StringUtils.isNotBlank(sysUserMenuDTO.getLogo()) ? sysUserMenuDTO.getLogo() : null); + data.setName(StringUtils.isNotBlank(sysUserMenuDTO.getName()) ? sysUserMenuDTO.getName() : null); + data.setShowLogo(sysUserMenuDTO.getShowLogo()); + } + } + if(ObjectUtils.isNotEmpty(data.getLogo())){ + data.setLogo(OSSFactory.splice(data.getLogo())); + } + return new Result().ok(data); + } + + @PutMapping("password") + @Operation(summary="修改密码") + @LogOperation("修改密码") + public Result password(@RequestBody PasswordDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto); + + UserDetail user = SecurityUser.getUser(); + + //原密码不正确 + if(!PasswordUtils.matches(dto.getPassword(), user.getPassword())){ + return new Result().error(ErrorCode.PASSWORD_ERROR); + } + + sysUserService.updatePassword(user.getId(), dto.getNewPassword()); + + return new Result(); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("sys:user:save") + public Result save(@RequestBody SysUserDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysUserService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("sys:user:update") + public Result update(@RequestBody SysUserDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysUserService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("sys:user:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + List idList = Arrays.asList(ids); + if(idList.contains(SecurityUser.getUserId())){ + throw new SysException(ErrorCode.DEL_MYSELF_ERROR); + } + sysUserService.delete(ids); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + List list = sysUserService.list(params); + List idList = Lists.newArrayList(ids); + if (CollectionUtil.isNotEmpty(idList)) list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + List excelList = ConvertUtils.sourceToTarget(list, SysUserExcel.class); + ExcelUtils.exportExcel(excelList, null, "用户管理", SysUserExcel.class, "用户管理.xls", response); + } + + @PostMapping("bindOpenId") + @Operation(summary="绑定openid") + public Result bindOpenId(@RequestBody BindDTO bindDTO){ + sysUserService.bindOpenId(bindDTO); + return new Result(); + } + + @Operation(summary="所有用户列表") + @GetMapping("queryUserList") + public Result> queryUserList(@RequestParam(required = false) String tenantCode){ + return new Result>().ok(sysUserService.queryUserList(tenantCode)); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserMenuController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserMenuController.java new file mode 100644 index 0000000..235a6bb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SysUserMenuController.java @@ -0,0 +1,114 @@ +package com.thing.sys.biz.controller; + +import com.mybatisflex.core.constant.SqlConsts; +import com.mybatisflex.core.query.QueryCondition; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysUserMenuDTO; +import com.thing.sys.biz.dto.SysUserMenuPageDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.table.SysUserEntityTableDef; +import com.thing.sys.biz.service.SysUserMenuService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.oss.cloud.OSSFactory; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + + +import java.util.Map; + +/** + * sys_tenant_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +@RestController +@RequestMapping("sys/sysusermenu") +@Tag(name = "用户菜单") +@RequiredArgsConstructor +public class SysUserMenuController { + private final SysUserMenuService sysUserMenuService; + private final SysUserService sysUserService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantName",description ="企业名称") + + }) + public Result> page( @RequestParam Map params) { + PageData page = sysUserMenuService.getSysUserMenuPage(params); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id) { + SysUserMenuDTO data = sysUserMenuService.get(id); + data.setLogo(OSSFactory.splice(data.getLogo())); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody SysUserMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setLogo(OSSFactory.cutOut(dto.getLogo())); + sysUserMenuService.saveSysUserMenu(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody SysUserMenuDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + dto.setLogo(OSSFactory.cutOut(dto.getLogo())); + sysUserMenuService.updateDto(dto); + /*sysUserService.update(new SysUserEntity(), Wrappers.lambdaUpdate().eq(SysUserEntity::getId, dto.getUserId()) + .set(SysUserEntity::getUrl, dto.getUrl()));*/ + SysUserEntity sysUserEntity = new SysUserEntity(); + sysUserEntity.setUrl(dto.getUrl()); + sysUserService.update( + sysUserEntity, + QueryCondition.create( + SysUserEntityTableDef.SYS_USER_ENTITY.ID, + SqlConsts.EQUALS, + dto.getUserId())); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysUserMenuService.deleteByIds(ids); + + return new Result(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/controller/SystemController.java b/modules/thing/src/main/java/com/thing/sys/biz/controller/SystemController.java new file mode 100644 index 0000000..52bd20d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/controller/SystemController.java @@ -0,0 +1,54 @@ +package com.thing.sys.biz.controller; + +import com.sun.management.OperatingSystemMXBean; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SystemDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.lang.management.ManagementFactory; +import java.math.BigDecimal; +import java.math.RoundingMode; + +/** + * 系统接口 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@Tag(name="系统接口") +public class SystemController { + + @GetMapping("sys/info") + @Operation(summary="系统信息") + public Result info(){ + OperatingSystemMXBean osmx = (OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean(); + + SystemDTO dto = new SystemDTO(); + dto.setSysTime(System.currentTimeMillis()); + dto.setOsName(System.getProperty("os.name")); + dto.setOsArch(System.getProperty("os.arch")); + dto.setOsVersion(System.getProperty("os.version")); + dto.setUserLanguage(System.getProperty("user.language")); + dto.setUserDir(System.getProperty("user.dir")); + dto.setTotalPhysical(osmx.getTotalPhysicalMemorySize()/1024/1024); + dto.setFreePhysical(osmx.getFreePhysicalMemorySize()/1024/1024); + dto.setMemoryRate(BigDecimal.valueOf((1-osmx.getFreePhysicalMemorySize()*1.0/osmx.getTotalPhysicalMemorySize())*100).setScale(2, RoundingMode.HALF_UP)); + dto.setProcessors(osmx.getAvailableProcessors()); + dto.setJvmName(System.getProperty("java.vm.name")); + dto.setJavaVersion(System.getProperty("java.version")); + dto.setJavaHome(System.getProperty("java.home")); + dto.setJavaTotalMemory(Runtime.getRuntime().totalMemory()/1024/1024); + dto.setJavaFreeMemory(Runtime.getRuntime().freeMemory()/1024/1024); + dto.setJavaMaxMemory(Runtime.getRuntime().maxMemory()/1024/1024); + dto.setUserName(System.getProperty("user.name")); + dto.setSystemCpuLoad(BigDecimal.valueOf(osmx.getSystemCpuLoad()*100).setScale(2, RoundingMode.HALF_UP)); + dto.setUserTimezone(System.getProperty("user.timezone")); + + return new Result().ok(dto); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/BindDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/BindDTO.java new file mode 100644 index 0000000..a6e1fd6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/BindDTO.java @@ -0,0 +1,21 @@ +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + + +@Data +@Schema( name= "绑定参数") +public class BindDTO { + @Schema(description = "用户名/手机/邮箱", required = true) + @NotBlank(message="用户名/手机/邮箱不能为空") + private String username; + @Schema(description = "密码") + @NotBlank(message="密码不能为空") + private String password; + @Schema(description = "微信公众号") + @NotBlank(message="微信公众号code不能为空") + private String code; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/CtlUserDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/CtlUserDTO.java new file mode 100644 index 0000000..6ec2408 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/CtlUserDTO.java @@ -0,0 +1,18 @@ +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +/** + * @author zhenghh. 2023-02-10 + **/ +@Data +@Schema( name= "用户选择框") +public class CtlUserDTO { + + @Schema(description = "主键") + private Long id; + @Schema(description = "名称") + private String realName; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/HomeNavReq.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/HomeNavReq.java new file mode 100644 index 0000000..55e9b67 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/HomeNavReq.java @@ -0,0 +1,18 @@ +package com.thing.sys.biz.dto; + + +import com.thing.sys.biz.entity.SysMenuEntity; +import lombok.Data; + +import java.util.List; + +@Data +public class HomeNavReq { + + private Integer sort; + + private String titleName; + + private List menuEntities; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/PasswordDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/PasswordDTO.java new file mode 100644 index 0000000..4477f66 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/PasswordDTO.java @@ -0,0 +1,33 @@ + + +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 修改密码 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "修改密码") +public class PasswordDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "原密码") + @NotBlank(message = "{sysuser.password.require}") + private String password; + + @Schema(description = "新密码") + @NotBlank(message = "{sysuser.password.require}") + private String newPassword; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDeptDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDeptDTO.java new file mode 100644 index 0000000..7212f13 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDeptDTO.java @@ -0,0 +1,62 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.params.TreeNode; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 部门管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "部门管理") +@EqualsAndHashCode(callSuper = true) +public class SysDeptDTO extends TreeNode implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "上级ID") + @NotNull(message = "{sysdept.pid.require}", groups = DefaultGroup.class) + private Long pid; + + @Schema(description = "部门名称") + @NotBlank(message = "{sysdept.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(description = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "上级部门名称") + private String parentName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictDataDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictDataDTO.java new file mode 100644 index 0000000..5832ca2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictDataDTO.java @@ -0,0 +1,66 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 字典数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "字典数据") +public class SysDictDataDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "字典类型ID") + @NotNull(message = "{sysdict.type.require}", groups = DefaultGroup.class) + private Long dictTypeId; + + @Schema(description = "字典标签") + @NotBlank(message = "{sysdict.label.require}", groups = DefaultGroup.class) + private String dictLabel; + + @Schema(description = "字典值") + private String dictValue; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeDTO.java new file mode 100644 index 0000000..14472f0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeDTO.java @@ -0,0 +1,62 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "字典类型") +public class SysDictTypeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "字典类型") + @NotBlank(message = "{sysdict.type.require}", groups = DefaultGroup.class) + private String dictType; + + @Schema(description = "字典名称") + @NotBlank(message = "{sysdict.name.require}", groups = DefaultGroup.class) + private String dictName; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Date updateDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeListDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeListDTO.java new file mode 100644 index 0000000..fe1ba4e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysDictTypeListDTO.java @@ -0,0 +1,41 @@ +package com.thing.sys.biz.dto; + +import com.thing.common.orm.entity.BaseEntity; +import com.thing.sys.biz.entity.SysDictDataEntity; +import lombok.Data; + +import java.util.Date; +import java.util.List; + +@Data +public class SysDictTypeListDTO extends BaseEntity { + + private static final long serialVersionUID = 1L; + /** + * 字典类型 + */ + private String dictType; + /** + * 字典名称 + */ + private String dictName; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; + /** + * 更新者 + */ + private Long updater; + /** + * 更新时间 + */ + private Long updateDate; + + private List dataList; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysIndustryTypeDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysIndustryTypeDTO.java new file mode 100644 index 0000000..78dcca3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysIndustryTypeDTO.java @@ -0,0 +1,41 @@ +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 行业类型 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-01-18 +*/ +@Data +@Schema( description= "行业类型") +public class SysIndustryTypeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "编码") + private String code; + @Schema(description = "父节编码") + private String parentCode; + @Schema(description = "行业名称") + private String name; + @Schema(description = "行业全名") + private String fullName; + @Schema(description = "是否能源加工转换类行业") + private Object jgzh; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业ID") + private Long deptId; + @Schema(description = "删除状态 0未删除 1删除") + private Integer deleteStatus; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysMenuDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysMenuDTO.java new file mode 100644 index 0000000..b41942c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysMenuDTO.java @@ -0,0 +1,89 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.params.TreeNode; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.Null; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.hibernate.validator.constraints.Range; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 菜单管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "菜单管理") +@EqualsAndHashCode(callSuper = true) +public class SysMenuDTO extends TreeNode implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + //@NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "上级ID") + //@NotNull(message="{sysmenu.pid.require}", groups = DefaultGroup.class) + private Long pid; + + @Schema(description = "菜单名称") + //@NotBlank(message="{sysmenu.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(description = "菜单URL") + private String url; + + @Schema(description = "类型 0:菜单 1:按钮") + @Range(min = 0, max = 1, message = "{sysmenu.type.range}", groups = DefaultGroup.class) + private Integer type; + + @Schema(description = "打开方式 0:内部 1:外部") + @Range(min = 0, max = 1, message = "{sysmenu.openstyle.range}", groups = DefaultGroup.class) + private Integer openStyle; + + @Schema(description = "菜单图标") + private String icon; + + @Schema(description = "授权(多个用逗号分隔,如:sys:user:list,sys:user:save)") + private String permissions; + + @Schema(description = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Integer sort; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "上级菜单名称") + private String parentName; + + @Schema(description = "超管可见 0:否 1:是") + @Range(min = 0, max = 1, message = "超管是否可见只能选择0或1", groups = DefaultGroup.class) + private Integer saView; + + @Schema(description = "说明") + private String description; + @Schema(description = "模式,1首页卡片,2普通路由") + private String model; + @Schema(description = "模式图片地址") + private String modelUrl; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysParamsDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysParamsDTO.java new file mode 100644 index 0000000..d577b57 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysParamsDTO.java @@ -0,0 +1,60 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "参数管理") +public class SysParamsDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "参数编码") + @NotBlank(message = "{sysparams.paramcode.require}", groups = DefaultGroup.class) + private String paramCode; + + @Schema(description = "参数值") + @NotBlank(message = "{sysparams.paramvalue.require}", groups = DefaultGroup.class) + private String paramValue; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysPostDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysPostDTO.java new file mode 100644 index 0000000..087af50 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysPostDTO.java @@ -0,0 +1,41 @@ + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 岗位管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "岗位管理") +public class SysPostDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "岗位编码") + private String postCode; + @Schema(description = "岗位名称") + private String postName; + @Schema(description = "排序") + private Integer sort; + @Schema(description = "状态") + private Integer status; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRegionDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRegionDTO.java new file mode 100644 index 0000000..4e6ed67 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRegionDTO.java @@ -0,0 +1,67 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Min; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 行政区域 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "行政区域") +public class SysRegionDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "区域标识") + @NotNull(message = "{id.require}", groups = DefaultGroup.class) + private Long id; + + @Schema(description = "上级区域ID") + @NotNull(message = "{region.pid.require}", groups = DefaultGroup.class) + private Long pid; + + @Schema(description = "天气编码") + private String weatherCode; + + @Schema(description = "天气编码") + private String weatherUrlCode; + + @Schema(description = "区域名称") + @NotBlank(message = "{region.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(description = "排序") + @Min(value = 0, message = "{sort.number}", groups = DefaultGroup.class) + private Long sort; + + @Schema(description = "上级区域名称") + private String parentName; + + @Schema(description = "是否有子节点") + private Boolean hasChildren; + + @Schema(description = "层级") + private Integer treeLevel; + + @Schema(description = "更新时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date updateDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRoleDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRoleDTO.java new file mode 100644 index 0000000..eaaa88e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysRoleDTO.java @@ -0,0 +1,64 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 角色管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "角色管理") +public class SysRoleDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "角色名称") + @NotBlank(message = "{sysrole.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "是否默认角色") + private Boolean defaultRole; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_PATTERN_STR) + private Date createDate; + + @Schema(description = "菜单ID列表") + private List menuIdList; + + @Schema(description = "部门ID列表") + private List deptIdList; + + @Schema(description = "角色类型,0-企业角色,1-用户角色,2-默认角色") + private String type; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserDTO.java new file mode 100644 index 0000000..b671685 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserDTO.java @@ -0,0 +1,129 @@ + + +package com.thing.sys.biz.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 用户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "用户管理") +public class SysUserDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message = "{id.null}", groups = AddGroup.class) + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "用户名", required = true) + @NotBlank(message = "{sysuser.username.require}", groups = DefaultGroup.class) + private String username; + + @Schema(description = "密码") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @NotBlank(message = "{sysuser.password.require}", groups = AddGroup.class) + private String password; + + @Schema(description = "姓名", required = true) + @NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class) + private String realName; + + @Schema(description = "头像") + private String headUrl; + + @Schema(description = "性别 0:男 1:女 2:保密", required = true) + @Range(min = 0, max = 2, message = "{sysuser.gender.range}", groups = DefaultGroup.class) + private Integer gender; + + @Schema(description = "邮箱") + @Email(message = "{sysuser.email.error}", groups = DefaultGroup.class) + private String email; + + @Schema(description = "手机号") + private String mobile; + + @Schema(description = "部门ID", required = true) + @NotNull(message = "{sysuser.deptId.require}", groups = DefaultGroup.class) + private Long deptId; + + @Schema(description = "状态 0:停用 1:正常", required = true) + @Range(min = 0, max = 1, message = "{sysuser.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + @Schema(description = "超级管理员 0:否 1:是") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Integer superAdmin; + + @Schema(description = "超级租户 0:否 1:是") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Integer superTenant; + + @Schema(description = "租户组管理员 0:否 1:是") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private Integer tenantGroup; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "角色ID列表") + private List roleIdList; + + @Schema(description = "角色名称") + private String roleNameStr; + + @Schema(description = "岗位ID列表") + private List postIdList; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "租户名称") + private String tenantName; + + @Schema(description = "openId") + private String openId; + + @Schema(description = "路由") + private String url; + + @Schema(description = "平台名称") + private String name; + + @Schema(description = "菜单URL") + private String logo; + + @Schema(description = "是否缩进展示logo") + private Boolean showLogo; + + @Schema(description = "授权可跳转的企业列表") + private String tenantList; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuDTO.java new file mode 100644 index 0000000..2f0597e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuDTO.java @@ -0,0 +1,48 @@ +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * sys_tenant_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +@Data +@Schema( name= "sys_user_menu") +public class SysUserMenuDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "用户id") + private Long userId; + @Schema(description = "菜单id") + private Long menuId; + @Schema(description = "菜单URL") + private String url; + @Schema(description = "平台名称") + private String name; + @Schema(description = "菜单URL") + private String logo; + @Schema(description = "是否缩进展示logo") + private Boolean showLogo; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Date createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Date updateDate; + + @Schema(description = "更新时间") + private Long tenantCode; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuPageDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuPageDTO.java new file mode 100644 index 0000000..a0b26e7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SysUserMenuPageDTO.java @@ -0,0 +1,28 @@ +package com.thing.sys.biz.dto; + +import com.thing.common.orm.dto.BaseDTO; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +@Data +@EqualsAndHashCode(callSuper = true) +public class SysUserMenuPageDTO extends BaseDTO { + + @Schema(description = "id") + private Long id; + @Schema(description = "企业名称") + private String tenantName; + @Schema(description = "菜单名称") + private String menuName; + @Schema(description = "菜单URL") + private String url; + @Schema(description = "平台名称") + private String name; + @Schema(description = "菜单URL") + private String logo; + @Schema(description = "是否缩进展示logo") + private Boolean showLogo; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/dto/SystemDTO.java b/modules/thing/src/main/java/com/thing/sys/biz/dto/SystemDTO.java new file mode 100644 index 0000000..aaa99e1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/dto/SystemDTO.java @@ -0,0 +1,44 @@ + + +package com.thing.sys.biz.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 系统数据 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "系统数据") +public class SystemDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long sysTime; + private String osName; + private String osArch; + private String osVersion; + private String userLanguage; + private String userDir; + private Long totalPhysical; + private Long freePhysical; + private BigDecimal memoryRate; + private Integer processors; + private String jvmName; + private String javaVersion; + private String javaHome; + private Long javaTotalMemory; + private Long javaFreeMemory; + private Long javaMaxMemory; + private String userName; + private BigDecimal systemCpuLoad; + private String userTimezone; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/DictData.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/DictData.java new file mode 100644 index 0000000..5ba0148 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/DictData.java @@ -0,0 +1,23 @@ + + +package com.thing.sys.biz.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +/** + * 字典数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class DictData { + @JsonIgnore + private Long dictTypeId; + private String dictLabel; + private String dictValue; + /** + * 排序 + */ + private Integer sort; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/DictType.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/DictType.java new file mode 100644 index 0000000..37f0fe7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/DictType.java @@ -0,0 +1,22 @@ + + +package com.thing.sys.biz.entity; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.Data; + +import java.util.ArrayList; +import java.util.List; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class DictType { + @JsonIgnore + private Long id; + private String dictType; + private List dataList = new ArrayList<>(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDeptEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDeptEntity.java new file mode 100644 index 0000000..f5aa224 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDeptEntity.java @@ -0,0 +1,46 @@ + + +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 部门管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_dept") +public class SysDeptEntity extends BaseTenantEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 上级ID + */ + private Long pid; + /** + * 所有上级ID,用逗号分开 + */ + private String pids; + /** + * 部门名称 + */ + private String name; + /** + * 排序 + */ + private Integer sort; + /** + * 上级部门名称 + */ + @Column(ignore = true) + private String parentName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictDataEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictDataEntity.java new file mode 100644 index 0000000..78d05b5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictDataEntity.java @@ -0,0 +1,43 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 数据字典 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("sys_dict_data") +public class SysDictDataEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 字典类型ID + */ + private Long dictTypeId; + /** + * 字典标签 + */ + private String dictLabel; + /** + * 字典值 + */ + private String dictValue; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictTypeEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictTypeEntity.java new file mode 100644 index 0000000..e8b68bb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysDictTypeEntity.java @@ -0,0 +1,39 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("sys_dict_type") +public class SysDictTypeEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 字典类型 + */ + private String dictType; + /** + * 字典名称 + */ + private String dictName; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Integer sort; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysIndustryTypeEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysIndustryTypeEntity.java new file mode 100644 index 0000000..44a5366 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysIndustryTypeEntity.java @@ -0,0 +1,64 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 行业类型 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-01-18 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_industry_type") +public class SysIndustryTypeEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 编码 + */ + private String code; + /** + * 父节编码 + */ + private String parentCode; + /** + * 行业名称 + */ + private String name; + /** + * 行业全名 + */ + private String fullName; + /** + * 是否能源加工转换类行业 + */ + private Object jgzh; + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 企业ID + */ + private Long deptId; + /** + * 删除状态 0未删除 1删除 + */ + private Integer deleteStatus; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysLanguageEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysLanguageEntity.java new file mode 100644 index 0000000..5b5b63f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysLanguageEntity.java @@ -0,0 +1,41 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 国际化 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Table("sys_language") +public class SysLanguageEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 表名 + */ + private String tableName; + /** + * 表主键 + */ + private Long tableId; + /** + * 字段名 + */ + private String fieldName; + /** + * 字段值 + */ + private String fieldValue; + /** + * 语言 + */ + private String language; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysMenuEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysMenuEntity.java new file mode 100644 index 0000000..912f66f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysMenuEntity.java @@ -0,0 +1,77 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 菜单管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("sys_menu") +public class SysMenuEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 父菜单ID,一级菜单为0 + */ + private Long pid; + /** + * 菜单名称 + */ + @Column(ignore = true) + private String name; + /** + * 菜单URL + */ + private String url; + /** + * 授权(多个用逗号分隔,如:sys:user:list,sys:user:save) + */ + private String permissions; + /** + * 类型 0:菜单 1:按钮 + */ + private Integer type; + /** + * 打开方式 0:内部 1:外部 + */ + private Integer openStyle; + /** + * 菜单图标 + */ + private String icon; + /** + * 排序 + */ + private Integer sort; + /** + * 上级菜单名称 + */ + @Column(ignore = true) + private String parentName; + + /** + * 超管可见 0:否 1:是 + */ + private Integer saView; + /** + * 说明 + */ + private String description; + + private String model; + + private String modelUrl; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysOnlineEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysOnlineEntity.java new file mode 100644 index 0000000..a47f25d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysOnlineEntity.java @@ -0,0 +1,44 @@ + +package com.thing.sys.biz.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 在线用户 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class SysOnlineEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + /** + * 用户名 + */ + private String username; + /** + * 姓名 + */ + private String realName; + /** + * 登录时间 + */ + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date loginDate; + /** + * 超时时间 + */ + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date expireDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysParamsEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysParamsEntity.java new file mode 100644 index 0000000..dc9fa0e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysParamsEntity.java @@ -0,0 +1,42 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("sys_params") +public class SysParamsEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 参数编码 + */ + private String paramCode; + /** + * 参数值 + */ + private String paramValue; + /** + * 类型 0:系统参数 1:非系统参数 + */ + private Integer paramType; + /** + * 备注 + */ + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysPostEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysPostEntity.java new file mode 100644 index 0000000..1dc7e27 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysPostEntity.java @@ -0,0 +1,37 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 岗位管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_post") +public class SysPostEntity extends BaseTenantEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 岗位编码 + */ + private String postCode; + /** + * 岗位名称 + */ + private String postName; + /** + * 排序 + */ + private Integer sort; + /** + * 状态 + */ + private Integer status; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRegionEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRegionEntity.java new file mode 100644 index 0000000..2de6a37 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRegionEntity.java @@ -0,0 +1,52 @@ + + +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 行政区域 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = true) +@Table("sys_region") +public class SysRegionEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 上级ID,一级为0 + */ + private Long pid; + /** + * 名称 + */ + private String name; + /** + * 层级 + */ + private Integer treeLevel; + /** + * 排序 + */ + private Long sort; + /** + * 是否叶子节点 0:否 1:是 + */ + private Integer leaf; + /** + * 上级名称 + */ + @Column(ignore = true) + private String parentName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleDataScopeEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleDataScopeEntity.java new file mode 100644 index 0000000..d9a1a4a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleDataScopeEntity.java @@ -0,0 +1,34 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 角色数据权限 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_role_data_scope") +public class SysRoleDataScopeEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + /** + * 部门ID + */ + private Long deptId; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleEntity.java new file mode 100644 index 0000000..bc6720c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleEntity.java @@ -0,0 +1,38 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 角色 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_role") +public class SysRoleEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色名称 + */ + private String name; + /** + * 备注 + */ + private String remark; + /** + * 是否默认角色 + */ + private Boolean defaultRole; + /** + * 角色类型,0-企业角色,1-用户角色,2-默认角色 + */ + private String type; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleMenuEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleMenuEntity.java new file mode 100644 index 0000000..53325b4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleMenuEntity.java @@ -0,0 +1,33 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 角色菜单关系 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_role_menu") +public class SysRoleMenuEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 角色ID + */ + private Long roleId; + /** + * 菜单ID + */ + private Long menuId; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleUserEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleUserEntity.java new file mode 100644 index 0000000..8fcbff5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysRoleUserEntity.java @@ -0,0 +1,32 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 角色用户关系 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_role_user") +public class SysRoleUserEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 角色ID + */ + private Long roleId; + /** + * 用户ID + */ + private Long userId; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserEntity.java new file mode 100644 index 0000000..be27906 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserEntity.java @@ -0,0 +1,88 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 系统用户 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_user") +public class SysUserEntity extends BaseTenantEntity { + @Serial + private static final long serialVersionUID = 1L; + /** + * 用户名 + */ + private String username; + /** + * 密码 + */ + private String password; + /** + * 姓名 + */ + private String realName; + /** + * 头像 + */ + private String headUrl; + /** + * 性别 0:男 1:女 2:保密 + */ + private Integer gender; + /** + * 邮箱 + */ + private String email; + /** + * 手机号 + */ + private String mobile; + /** + * 部门ID + */ + private Long deptId; + /** + * 超级管理员 0:否 1:是 + */ + private Integer superAdmin; + /** + * 租户管理员 0:否 1:是 + */ + private Integer superTenant; + /** + * 租户组管理员 0:否 1:是 + */ + private Integer tenantGroup; + /** + * 状态 0:停用 1:正常 + */ + private Integer status; + /** + * 部门名称 + */ + @Column(ignore = true) + private String deptName; + /** + * openId + */ + private String openId; + /** + * 路由 + */ + private String url; + +// /** +// * 企业列表 +// */ +// private String tenantList; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserMenuEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserMenuEntity.java new file mode 100644 index 0000000..4b6e30d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserMenuEntity.java @@ -0,0 +1,47 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseTenantEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * sys_tenant_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_user_menu") +public class SysUserMenuEntity extends BaseTenantEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户编码 + */ + private Long userId; + /** + * 菜单id + */ + private Long menuId; + /** + * 菜单URL + */ + private String url; + /** + * 平台名称 + */ + private String name; + /** + * logo + */ + private String logo; + /** + * 是否缩进展示logo + */ + private Boolean showLogo; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserPostEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserPostEntity.java new file mode 100644 index 0000000..d192c43 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserPostEntity.java @@ -0,0 +1,30 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 用户岗位关系 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("sys_user_post") +public class SysUserPostEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 岗位ID + */ + private Long postId; + /** + * 用户ID + */ + private Long userId; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserTokenEntity.java b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserTokenEntity.java new file mode 100644 index 0000000..c52c812 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/entity/SysUserTokenEntity.java @@ -0,0 +1,45 @@ +package com.thing.sys.biz.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 系统用户Token + */ +@Data +@Table("sys_user_token") +public class SysUserTokenEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * id + */ + @Id + private Long id; + /** + * 用户ID + */ + private Long userId; + /** + * 用户token + */ + private String token; + /** + * 过期时间 + */ + private Date expireDate; + /** + * 更新时间 + */ + private Date updateDate; + /** + * 创建时间 + */ + private Date createDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/excel/SysParamsExcel.java b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysParamsExcel.java new file mode 100644 index 0000000..75e7eef --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysParamsExcel.java @@ -0,0 +1,23 @@ + + +package com.thing.sys.biz.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +public class SysParamsExcel { + @Excel(name="参数编码", height = 20, width = 25) + private String paramCode; + @Excel(name="参数值", height = 20, width = 25) + private String paramValue; + @Excel(name="备注", height = 20, width = 25) + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserExcel.java b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserExcel.java new file mode 100644 index 0000000..82de6c6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserExcel.java @@ -0,0 +1,45 @@ + + +package com.thing.sys.biz.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 用户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class SysUserExcel { + @Excel(name="用户名", height = 20, width = 25) + private String username; + + @Excel(name="姓名", height = 20, width = 25) + private String realName; + + /*@Excel(name="性别", height = 20, width = 20) + private Integer gender;*/ + + @Excel(name="邮箱", height = 20, width = 25) + private String email; + + @Excel(name="手机号", height = 20, width = 25) + private String mobile; + + @Excel(name="部门名称", height = 20, width = 25) + private String deptName; + + @Excel(name="状态", height = 20, width = 20, replace = {"停用_0", "正常_1"}) + private Integer status; + + @Excel(name="备注", height = 20, width = 25) + private String remark; + + @Excel(name="创建时间", height = 20, width = 25, format = "yyyy-MM-dd HH:mm:ss", exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserMenuExcel.java b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserMenuExcel.java new file mode 100644 index 0000000..3c36878 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/excel/SysUserMenuExcel.java @@ -0,0 +1,35 @@ +package com.thing.sys.biz.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * sys_user_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +@Data + +public class SysUserMenuExcel { + @Excel(name = "id") + private Long id; + @Excel(name = "用户id") + private Long userId; + @Excel(name = "菜单id") + private Long menuId; + @Excel(name = "菜单名称") + private Long menuName; + @Excel(name = "菜单URL") + private String url; + @Excel(name = "创建者") + private Long creator; + @Excel(name = "创建时间") + private Date createDate; + @Excel(name = "更新者") + private Long updater; + @Excel(name = "更新时间") + private Date updateDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDeptMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDeptMapper.java new file mode 100644 index 0000000..b916c82 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDeptMapper.java @@ -0,0 +1,50 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 部门管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysDeptMapper extends PowerBaseMapper { + + List getList(Map params); + + SysDeptEntity getById(@Param("id") Long id); + + /** + * 获取所有部门的id、pid列表 + */ + List getIdAndPidList(); + + /** + * 根据部门ID,获取所有子部门ID列表 + * @param id 部门ID + */ + List getSubDeptIdList(String id); + + + /** + * 根据租户Code以及pid=0获取dept信息 + * @param pid + * @param tenantCode + * @return + */ + SysDeptEntity getDeptByPid(Long pid, Long tenantCode); + + /** + * 根据超级用户获取租户code + * @return + */ + List getListbySuperAdmin(); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictDataMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictDataMapper.java new file mode 100644 index 0000000..4fd89ec --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictDataMapper.java @@ -0,0 +1,35 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.entity.DictData; +import com.thing.sys.biz.entity.SysDictDataEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 字典数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysDictDataMapper extends PowerBaseMapper { + + /** + * 字典数据列表 + */ + List getDictDataList(); + + + SysDictDataDTO getDictDataNameByDictTypes(@Param("industryCode")String industryCode); + + SysDictDataDTO getDictDataNameByDictType(@Param("dictValue")String dictValue); + + SysDictDataDTO getIndustryNameByDictType(@Param("industryCode")String industryCode); + + List getDictDataNameByDictTypeNew(@Param("quotaType")String quotaType,@Param("tenantCode")Long tenantCode); + + List getDictDataById(@Param("id") Long id); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictTypeMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictTypeMapper.java new file mode 100644 index 0000000..c9f4cc2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysDictTypeMapper.java @@ -0,0 +1,26 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.DictType; +import com.thing.sys.biz.entity.SysDictTypeEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysDictTypeMapper extends PowerBaseMapper { + + /** + * 字典类型列表 + */ + List getDictTypeList(); + + List getDictListByDictTypeList(@Param("dictTypeList1") List dictTypeList1); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysIndustryTypeMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysIndustryTypeMapper.java new file mode 100644 index 0000000..9a45bdd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysIndustryTypeMapper.java @@ -0,0 +1,23 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.dto.SysIndustryTypeDTO; +import com.thing.sys.biz.entity.SysIndustryTypeEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 行业类型 +* +* @author ssy 2337720667@qq.com +* @since 5.1 2024-01-18 +*/ +@Mapper +public interface SysIndustryTypeMapper extends PowerBaseMapper { + + String getByCode(@Param("code") String code); + + List listSysIndustryTypeDTO(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysLanguageMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysLanguageMapper.java new file mode 100644 index 0000000..e78dfbb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysLanguageMapper.java @@ -0,0 +1,26 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysLanguageEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +/** + * 国际化 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysLanguageMapper extends PowerBaseMapper { + + SysLanguageEntity getLanguage(SysLanguageEntity entity); + + void updateLanguage(SysLanguageEntity entity); + + /** + * 删除国际化 + * @param tableName 表名 + * @param tableId 表主键 + */ + void deleteLanguage(@Param("tableName") String tableName, @Param("tableId") Long tableId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysMenuMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysMenuMapper.java new file mode 100644 index 0000000..ce0a3fd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysMenuMapper.java @@ -0,0 +1,75 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysMenuEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 菜单管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysMenuMapper extends PowerBaseMapper { + + SysMenuEntity getById(@Param("id") Long id, @Param("language") String language); + + SysMenuEntity getByUrl(@Param("url") String url, @Param("language") String language); + + List getByUrlList(@Param("urlList") List urlList, @Param("language") String language); + + /** + * 查询所有菜单列表 + * + * @param type 菜单类型 + * @param language 语言 + */ + List getMenuList(@Param("type") Integer type, @Param("language") String language); + + /** + * 查询超管菜单列表 + * + * @param type 菜单类型 + * @param language 语言 + * @return 菜单列表 + */ + List getSuperAdminMenuList(@Param("type") Integer type, @Param("language") String language,@Param("flag")String flag); + + /** + * 查询用户菜单列表 + * + * @param userId 用户ID + * @param type 菜单类型 + * @param language 语言 + */ + List getUserMenuList(@Param("userId") Long userId, @Param("type") Integer type, @Param("language") String language,@Param("flag")String flag); + + /** + * 查询用户权限列表 + * @param userId 用户ID + */ + List getUserPermissionsList(Long userId); + + /** + * 查询所有权限列表 + */ + List getPermissionsList(); + + /** + * 根据父菜单,查询子菜单 + * @param pid 父菜单ID + */ + List getListPid(Long pid); + + /** + * 查询企业 + * @param tenantCode 租户主键 + * @param type 类型 + * @param language 语言 + * @return list + */ + List getCompanyMenuList(@Param("tenantCode") Long tenantCode, @Param("type") Integer type, @Param("language") String language,@Param("flag")String flag); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysParamsMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysParamsMapper.java new file mode 100644 index 0000000..aed7c96 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysParamsMapper.java @@ -0,0 +1,38 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysParamsEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysParamsMapper extends PowerBaseMapper { + /** + * 根据参数编码,查询value + * @param paramCode 参数编码 + * @return 参数值 + */ + String getValueByCode(String paramCode); + + /** + * 获取参数编码列表 + * @param ids ids + * @return 返回参数编码列表 + */ + List getParamCodeList(Long[] ids); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(@Param("paramCode") String paramCode, @Param("paramValue") String paramValue); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysPostMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysPostMapper.java new file mode 100644 index 0000000..8903c98 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysPostMapper.java @@ -0,0 +1,15 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysPostEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 岗位管理 +* +* @author Mark sunlightcs@gmail.com +*/ +@Mapper +public interface SysPostMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRegionMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRegionMapper.java new file mode 100644 index 0000000..f507575 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRegionMapper.java @@ -0,0 +1,28 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysRegionEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** + * 行政区域 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysRegionMapper extends PowerBaseMapper { + + List getList(Map params); + + List getListByLevel(Integer treeLevel); + + List> getTreeList(); + + SysRegionEntity getById(Long id); + + int getCountByPid(Long pid); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleDataScopeMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleDataScopeMapper.java new file mode 100644 index 0000000..a70551d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleDataScopeMapper.java @@ -0,0 +1,38 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysRoleDataScopeEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色数据权限 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysRoleDataScopeMapper extends PowerBaseMapper { + + /** + * 根据角色ID,获取部门ID列表 + */ + List getDeptIdList(Long roleId); + /** + * + * 根据角色ID,获取部门ID列表 + */ + List getRoleIdList(List deptIdList); + + /** + * 获取用户的部门数据权限列表 + */ + List getDataScopeList(Long userId); + + /** + * 根据角色id,删除角色数据权限关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMapper.java new file mode 100644 index 0000000..faaa5fc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMapper.java @@ -0,0 +1,20 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysRoleEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 角色管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysRoleMapper extends PowerBaseMapper { + + /** + * 新增租户角色 + */ + void insertTenantRole(SysRoleEntity entity); + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMenuMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMenuMapper.java new file mode 100644 index 0000000..34932c3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleMenuMapper.java @@ -0,0 +1,39 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysRoleMenuEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色与菜单对应关系 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysRoleMenuMapper extends PowerBaseMapper { + + /** + * 根据角色ID,获取菜单ID列表 + */ + List getMenuIdList(Long roleId); + + /** + * 根据角色id,删除角色菜单关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据菜单id,删除角色菜单关系 + * @param menuId 菜单id + */ + void deleteByMenuId(Long menuId); + + /** + * 根据菜单id,删除角色菜单关系 + * @param menuIdList 菜单idLists + */ + void deleteByMenuIds(List menuIdList); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleUserMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleUserMapper.java new file mode 100644 index 0000000..196276b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysRoleUserMapper.java @@ -0,0 +1,49 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.entity.SysRoleUserEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 角色用户关系 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysRoleUserMapper extends PowerBaseMapper { + + /** + * 根据角色ids,删除角色用户关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据用户id,删除角色用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 角色ID列表 + * @param userId 用户ID + * + * @return + */ + List getRoleIdList(Long userId); + + /** + * 用户id获取角色中文 + * @param userIds 用户id集合 + * @return list + */ + List getRoleListByUserIds(List userIds); + + List getRoleIdLists(Long userId,Long tenantCode); + + void deleteByRoleIdsAndUserId(List roleIdList, Long userId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMapper.java new file mode 100644 index 0000000..955a104 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMapper.java @@ -0,0 +1,47 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 系统用户 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysUserMapper extends PowerBaseMapper { + + List getList(Map params); + + SysUserEntity getById(Long id); + + SysUserEntity getByUsername(String username); + + int updatePassword(@Param("id") Long id, @Param("newPassword") String newPassword); + + /** + * 根据部门ID,查询用户数 + */ + int getCountByDeptId(Long deptId); + + /** + * 根据部门ID,查询用户ID列表 + */ + List getUserIdListByDeptId(List deptIdList); + + void updateStatusByIds(@Param("ids") List ids, @Param("status") Integer status); + + List getListbySuperAdmin(); + + List getUserNameList(@Param("wxUser") String[] wxUser); + + List getUserNameLists(List longs); + + List getUserName(@Param("names") List names); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMenuMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMenuMapper.java new file mode 100644 index 0000000..c221150 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserMenuMapper.java @@ -0,0 +1,21 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.dto.SysUserMenuPageDTO; +import com.thing.sys.biz.entity.SysUserMenuEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* sys_user_menu +* +* @author Mark sunlightcs@gmail.com +* @since 3.0 2022-05-27 +*/ +@Mapper +public interface SysUserMenuMapper extends PowerBaseMapper { + + List getSysUserMenuList(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserPostMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserPostMapper.java new file mode 100644 index 0000000..65f372b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserPostMapper.java @@ -0,0 +1,34 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysUserPostEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 用户岗位关系 +* +* @author Mark sunlightcs@gmail.com +*/ +@Mapper +public interface SysUserPostMapper extends PowerBaseMapper { + + /** + * 根据岗位ids,删除岗位用户关系 + * @param postIds 岗位ids + */ + void deleteByPostIds(Long[] postIds); + + /** + * 根据用户id,删除岗位用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 岗位ID列表 + * @param userId 用户ID + */ + List getPostIdList(Long userId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserTokenMapper.java b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserTokenMapper.java new file mode 100644 index 0000000..2ff9d46 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/mapper/SysUserTokenMapper.java @@ -0,0 +1,31 @@ +package com.thing.sys.biz.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.biz.entity.SysOnlineEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 系统用户Token + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysUserTokenMapper extends PowerBaseMapper { + + SysUserTokenEntity getByToken(String token); + + SysUserTokenEntity getByUserId(Long userId); + + void logout(@Param("userId") Long userId, @Param("expireDate") Date expireDate); + + /** + * 获取在线用户列表 + */ + List getOnlineList(Map params); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/region/Region.java b/modules/thing/src/main/java/com/thing/sys/biz/region/Region.java new file mode 100644 index 0000000..467be0d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/region/Region.java @@ -0,0 +1,32 @@ + + +package com.thing.sys.biz.region; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 地区管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "地区管理") +public class Region implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "地区ID") + private Long id; + + @JsonIgnore + private Long pid; + + @Schema(description = "名称") + private String name; +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/region/RegionCity.java b/modules/thing/src/main/java/com/thing/sys/biz/region/RegionCity.java new file mode 100644 index 0000000..0bd0704 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/region/RegionCity.java @@ -0,0 +1,24 @@ + + +package com.thing.sys.biz.region; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 市 + * + * @author Mark sunlightcs@gmail.com + */ +@Schema( name= "市") +@Data +@EqualsAndHashCode(callSuper = true) +public class RegionCity extends Region { + @Schema(description = "区、县列表") + private List counties = new ArrayList<>(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/region/RegionProvince.java b/modules/thing/src/main/java/com/thing/sys/biz/region/RegionProvince.java new file mode 100644 index 0000000..8e30845 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/region/RegionProvince.java @@ -0,0 +1,24 @@ + + +package com.thing.sys.biz.region; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.ArrayList; +import java.util.List; + +/** + * 省 + * + * @author Mark sunlightcs@gmail.com + */ +@Schema( name= "省") +@Data +@EqualsAndHashCode(callSuper = true) +public class RegionProvince extends Region { + @Schema(description = "市列表") + private List cities = new ArrayList<>(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysDeptService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDeptService.java new file mode 100644 index 0000000..7e7d889 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDeptService.java @@ -0,0 +1,87 @@ +package com.thing.sys.biz.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.entity.SysDeptEntity; + +import java.util.List; +import java.util.Map; + +/** + * 部门管理 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysDeptService extends IBaseService { + + List list(Map params); + + SysDeptDTO get(Long id); + + void save(SysDeptDTO dto); + + void update(SysDeptDTO dto); + + void delete(Long id); + + /** + * 根据部门ID,获取本部门及子部门ID列表 + * @param id 部门ID + */ + List getSubDeptIdList(Long id); + + /** + * + * @param pid 父级Id + * @param tenantCode 租户编码 + * @return + */ + SysDeptDTO getDeptByPid(Long pid, Long tenantCode); + + /** + * 校验权限查询deptId + * 如果登入的超级管理员,且在琅润达的租户下,那么返回为null,可查询所有的信息 + * 如果登入为管理员且不在琅润达的租户下,则只能查询当前租户的 + * 非管理员登录只能查询自己的,否则查询返回-1L + * @param deptId + * @return + */ +// Long getDeptIdVaildPermission(Long deptId); + + /** + * 建议新增修改使用此方法 + * 获取dept,添加管理员权限校验 + * 1.判断是否是超级管理员 + * 2.若为超级管理员,获取前端传送的,获取不到则获取上下文的deptId + * 3.若不为超级管理员,则获取当前用户登录状态的dept + * @param + * @return + */ + SysDeptEntity getDeptVaildPermission(); + + + /** + * 建议新增修改使用此方法 + * 获取dept,添加管理员权限校验 + * 1.判断是否是超级管理员 + * 2.若为超级管理员,获取前端传送的,获取不到则获取上下文的deptId + * 3.若不为超级管理员,则获取当前用户登录状态的dept + * @param deptId + * @return + */ + SysDeptEntity getDeptVaildPermission(String deptId); + + /** + * 建议新增修改使用此方法 + * 获取dept,添加管理员权限校验 + * 1.判断是否是超级管理员 + * 2.若为超级管理员,获取前端传送的,获取不到则获取上下文的deptId + * 3.若不为超级管理员,则获取当前用户登录状态的dept + * @param deptId + * @return + */ + SysDeptEntity getDeptVaildPermission(Long deptId); + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictDataService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictDataService.java new file mode 100644 index 0000000..e8844f5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictDataService.java @@ -0,0 +1,40 @@ +package com.thing.sys.biz.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.entity.SysDictDataEntity; + +import java.util.List; +import java.util.Map; + +/** + * 数据字典 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysDictDataService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + SysDictDataDTO get(Long id); + + void save(SysDictDataDTO dto); + + void update(SysDictDataDTO dto); + + void delete(Long[] ids); + + SysDictDataDTO getDictDataNameByDictTypes(String key); + + SysDictDataDTO getDictDataNameByDictType(String key); + + SysDictDataDTO getIndustryNameByDictType(String industryCode); + + List getDictDataNameByDictTypeNew(String quotaType, Long tenantCode); + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictTypeService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictTypeService.java new file mode 100644 index 0000000..c6806bd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysDictTypeService.java @@ -0,0 +1,49 @@ +package com.thing.sys.biz.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysDictTypeDTO; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.DictData; +import com.thing.sys.biz.entity.DictType; +import com.thing.sys.biz.entity.SysDictTypeEntity; + +import java.util.List; +import java.util.Map; + +/** + * 数据字典 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysDictTypeService extends IBaseService { + + PageData page(Map params); + + SysDictTypeDTO get(Long id); + + void save(SysDictTypeDTO dto); + + void update(SysDictTypeDTO dto); + + void delete(Long[] ids); + + /** + * 获取所有字典 + */ + List getAllList(); + + /** + * 字典类型列表 + */ + List getDictTypeList(); + + /** + * 类型查询字典 + * @param type 类型 + * @return list + */ + List getDictDataByType(String type); + + List getDictListByDictTypeList(List dictTypeList1); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysIndustryTypeService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysIndustryTypeService.java new file mode 100644 index 0000000..1025f01 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysIndustryTypeService.java @@ -0,0 +1,26 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysIndustryTypeDTO; +import com.thing.sys.biz.entity.SysIndustryTypeEntity; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 行业类型 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-01-18 + */ +public interface SysIndustryTypeService extends IBaseService { + + List getByParent(String parentCode); + + List getByCodes(Collection codes); + + String getByCode(String code); + + List listSysIndustryTypeDTO(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysLanguageService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysLanguageService.java new file mode 100644 index 0000000..440dcea --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysLanguageService.java @@ -0,0 +1,32 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysLanguageEntity; + +/** + * 国际化 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysLanguageService extends IBaseService { + + /** + * 保存或更新 + * @param tableName 表名 + * @param tableId 表主键 + * @param fieldName 字段名 + * @param fieldValue 字段值 + * @param language 语言 + */ + void saveOrUpdate(String tableName, Long tableId, String fieldName, String fieldValue, String language); + + /** + * 删除国际化 + * @param tableName 表名 + * @param tableId 表主键 + */ + void deleteLanguage(String tableName, Long tableId); + + String getMenuName(Long menuId); +} + diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysMenuService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysMenuService.java new file mode 100644 index 0000000..921c44e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysMenuService.java @@ -0,0 +1,60 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysMenuDTO; +import com.thing.sys.biz.entity.SysMenuEntity; +import com.thing.sys.security.domain.UserDetail; + +import java.util.List; + + +/** + * 菜单管理 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysMenuService extends IBaseService { + + SysMenuDTO get(Long id); + + SysMenuDTO getByUrl(String url); + + List getByUrlList(List urlList); + + void save(SysMenuDTO dto); + + void update(SysMenuDTO dto); + + void delete(Long id); + + /** + * 菜单列表 + * + * @param type 菜单类型 + */ + List getAllMenuList(Integer type); + + /** + * 用户菜单列表 + * + * @param user 用户 + * @param type 菜单类型 + */ + List getUserMenuList(UserDetail user, Integer type,String flag); + + /** + * 根据父菜单,查询子菜单 + * @param pid 父菜单ID + */ + List getListPid(Long pid); + + /** + * 租户企业查询 + * @return list + */ + List companySelect(); + + List selectMenu(Long id); + + List getUserMenuListHomeNav(UserDetail user, int value, String flag); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysParamsService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysParamsService.java new file mode 100644 index 0000000..835034e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysParamsService.java @@ -0,0 +1,52 @@ +package com.thing.sys.biz.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysParamsDTO; +import com.thing.sys.biz.entity.SysParamsEntity; + +import java.util.List; +import java.util.Map; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysParamsService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + SysParamsDTO get(Long id); + + void save(SysParamsDTO dto); + + void update(SysParamsDTO dto); + + void delete(Long[] ids); + + /** + * 根据参数编码,获取参数的value值 + * + * @param paramCode 参数编码 + */ + String getValue(String paramCode); + + /** + * 根据参数编码,获取value的Object对象 + * @param paramCode 参数编码 + * @param clazz Object对象 + */ + T getValueObject(String paramCode, Class clazz); + + /** + * 根据参数编码,更新value + * @param paramCode 参数编码 + * @param paramValue 参数值 + */ + int updateValueByCode(String paramCode, String paramValue); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysPostService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysPostService.java new file mode 100644 index 0000000..f747370 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysPostService.java @@ -0,0 +1,22 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysPostDTO; +import com.thing.sys.biz.entity.SysPostEntity; + +import java.util.List; +import java.util.Map; + +/** + * 岗位管理 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysPostService extends IBaseService { + + List list(Map params); + + void delete(Long[] ids); + + SysPostDTO get(Long id); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysRegionService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRegionService.java new file mode 100644 index 0000000..ebf6664 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRegionService.java @@ -0,0 +1,34 @@ +package com.thing.sys.biz.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysRegionDTO; +import com.thing.sys.biz.entity.SysRegionEntity; +import com.thing.sys.biz.region.RegionProvince; + +import java.util.List; +import java.util.Map; + +/** + * 行政区域 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysRegionService extends IBaseService { + + List list(Map params); + + List> getTreeList(); + + SysRegionDTO get(Long id); + + void save(SysRegionDTO dto); + + void update(SysRegionDTO dto); + + void delete(Long id); + + int getCountByPid(Long pid); + + List getRegion(boolean threeLevel); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleDataScopeService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleDataScopeService.java new file mode 100644 index 0000000..1e8f2d6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleDataScopeService.java @@ -0,0 +1,41 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysRoleDataScopeEntity; + +import java.util.List; + +/** + * 角色数据权限 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysRoleDataScopeService extends IBaseService { + + /** + * 根据角色ID,获取部门ID列表 + */ + List getDeptIdList(Long roleId); + + /** + * 根据角色ID,获取部门ID列表 + */ + List getRoleIdList(List deptIdList); + + /** + * 保存或修改 + * @param roleId 角色ID + * @param deptIdList 部门ID列表 + */ + void saveOrUpdate(Long roleId, List deptIdList); + + /** + * 根据角色id,删除角色数据权限关系 + * @param roleId 角色ids + */ + void deleteByRoleIds(Long[] roleId); + + SysRoleDataScopeEntity getOrgDataByRoleDept(Long roleId, Long deptId); + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleMenuService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleMenuService.java new file mode 100644 index 0000000..f937d83 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleMenuService.java @@ -0,0 +1,38 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysRoleMenuEntity; + +import java.util.List; + +/** + * 角色与菜单对应关系 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysRoleMenuService extends IBaseService { + + /** + * 根据角色ID,获取菜单ID列表 + */ + List getMenuIdList(Long roleId); + + /** + * 保存或修改 + * @param roleId 角色ID + * @param menuIdList 菜单ID列表 + */ + void saveOrUpdate(Long roleId, List menuIdList); + + /** + * 根据角色id,删除角色菜单关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据菜单id,删除角色菜单关系 + * @param menuId 菜单id + */ + void deleteByMenuId(Long menuId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleService.java new file mode 100644 index 0000000..d06f177 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleService.java @@ -0,0 +1,33 @@ +package com.thing.sys.biz.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysRoleEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 角色 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysRoleService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + SysRoleDTO getDefaultRole(Map params); + + SysRoleDTO get(Long id); + + void save(SysRoleDTO dto); + + void update(SysRoleDTO dto); + + void delete(Long[] ids); + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleUserService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleUserService.java new file mode 100644 index 0000000..6e02453 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysRoleUserService.java @@ -0,0 +1,49 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysRoleUserEntity; + +import java.util.List; + +/** + * 角色用户关系 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysRoleUserService extends IBaseService { + + /** + * 保存或修改 + * @param userId 用户ID + * @param roleIdList 角色ID列表 + */ + void saveOrUpdate(Long userId, List roleIdList); + + /** + * 根据角色ids,删除角色用户关系 + * @param roleIds 角色ids + */ + void deleteByRoleIds(Long[] roleIds); + + /** + * 根据用户id,删除角色用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 角色ID列表 + * @param userId 用户ID + */ + List getRoleIdList(Long userId); + + /** + * 获取已分配角色的数量 + * @param roleIds 角色主键 + * @return 数量 + */ + Long selectCountByRoleIds(List roleIds); + + List getRoleIdLists(Long userId,Long tenantCode); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserMenuService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserMenuService.java new file mode 100644 index 0000000..39f3e84 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserMenuService.java @@ -0,0 +1,31 @@ +package com.thing.sys.biz.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysUserMenuDTO; +import com.thing.sys.biz.dto.SysUserMenuPageDTO; +import com.thing.sys.biz.entity.SysUserMenuEntity; + +import java.util.Map; + +/** + * sys_user_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +public interface SysUserMenuService extends IBaseService { + + + PageData getSysUserMenuPage(Map params); + + void saveSysUserMenu(SysUserMenuDTO sysUserMenuDTO); + + SysUserMenuDTO getSysUserMenuByUserId(Long userId); + + void deleteByIds(Long[] ids); + + SysUserMenuDTO queryUrlByTenantcode(Long tenantCode); + + SysUserMenuDTO get(Long id); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserPostService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserPostService.java new file mode 100644 index 0000000..cf06c34 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserPostService.java @@ -0,0 +1,39 @@ +package com.thing.sys.biz.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysUserPostEntity; + +import java.util.List; + +/** + * 用户岗位关系 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysUserPostService extends IBaseService { + + /** + * 保存或修改 + * @param userId 用户ID + * @param postIdList 岗位ID列表 + */ + void saveOrUpdate(Long userId, List postIdList); + + /** + * 根据岗位ids,删除岗位用户关系 + * @param postIds 岗位ids + */ + void deleteByPostIds(Long[] postIds); + + /** + * 根据用户id,删除岗位用户关系 + * @param userIds 用户ids + */ + void deleteByUserIds(Long[] userIds); + + /** + * 岗位ID列表 + * @param userId 用户ID + */ + List getPostIdList(Long userId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserService.java new file mode 100644 index 0000000..766e670 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserService.java @@ -0,0 +1,82 @@ +package com.thing.sys.biz.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.BindDTO; +import com.thing.sys.biz.dto.CtlUserDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysUserEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 系统用户 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysUserService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + List listWithOutAuth(); + + /** + * 系统用户 + * @param params 入参 + * @return list + */ + List ctlList(Map params); + + SysUserDTO get(Long id); + + SysUserDTO getByUsername(String username); + + void save(SysUserDTO dto); + + void update(SysUserDTO dto); + + void delete(Long[] ids); + + /** + * 修改密码 + * @param id 用户ID + * @param newPassword 新密码 + */ + void updatePassword(Long id, String newPassword); + + /** + * 根据部门ID,查询用户数 + */ + int getCountByDeptId(Long deptId); + + /** + * 根据部门ID,查询用户Id列表 + */ + List getUserIdListByDeptId(List deptIdList); + + List getSysUserDTOList(List idList); + + void bindOpenId(BindDTO bindDTO); + + String title(); + String sign(); + + String support(); + /** + * 获取超级管理员的用户信息 + * @return + */ + SysUserEntity getUserbySuperAdmin(); + + List queryUserList(String tenantCode); + + List getUserNameList(String[] wxUser); + + List getUserNameLists(List longs); + + List getUserName(List longs); +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserTokenService.java b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserTokenService.java new file mode 100644 index 0000000..7eb0394 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/SysUserTokenService.java @@ -0,0 +1,36 @@ +package com.thing.sys.biz.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.entity.SysOnlineEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; + +import java.util.Map; + +/** + * 用户Token + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysUserTokenService extends IBaseService { + + /** + * 生成token + * @param userId 用户ID + */ + Result createToken(Long userId); + + /** + * 退出 + * @param userId 用户ID + */ + void logout(Long userId); + + /** + * 在线用户分页 + */ + PageData onlinePage(Map params); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDeptServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDeptServiceImpl.java new file mode 100644 index 0000000..bfd93ff --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDeptServiceImpl.java @@ -0,0 +1,231 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.comparator.CompareUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.SuperTenantEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.mapper.SysDeptMapper; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class SysDeptServiceImpl extends BaseServiceImpl implements SysDeptService { + + private final SysUserMapper sysUserMapper; + private final SysDeptMapper sysDeptMapper; + //private final SysUserService sysUserService; + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public List list(Map params) { + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if(user.getSuperAdmin() == SuperAdminEnum.NO.value() + && user.getSuperTenant() == SuperTenantEnum.NO.value()) { + params.put("deptIdList", getSubDeptIdList(user.getDeptId())); + } + + //租户 + params.put(Constant.TENANT_CODE, TenantContext.getTenantCode(user)); + + //查询部门列表 + List entityList = mapper.getList(params); + + List dtoList = ConvertUtils.sourceToTarget(entityList, SysDeptDTO.class); + + return TreeUtils.build(dtoList); + } + + @Override + public SysDeptDTO get(Long id) { + //超级管理员,部门ID为null + if(id == null){ + return null; + } + + SysDeptEntity entity = mapper.getById(id); + + return ConvertUtils.sourceToTarget(entity, SysDeptDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDeptDTO dto) { + SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class); + + entity.setPids(getPidList(entity.getPid())); + mapper.insertSelective(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDeptDTO dto) { + SysDeptEntity entity = ConvertUtils.sourceToTarget(dto, SysDeptEntity.class); + + //上级部门不能为自身 + if(entity.getId().equals(entity.getPid())){ + throw new SysException(ErrorCode.SUPERIOR_DEPT_ERROR); + } + + //上级部门不能为下级部门 + List subDeptList = getSubDeptIdList(entity.getId()); + if(subDeptList.contains(entity.getPid())){ + throw new SysException(ErrorCode.SUPERIOR_DEPT_ERROR); + } + + entity.setPids(getPidList(entity.getPid())); + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + //判断是否有子部门 + List subList = getSubDeptIdList(id); + if(subList.size() > 1){ + throw new SysException(ErrorCode.DEPT_SUB_DELETE_ERROR); + } + + //判断部门下面是否有用户 + int count = sysUserMapper.getCountByDeptId(id); + if(count > 0){ + throw new SysException(ErrorCode.DEPT_USER_DELETE_ERROR); + } + + //删除 + mapper.deleteById(id); + } + + @Override + public List getSubDeptIdList(Long id) { + List deptIdList = mapper.getSubDeptIdList("%" + id + "%"); + deptIdList.add(id); + + return deptIdList; + } + + @Override + public SysDeptDTO getDeptByPid(Long pid, Long tenantCode) { + return ConvertUtils.sourceToTarget(mapper.getDeptByPid(pid, tenantCode), SysDeptDTO.class); + } + + /** + * 校验权限查询deptId + * 如果登入的超级管理员,且在琅润达的租户下,那么返回为null,可查询所有的信息 + * 如果登入为管理员且不在琅润达的租户下,则只能查询当前租户的 + * 非管理员登录只能查询自己的,否则查询不到 + * + * @param deptId + * @return + */ +// @Override +// public Long getDeptIdVaildPermission(Long deptId) { +// SysUserEntity sysUserEntity = sysUserService.getUserbySuperAdmin(); +// return CompareUtil.compare(SecurityUser.getUser().getSuperAdmin(),1) == 0 ? +// (TenantContext.getTenantCode(SecurityUser.getUser()).equals(sysUserEntity.getTenantCode()) +// ? deptId : TenantContext.getConsumerPid(SecurityUser.getUser())) +// : +// (ObjectUtil.isNotNull(SecurityUser.getUser().getConsumerPid()) +// ? SecurityUser.getUser().getConsumerPid() : -1L); +// } + + @Override + public SysDeptEntity getDeptVaildPermission() { + return getDeptVaildPermission((String) null); + } + + @Override + public SysDeptEntity getDeptVaildPermission(String deptId) { + Long id = StringUtils.isBlank(deptId) ? null : Long.parseLong(deptId); + return getDeptVaildPermission(id); + } + + /** + * 获取dept,添加管理员权限校验 + * 1.判断是否是超级管理员 + * 2.若为超级管理员,获取前端传送的,获取不到则获取上下文的deptId + * 3.若不为超级管理员,则获取当前用户登录状态的dept + * + * @param deptId + * @return + */ + @Override + public SysDeptEntity getDeptVaildPermission(Long deptId) { +// Long id = new Integer(1).equals(SecurityUser.getUser().getSuperAdmin()) ? +// (null != deptId ? deptId : TenantContext.getConsumerPid(SecurityUser.getUser())) +// : SecurityUser.getUser().getConsumerPid(); + + Long id = CompareUtil.compare(SecurityUser.getUser().getSuperAdmin(),1) == 0 ? + (null != deptId ? deptId : TenantContext.getCompanyId(SecurityUser.getUser())) + : SecurityUser.getUser().getCompanyId(); + return sysDeptMapper.selectOneById(id); + } + + /** + * 获取所有上级部门ID + * @param pid 上级ID + */ + private String getPidList(Long pid){ + //顶级部门,无上级部门 + if(Constant.DEPT_ROOT.equals(pid)){ + return Constant.DEPT_ROOT + ""; + } + + //所有部门的id、pid列表 + List deptList = mapper.getIdAndPidList(); + + //list转map + Map map = new HashMap<>(deptList.size()); + for(SysDeptEntity entity : deptList){ + map.put(entity.getId(), entity); + } + + //递归查询所有上级部门ID列表 + List pidList = new ArrayList<>(); + getPidTree(pid, map, pidList); + + return StringUtils.join(pidList, ","); + } + + private void getPidTree(Long pid, Map map, List pidList) { + //顶级部门,无上级部门 + if(Constant.DEPT_ROOT.equals(pid)){ + return ; + } + + //上级部门存在 + SysDeptEntity parent = map.get(pid); + if(parent != null){ + getPidTree(parent.getPid(), map, pidList); + } + + pidList.add(pid); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictDataServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictDataServiceImpl.java new file mode 100644 index 0000000..660ac8c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictDataServiceImpl.java @@ -0,0 +1,115 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.mapper.SysDictDataMapper; +import com.thing.sys.biz.service.SysDictDataService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@Primary +public class SysDictDataServiceImpl extends BaseServiceImpl implements SysDictDataService { + + @Override + public QueryWrapper getWrapper(Map params){ + String dictTypeId = (String) params.get("dictTypeId"); + + QueryWrapper wrapper = QueryWrapper.create(); + wrapper.eq("dict_type_id", StringUtils.isNotBlank(dictTypeId) ? Long.parseLong(dictTypeId) : -1L); + + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, null, false) + ); + + return getPageData(page, SysDictDataDTO.class); + } + + @Override + public List list(Map params) { + Long dictTypeId = MapUtil.getLong(params, "dictTypeId"); + String dictValue = MapUtil.getStr(params, "dictValue"); + String dictLabel = MapUtil.getStr(params, "dictLabel"); + + List sysDictDataEntities = mapper.selectListByQuery(QueryWrapper.create() + .eq(SysDictDataEntity::getDictTypeId,dictTypeId, ObjectUtil.isNotNull(dictTypeId)) + .eq(SysDictDataEntity::getDictValue,dictValue, StringUtils.isNotBlank(dictValue)) + .eq(SysDictDataEntity::getDictLabel,dictLabel, StringUtils.isNotBlank(dictLabel)) + ); + return ConvertUtils.sourceToTarget(sysDictDataEntities,SysDictDataDTO.class); + } + + @Override + public SysDictDataDTO get(Long id) { + SysDictDataEntity entity = mapper.selectOneById(id); + + return ConvertUtils.sourceToTarget(entity, SysDictDataDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDictDataDTO dto) { + SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class); + + save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDictDataDTO dto) { + SysDictDataEntity entity = ConvertUtils.sourceToTarget(dto, SysDictDataEntity.class); + + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除 + batchDelete(ids); + } + + + @Override + public SysDictDataDTO getDictDataNameByDictTypes(String key) { + return mapper.getDictDataNameByDictTypes(key); + } + + @Override + public SysDictDataDTO getDictDataNameByDictType(String dictValue) { + return mapper.getDictDataNameByDictType(dictValue); + } + + @Override + public SysDictDataDTO getIndustryNameByDictType(String industryCode) { + return mapper.getIndustryNameByDictType(industryCode); + } + + @Override + public List getDictDataNameByDictTypeNew(String quotaType, Long tenantCode) { + return mapper.getDictDataNameByDictTypeNew(quotaType,tenantCode); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictTypeServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictTypeServiceImpl.java new file mode 100644 index 0000000..b2aba11 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysDictTypeServiceImpl.java @@ -0,0 +1,140 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysDictTypeDTO; +import com.thing.sys.biz.dto.SysDictTypeListDTO; +import com.thing.sys.biz.entity.DictData; +import com.thing.sys.biz.entity.DictType; +import com.thing.sys.biz.entity.SysDictDataEntity; +import com.thing.sys.biz.entity.SysDictTypeEntity; +import com.thing.sys.biz.mapper.SysDictDataMapper; +import com.thing.sys.biz.mapper.SysDictTypeMapper; +import com.thing.sys.biz.service.SysDictTypeService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 字典类型 + * + * @author Mark sunlightcs@gmail.com + */ + +@RequiredArgsConstructor +@Service +@Primary +public class SysDictTypeServiceImpl extends BaseServiceImpl implements SysDictTypeService { + private final SysDictDataMapper sysDictDataMapper; + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + getWrapper(params) + ); + + return getPageData(page, SysDictTypeDTO.class); + } + + public QueryWrapper getWrapper(Map params){ + String dictType = (String) params.get("dictType"); + String dictName = (String) params.get("dictName"); + + QueryWrapper wrapper = QueryWrapper.create(); + wrapper.like("dict_type", dictType, StringUtils.isNotBlank(dictType)); + wrapper.like("dict_name", dictName, StringUtils.isNotBlank(dictName)); + + return wrapper; + } + + @Override + public SysDictTypeDTO get(Long id) { + SysDictTypeEntity entity = mapper.selectOneById(id); + + return ConvertUtils.sourceToTarget(entity, SysDictTypeDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysDictTypeDTO dto) { + SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class); + + save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysDictTypeDTO dto) { + SysDictTypeEntity entity = ConvertUtils.sourceToTarget(dto, SysDictTypeEntity.class); + + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除 + batchDelete(ids); + } + + @Override + public List getAllList() { + List typeList = mapper.getDictTypeList(); + List dataList = sysDictDataMapper.getDictDataList(); + for(DictType type : typeList){ + for(DictData data : dataList){ + if(type.getId().equals(data.getDictTypeId())){ + type.getDataList().add(data); + } + } + type.setDataList(type.getDataList().parallelStream() + .sorted(Comparator.comparing(DictData::getSort, Comparator.nullsLast(Comparator.naturalOrder()))).collect(Collectors.toList())); + } + return typeList; + } + + @Override + public List getDictTypeList() { + return mapper.getDictTypeList(); + } + + /** + * 类型查询字典 + * + * @param type 类型 + * @return list + */ + @Override + public List getDictDataByType(String type) { + SysDictTypeEntity dictTypeEntity = mapper.selectOneByQuery(new QueryWrapper() + .eq(SysDictTypeEntity::getDictType, type)); + if (dictTypeEntity == null || dictTypeEntity.getId() == null) { + return new ArrayList<>(); + } + List dictDataEntityList = sysDictDataMapper.selectListByQuery(new QueryWrapper() + .eq(SysDictDataEntity::getDictTypeId, dictTypeEntity.getId())); + return ConvertUtils.sourceToTarget(dictDataEntityList, DictData.class); + } + + @Override + public List getDictListByDictTypeList(List dictTypeList1) { + List dictListByDictTypeList = mapper.getDictListByDictTypeList(dictTypeList1); + + for (SysDictTypeListDTO sysDictTypeEntity : dictListByDictTypeList) { + List dictData = sysDictDataMapper.getDictDataById(sysDictTypeEntity.getId()); + sysDictTypeEntity.setDataList(dictData); + } + return dictListByDictTypeList; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysIndustryTypeServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysIndustryTypeServiceImpl.java new file mode 100644 index 0000000..1d446f1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysIndustryTypeServiceImpl.java @@ -0,0 +1,58 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysIndustryTypeDTO; +import com.thing.sys.biz.entity.SysIndustryTypeEntity; +import com.thing.sys.biz.mapper.SysIndustryTypeMapper; +import com.thing.sys.biz.service.SysIndustryTypeService; + +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * 行业类型 + * + * @author ssy 2337720667@qq.com + * @since 5.1 2024-01-18 + */ +@Service +public class SysIndustryTypeServiceImpl extends BaseServiceImpl implements SysIndustryTypeService { + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + return wrapper; + } + + @Override + public List getByParent(String parentCode) { + return listAs( + QueryWrapper.create().eq(SysIndustryTypeEntity::getParentCode, parentCode), + SysIndustryTypeDTO.class); + } + + @Override + public List getByCodes(Collection codes) { + if (CollectionUtils.isEmpty(codes)) { + return Collections.emptyList(); + } + return list(QueryWrapper.create().in(SysIndustryTypeEntity::getCode, codes)); + } + + @Override + public String getByCode(String code) { + return mapper.getByCode(code); + } + + @Override + public List listSysIndustryTypeDTO() { + return mapper.listSysIndustryTypeDTO(); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysLanguageServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysLanguageServiceImpl.java new file mode 100644 index 0000000..3f3c82c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysLanguageServiceImpl.java @@ -0,0 +1,57 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysLanguageEntity; +import com.thing.sys.biz.mapper.SysLanguageMapper; +import com.thing.sys.biz.service.SysLanguageService; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 国际化 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@Primary +public class SysLanguageServiceImpl extends BaseServiceImpl implements SysLanguageService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public void saveOrUpdate(String tableName, Long tableId, String fieldName, String fieldValue, String language) { + SysLanguageEntity entity = new SysLanguageEntity(); + entity.setTableName(tableName); + entity.setTableId(tableId); + entity.setFieldName(fieldName); + entity.setFieldValue(fieldValue); + entity.setLanguage(language); + + //判断是否有数据 + if(mapper.getLanguage(entity) == null){ + mapper.insertSelective(entity); + }else { + mapper.updateLanguage(entity); + } + } + + @Override + public void deleteLanguage(String tableName, Long tableId) { + mapper.deleteLanguage(tableName, tableId); + } + + @Override + public String getMenuName(Long menuId) { + SysLanguageEntity sysLanguageEntity = mapper.selectOneByQuery(QueryWrapper.create() + .eq(SysLanguageEntity::getTableId, menuId)); + return sysLanguageEntity.getFieldValue(); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysMenuServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysMenuServiceImpl.java new file mode 100644 index 0000000..665e457 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysMenuServiceImpl.java @@ -0,0 +1,247 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.MenuTypeEnum; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.dashboard.entity.IotDashboardGroupEntity; +import com.thing.dashboard.mapper.IotDashboardGroupMapper; +import com.thing.sys.biz.dto.SysMenuDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.entity.SysMenuEntity; +import com.thing.sys.biz.mapper.SysMenuMapper; +import com.thing.sys.biz.service.SysLanguageService; +import com.thing.sys.biz.service.SysMenuService; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Primary +public class SysMenuServiceImpl extends BaseServiceImpl implements SysMenuService { + private final SysRoleMenuService sysRoleMenuService; + private final SysLanguageService sysLanguageService; + private final SysUserService sysUserService; + private final IotDashboardGroupMapper dashboardGroupMapper; + private final SysTenantMapper sysTenantMapper; + + + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public SysMenuDTO get(Long id) { + SysMenuEntity entity = mapper.getById(id, HttpContextUtils.getLanguage()); + return ConvertUtils.sourceToTarget(entity, SysMenuDTO.class); + } + + @Override + public SysMenuDTO getByUrl(String url) { + SysMenuEntity entity = mapper.getByUrl(url, HttpContextUtils.getLanguage()); + return ConvertUtils.sourceToTarget(entity, SysMenuDTO.class); + } + + @Override + public List getByUrlList(List urlList) { + List entityList = mapper.getByUrlList(urlList, HttpContextUtils.getLanguage()); + return ConvertUtils.sourceToTarget(entityList, SysMenuDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysMenuDTO dto) { + SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class); + + //保存菜单 + save(entity); + saveLanguage(entity.getId(), "name", entity.getName()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysMenuDTO dto) { + SysMenuEntity entity = ConvertUtils.sourceToTarget(dto, SysMenuEntity.class); + + //上级菜单不能为自身 + if(entity.getId().equals(entity.getPid())){ + throw new SysException(ErrorCode.SUPERIOR_MENU_ERROR); + } + + //更新菜单 + updateById(entity); + saveLanguage(entity.getId(), "name", entity.getName()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + //删除菜单 + mapper.deleteById(id); + + //删除菜单国际化 + sysLanguageService.deleteLanguage("sys_menu", id); + + //删除角色菜单关系 + sysRoleMenuService.deleteByMenuId(id); + } + + @Override + public List getAllMenuList(Integer type) { + List menuList = mapper.getMenuList(type, HttpContextUtils.getLanguage()); + + List dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + + return TreeUtils.build(dtoList, Constant.MENU_ROOT); + } + + @Override + public List getUserMenuList(UserDetail user, Integer type,String flag) { + List menuList; + + //获取实际登录企业的,管理员用户的userId; + Long tenantCode = UserContext.getTenantCode(); + Long userId = sysTenantMapper.getTenantCode(tenantCode).getUserId(); + //系统管理员,拥有最高权限. + if(tenantCode!=null&&tenantCode==1001){ + //menuList = baseDao.getMenuList(type, HttpContextUtils.getLanguage()); + menuList = mapper.getSuperAdminMenuList(type, HttpContextUtils.getLanguage(),flag); + }else { + //当前企业编码,与登录用户企业编码不一致,说明切换了企业,获取切换企业的菜单 + if(!Objects.equals(tenantCode, user.getTenantCode())) { + menuList = mapper.getUserMenuList(userId, type, HttpContextUtils.getLanguage(),flag); + }else { + menuList = mapper.getUserMenuList(user.getId(), type, HttpContextUtils.getLanguage(),flag); + } + } + //看板处理 一对一关系为什么要用url不用id关联??? + //临时方案 后续变更IotDashboardGroupServiceImpl关系,使用id关联 + List collect = menuList.parallelStream() + .filter(item -> StringUtils.isNotBlank(item.getUrl()) && item.getUrl().startsWith("dashboard/index?id=")).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(collect)) { + List dashboardIdList = collect.parallelStream().map(item -> Long.parseLong(item.getUrl().split("=")[1])).collect(Collectors.toList()); + List dashboardGroupList = dashboardGroupMapper.selectListByQuery(QueryWrapper.create() + .in(IotDashboardGroupEntity::getId, dashboardIdList)); + if (CollectionUtil.isNotEmpty(dashboardGroupList)) { + menuList.stream() + .peek(item -> dashboardGroupList.parallelStream() + .filter(dashboard -> StringUtils.isNotBlank(item.getUrl()) && item.getUrl().endsWith(String.valueOf(dashboard.getId()))) + .findFirst().ifPresent(dashboard -> { + StringBuilder url = new StringBuilder(item.getUrl()); + if(StringUtils.equals(dashboard.getSlideshow(), "1")) { + url.append("&slideshow=").append(dashboard.getSlideshow()); + } + if(StringUtils.equals(dashboard.getSlideshow(), "1")) { + url.append("&interval=").append(dashboard.getSlideshowInterval()); + } + if(StringUtils.isNotBlank(dashboard.getMultiple())) { + url.append("&multiple=").append(dashboard.getMultiple()); + } + item.setUrl(url.toString()); + })) + .collect(Collectors.toList()); + } + } + if(CollectionUtil.isEmpty(menuList)) { + return Lists.newArrayList(); + } + List dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + + return TreeUtils.build(dtoList); + } + + /** + * 租户企业查询 + * + * @return list + */ + @Override + public List companySelect() { + UserDetail user = SecurityUser.getUser(); + List menuList; + //系统管理员且未切换租户,拥有最高权限 + //租户未切换企业,获取自己的 + //系统管理员切换租户、租户切换企业,获取切换企业的 + if(user.getSuperAdmin() == SuperAdminEnum.YES.value() && Objects.equals(TenantContext.getTenantCode(user), user.getTenantCode())){ + menuList = mapper.getSuperAdminMenuList(null, HttpContextUtils.getLanguage(),"1"); + }else if (Objects.equals(TenantContext.getTenantCode(user), user.getTenantCode())){ + menuList = mapper.getUserMenuList(user.getId(), null, HttpContextUtils.getLanguage(),"1"); + } else { + menuList = mapper.getCompanyMenuList(TenantContext.getTenantCode(user), null, HttpContextUtils.getLanguage(),"1"); + } + if(CollectionUtil.isEmpty(menuList)) { + return Lists.newArrayList(); + } + List dtoList = ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + + return TreeUtils.build(dtoList); + } + + @Override + public List getListPid(Long pid) { + List menuList = mapper.getListPid(pid); + + return ConvertUtils.sourceToTarget(menuList, SysMenuDTO.class); + } + + private void saveLanguage(Long tableId, String fieldName, String fieldValue){ + sysLanguageService.saveOrUpdate("sys_menu", tableId, fieldName, fieldValue, HttpContextUtils.getLanguage()); + } + + @Override + public List selectMenu(Long id){ + UserDetail user = new UserDetail(); + user.setId(id); + SysUserDTO sysUserDTO = sysUserService.get(id); + user.setSuperAdmin(sysUserDTO.getSuperAdmin()); + user.setTenantCode(sysUserDTO.getTenantCode()); + user.setSuperTenant(sysUserDTO.getSuperTenant()); + user.setTenantGroup(sysUserDTO.getTenantGroup()); + return getUserMenuList(user, null,null); + } + + @Override + public List getUserMenuListHomeNav(UserDetail user, int value, String flag) { + List menuList; + //系统管理员且未切换租户,拥有最高权限 + //租户未切换企业,获取自己的 + //系统管理员切换租户、租户切换企业,获取切换企业的 + //获取实际登录企业的,管理员用户的userId; + Long tenantCode = TenantContext.getTenantCode(user); + Long userId = sysTenantMapper.getTenantCode(tenantCode).getUserId(); + //系统管理员,拥有最高权限. + if(tenantCode!=null&&tenantCode==1001){ + //menuList = baseDao.getMenuList(type, HttpContextUtils.getLanguage()); + menuList = mapper.getSuperAdminMenuList(MenuTypeEnum.MENU.value(), HttpContextUtils.getLanguage(),flag); + }else { + menuList = mapper.getUserMenuList(userId, MenuTypeEnum.MENU.value(), HttpContextUtils.getLanguage(),flag); + } + return menuList; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysParamsServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysParamsServiceImpl.java new file mode 100644 index 0000000..f655639 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysParamsServiceImpl.java @@ -0,0 +1,158 @@ + + +package com.thing.sys.biz.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.param.redis.MsgSysParamsRedis; +import com.thing.sys.biz.dto.SysParamsDTO; +import com.thing.sys.biz.entity.SysParamsEntity; +import com.thing.sys.biz.mapper.SysParamsMapper; +import com.thing.sys.biz.service.SysParamsService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 参数管理 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysParamsServiceImpl extends BaseServiceImpl implements SysParamsService { + @Value("${spring.cache.type}") + private String cacheType; + + + private MsgSysParamsRedis getMsgSysParamsRedis(){ + return SpringContextUtils.getBean(MsgSysParamsRedis.class); + } + + private boolean inUsingRedis(){ + return "redis".equals(cacheType); + } + + @Override + public QueryWrapper getWrapper(Map params){ + String paramCode = (String) params.get("paramCode"); + + QueryWrapper wrapper = QueryWrapper.create(); + wrapper.eq("param_type", 1); + wrapper.like("param_code", paramCode, StringUtils.isNotBlank(paramCode)); + + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + + return getPageData(page, SysParamsDTO.class); + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysParamsDTO.class); + } + + @Override + public SysParamsDTO get(Long id) { + SysParamsEntity entity = mapper.selectOneById(id); + + return ConvertUtils.sourceToTarget(entity, SysParamsDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysParamsDTO dto) { + SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class); + save(entity); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(entity.getParamCode(), entity.getParamValue()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysParamsDTO dto) { + SysParamsEntity entity = ConvertUtils.sourceToTarget(dto, SysParamsEntity.class); + updateById(entity); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(entity.getParamCode(), entity.getParamValue()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除Redis数据 + List paramCodeList = mapper.getParamCodeList(ids); + String[] paramCodes = paramCodeList.toArray(new String[paramCodeList.size()]); + if (inUsingRedis()) { + getMsgSysParamsRedis().delete(paramCodes); + } + + //删除 + batchDelete(ids); + } + + @Override + public String getValue(String paramCode) { + String paramValue = inUsingRedis() ? getMsgSysParamsRedis().get(paramCode) : null; + if(paramValue == null){ + paramValue = mapper.getValueByCode(paramCode); + + if (inUsingRedis()) { + getMsgSysParamsRedis().set(paramCode, paramValue); + } + } + return paramValue; + } + + @Override + public T getValueObject(String paramCode, Class clazz) { + String paramValue = getValue(paramCode); + if(StringUtils.isNotBlank(paramValue)){ + return JSON.parseObject(paramValue, clazz); + } + + try { + return clazz.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new SysException(ErrorCode.PARAMS_GET_ERROR); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public int updateValueByCode(String paramCode, String paramValue) { + int count = mapper.updateValueByCode(paramCode, paramValue); + if (inUsingRedis()) { + getMsgSysParamsRedis().set(paramCode, paramValue); + } + return count; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysPostServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysPostServiceImpl.java new file mode 100644 index 0000000..24ac9e7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysPostServiceImpl.java @@ -0,0 +1,74 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysPostDTO; +import com.thing.sys.biz.entity.SysPostEntity; +import com.thing.sys.biz.mapper.SysPostMapper; +import com.thing.sys.biz.service.SysPostService; +import com.thing.sys.biz.service.SysUserPostService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 岗位管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysPostServiceImpl extends BaseServiceImpl implements SysPostService { + private final SysUserPostService sysUserPostService; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + + String postCode = (String)params.get("postCode"); + wrapper.like("post_code", postCode, StringUtils.isNotBlank(postCode)); + + String postName = (String)params.get("postName"); + wrapper.like("post_name", postName, StringUtils.isNotBlank(postName)); + + String status = (String)params.get("status"); + if(StringUtils.isNotBlank(status)){ + wrapper.eq("status", Integer.parseInt(status)); + } + + wrapper.eq("tenant_code", TenantContext.getTenantCode(SecurityUser.getUser())); + + wrapper.orderBy("sort",true); + + return wrapper; + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + return ConvertUtils.sourceToTarget(entityList, SysPostDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除岗位 + batchDelete(ids); + //删除岗位用户关系 + sysUserPostService.deleteByPostIds(ids); + } + + @Override + public SysPostDTO get(Long id) { + return ConvertUtils.sourceToTarget(getById(id), SysPostDTO.class); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRegionServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRegionServiceImpl.java new file mode 100644 index 0000000..79ca99b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRegionServiceImpl.java @@ -0,0 +1,178 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.RegionLeafEnum; +import com.thing.common.core.enumeration.RegionLevelEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysRegionDTO; +import com.thing.sys.biz.entity.SysRegionEntity; +import com.thing.sys.biz.mapper.SysRegionMapper; +import com.thing.sys.biz.region.Region; +import com.thing.sys.biz.region.RegionCity; +import com.thing.sys.biz.region.RegionProvince; +import com.thing.sys.biz.service.SysRegionService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + + +@Service +@Primary +public class SysRegionServiceImpl extends BaseServiceImpl implements SysRegionService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public List list(Map params) { + String pid = (String) params.get("pid"); + + if(StringUtils.isBlank(pid)){ + //查询一级 + params.put("treeLevel", RegionLevelEnum.ONE.value()); + } else { + params.put("pid", Long.parseLong(pid)); + } + //查询列表 + List entityList = mapper.getList(params); + + List dtoList = new ArrayList<>(entityList.size()); + for(SysRegionEntity entity : entityList){ + SysRegionDTO dto = new SysRegionDTO(); + BeanUtils.copyProperties(entity, dto); + dto.setHasChildren(entity.getLeaf() != 1); + + dtoList.add(dto); + } + + return dtoList; + } + + @Override + public List> getTreeList() { + return mapper.getTreeList(); + } + + @Override + public SysRegionDTO get(Long id) { + SysRegionEntity entity = mapper.getById(id); + + return ConvertUtils.sourceToTarget(entity, SysRegionDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysRegionDTO dto) { + SysRegionEntity entity = ConvertUtils.sourceToTarget(dto, SysRegionEntity.class); + + //查询上级 + SysRegionEntity parentEntity = mapper.getById(dto.getPid()); + if(parentEntity == null){ + entity.setTreeLevel(RegionLevelEnum.ONE.value()); + }else { + entity.setTreeLevel(parentEntity.getTreeLevel() + 1); + //上级存在,且为叶子节点,需要修改为非叶子节点 + if(parentEntity.getLeaf() == RegionLeafEnum.YES.value()){ + parentEntity.setLeaf(RegionLeafEnum.NO.value()); + mapper.update(parentEntity); + } + } + + //新增都是叶子节点 + entity.setLeaf(RegionLeafEnum.YES.value()); + save(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysRegionDTO dto) { + SysRegionEntity entity = ConvertUtils.sourceToTarget(dto, SysRegionEntity.class); + + //上级不能为自身 + if(entity.getId().equals(entity.getPid())){ + throw new SysException(ErrorCode.SUPERIOR_REGION_ERROR); + } + + //查询上级 + SysRegionEntity parentEntity = mapper.getById(dto.getPid()); + if(parentEntity == null){ + entity.setTreeLevel(RegionLevelEnum.ONE.value()); + }else { + entity.setTreeLevel(parentEntity.getTreeLevel() + 1); + //上级存在,且为叶子节点,需要修改为非叶子节点 + if(parentEntity.getLeaf() == RegionLeafEnum.YES.value()){ + parentEntity.setLeaf(RegionLeafEnum.NO.value()); + mapper.update(parentEntity); + } + } + + //查询下级 + int subCount = mapper.getCountByPid(dto.getId()); + if(subCount == 0){ + entity.setLeaf(RegionLeafEnum.YES.value()); + }else { + entity.setLeaf(RegionLeafEnum.NO.value()); + } + + updateById(entity); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long id) { + //删除 + mapper.deleteById(id); + } + + @Override + public int getCountByPid(Long pid) { + return mapper.getCountByPid(pid); + } + + @Override + public List getRegion(boolean threeLevel) { + List provinceList = mapper.getListByLevel(RegionLevelEnum.ONE.value()); + List cityList = mapper.getListByLevel(RegionLevelEnum.TWO.value()); + + List provinces = ConvertUtils.sourceToTarget(provinceList, RegionProvince.class); + List cities = ConvertUtils.sourceToTarget(cityList, RegionCity.class); + + for(RegionCity city : cities){ + for(RegionProvince province : provinces){ + if(city.getPid().equals(province.getId())){ + province.getCities().add(city); + } + } + } + + //无需显示3级区县 + if(!threeLevel){ + return provinces; + } + + List countyList = mapper.getListByLevel(RegionLevelEnum.THREE.value()); + List counties = ConvertUtils.sourceToTarget(countyList, Region.class); + for(Region county : counties){ + for(RegionCity city : cities){ + if(county.getPid().equals(city.getId())){ + city.getCounties().add(county); + } + } + } + + return provinces; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleDataScopeServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleDataScopeServiceImpl.java new file mode 100644 index 0000000..29c8478 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleDataScopeServiceImpl.java @@ -0,0 +1,82 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysRoleDataScopeEntity; +import com.thing.sys.biz.mapper.SysRoleDataScopeMapper; +import com.thing.sys.biz.service.SysRoleDataScopeService; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 角色数据权限 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +@Primary +public class SysRoleDataScopeServiceImpl extends BaseServiceImpl + implements SysRoleDataScopeService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public List getDeptIdList(Long roleId) { + return mapper.getDeptIdList(roleId); + } + + @Override + public List getRoleIdList(List deptIdList) { + return mapper.getRoleIdList(deptIdList); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdate(Long roleId, List deptIdList) { + //先删除角色数据权限关系 + deleteByRoleIds(new Long[]{roleId}); + + //角色没有一个数据权限的情况 + if(CollUtil.isEmpty(deptIdList)){ + return ; + } + + //保存角色数据权限关系 + for(Long deptId : deptIdList){ + SysRoleDataScopeEntity sysRoleDataScopeEntity = new SysRoleDataScopeEntity(); + sysRoleDataScopeEntity.setDeptId(deptId); + sysRoleDataScopeEntity.setRoleId(roleId); + + //保存 + save(sysRoleDataScopeEntity); + } + } + + @Override + public void deleteByRoleIds(Long[] roleIds) { + mapper.deleteByRoleIds(roleIds); + } + + @Override + public SysRoleDataScopeEntity getOrgDataByRoleDept(Long roleId, Long deptId) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq(SysRoleDataScopeEntity::getRoleId,roleId).eq(SysRoleDataScopeEntity::getDeptId, deptId); +// .eq(SysRoleDataScopeEntity::getType, StructureTypeEnum.ORG) + List resultList = this.mapper.selectListExt(queryWrapper); + if(!resultList.isEmpty()) { + return resultList.get(0); + } + return null; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleMenuServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleMenuServiceImpl.java new file mode 100644 index 0000000..369f841 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleMenuServiceImpl.java @@ -0,0 +1,71 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysRoleMenuEntity; +import com.thing.sys.biz.mapper.SysRoleMenuMapper; +import com.thing.sys.biz.service.SysRoleMenuService; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + + +/** + * 角色与菜单对应关系 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@Primary +public class SysRoleMenuServiceImpl extends BaseServiceImpl implements SysRoleMenuService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveOrUpdate(Long roleId, List menuIdList) { + //先删除角色菜单关系 + deleteByRoleIds(new Long[]{roleId}); + + //角色没有一个菜单权限的情况 + if(CollUtil.isEmpty(menuIdList)){ + return ; + } + + //保存角色菜单关系 + for(Long menuId : menuIdList){ + SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity(); + sysRoleMenuEntity.setMenuId(menuId); + sysRoleMenuEntity.setRoleId(roleId); + + //保存 + save(sysRoleMenuEntity); + } + } + + @Override + public List getMenuIdList(Long roleId){ + return mapper.getMenuIdList(roleId); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByRoleIds(Long[] roleIds) { + mapper.deleteByRoleIds(roleIds); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByMenuId(Long menuId) { + mapper.deleteByMenuId(menuId); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleServiceImpl.java new file mode 100644 index 0000000..1a533d2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleServiceImpl.java @@ -0,0 +1,157 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.RoleType; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.SuperTenantEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.mapper.SysRoleMapper; +import com.thing.sys.biz.service.*; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; + +/** + * 角色 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysRoleServiceImpl extends BaseServiceImpl implements SysRoleService { + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleDataScopeService sysRoleDataScopeService; + private final SysRoleUserService sysRoleUserService; + private final SysDeptService sysDeptService; + + public QueryWrapper getWrapper(Map params){ + String name = (String)params.get("name"); + String type = (String)params.get("type"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("name", name, StringUtils.isNotBlank(name)); + wrapper.eq("type", type, StringUtils.isNotBlank(type)); + + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if(user.getSuperAdmin() == SuperAdminEnum.NO.value() && + user.getSuperTenant() == SuperTenantEnum.NO.value()) { + List deptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); + if(CollectionUtils.isNotEmpty(deptIdList)){ + List roleIdList = sysRoleDataScopeService.getRoleIdList(deptIdList); + wrapper.in("id", roleIdList, CollectionUtils.isNotEmpty(roleIdList)); + } + } + return wrapper; + } + + @Override + public PageData page(Map params) { + paramsToUser(params); + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + return getPageData(page, SysRoleDTO.class); + } + + @Override + public List list(Map params) { + paramsToUser(params); + List entityList = mapper.selectListByQuery(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysRoleDTO.class); + } + + @Override + public SysRoleDTO getDefaultRole(Map params) { + params.put("type", RoleType.DEFAULT.getValue()); + QueryWrapper wrapper = getWrapper(params); + Long tenantCode = ObjectUtil.isNotNull(params.get("tenantCode")) && StringUtils.isNotBlank(String.valueOf(params.get("tenantCode"))) ? + Long.parseLong(String.valueOf(params.get("tenantCode"))) : null ; + SysRoleEntity entity; + if (ObjectUtil.isNotNull(tenantCode)) { + wrapper.eq(SysRoleEntity::getTenantCode, tenantCode).limit(1); + entity = mapper.selectOneByQuery(wrapper); + } else { + entity = mapper.selectOneExt(wrapper); + } + return ConvertUtils.sourceToTarget(entity, SysRoleDTO.class); + } + + + @Override + public SysRoleDTO get(Long id) { + SysRoleEntity entity = mapper.selectOneById(id); + + return ConvertUtils.sourceToTarget(entity, SysRoleDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + + //保存角色 + entity.setType(RoleType.USER.getValue()); + save(entity); + + //保存角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + + //保存角色数据权限关系 + sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + + //更新角色 + updateById(entity); + + //更新角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + + //更新角色数据权限关系 + sysRoleDataScopeService.saveOrUpdate(entity.getId(), dto.getDeptIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除角色 + batchDelete(ids); + + //删除角色用户关系 + sysRoleUserService.deleteByRoleIds(ids); + + //删除角色菜单关系 + sysRoleMenuService.deleteByRoleIds(ids); + + //删除角色数据权限关系 + sysRoleDataScopeService.deleteByRoleIds(ids); + } + + private void paramsToUser(Map params) { + params.put("type", RoleType.USER.getValue()); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleUserServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleUserServiceImpl.java new file mode 100644 index 0000000..f1951b6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysRoleUserServiceImpl.java @@ -0,0 +1,90 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysRoleUserEntity; +import com.thing.sys.biz.mapper.SysRoleUserMapper; +import com.thing.sys.biz.service.SysRoleUserService; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 角色用户关系 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +@Primary +public class SysRoleUserServiceImpl extends BaseServiceImpl implements SysRoleUserService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public void saveOrUpdate(Long userId, List roleIdList) { + //先删除角色用户关系 + deleteByUserIds(new Long[]{userId}); + + //用户没有一个角色权限的情况 + if(CollUtil.isEmpty(roleIdList)){ + return ; + } + + //保存角色用户关系 + for(Long roleId : roleIdList){ + SysRoleUserEntity sysRoleUserEntity = new SysRoleUserEntity(); + sysRoleUserEntity.setUserId(userId); + sysRoleUserEntity.setRoleId(roleId); + + //保存 + save(sysRoleUserEntity); + } + } + + @Override + public void deleteByRoleIds(Long[] roleIds) { + mapper.deleteByRoleIds(roleIds); + } + + @Override + public void deleteByUserIds(Long[] userIds) { + mapper.deleteByUserIds(userIds); + } + + @Override + public List getRoleIdList(Long userId) { + + return mapper.getRoleIdList(userId); + } + + /** + * 获取已分配角色的数量 + * + * @param roleIds 角色主键 + * @return 数量 + */ + @Override + public Long selectCountByRoleIds(List roleIds) { + if(CollectionUtil.isEmpty(roleIds)) { + return 0L; + } + return mapper.selectCountByQuery(new QueryWrapper() + .in(SysRoleUserEntity::getRoleId, roleIds)); + } + + @Override + public List getRoleIdLists(Long userId,Long te) { + + return mapper.getRoleIdLists(userId,te); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserMenuServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserMenuServiceImpl.java new file mode 100644 index 0000000..a399b1e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserMenuServiceImpl.java @@ -0,0 +1,113 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.mybatisflex.core.util.UpdateEntity; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysUserMenuDTO; +import com.thing.sys.biz.dto.SysUserMenuPageDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.SysUserMenuEntity; +import com.thing.sys.biz.mapper.SysUserMenuMapper; +import com.thing.sys.biz.service.SysUserMenuService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import jakarta.annotation.Resource; +import org.springframework.context.annotation.Lazy; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * sys_user_menu + * + * @author Mark sunlightcs@gmail.com + * @since 3.0 2022-05-27 + */ +@Service +@Primary +public class SysUserMenuServiceImpl extends BaseServiceImpl implements SysUserMenuService { + + @Lazy + @Resource + private SysUserService sysUserService; + + @DataFilter + public QueryWrapper getWrapper(Map params){ + return QueryWrapper.create(); + } + + @Override + public PageData getSysUserMenuPage(Map params){ + paramsToLike(params, "tenantName"); + // IPage page = PageUtils.getPage(params, "su.create_date", false); + Page page = getPage(params); + params.put("tenantCode", UserContext.getRealTenantCode()); + List sysUserMenuPageList = mapper.getSysUserMenuList(params); + return getPageData(sysUserMenuPageList.stream().peek(item -> item.setLogo(OSSFactory.splice(item.getLogo()))).collect(Collectors.toList()), page.getTotalRow(), SysUserMenuPageDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void saveSysUserMenu(SysUserMenuDTO sysUserMenuDTO) { + SysUserMenuEntity sysUserMenuEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(SysUserMenuEntity::getUserId, sysUserMenuDTO.getUserId()).limit(1)); + if (ObjectUtil.isNotNull(sysUserMenuEntity)) throw new SysException("企业已设置路由存在!"); +// sysUserService.update(new SysUserEntity(), Wrappers.lambdaUpdate().eq(SysUserEntity::getId, sysUserMenuDTO.getUserId()) +// .set(SysUserEntity::getUrl, sysUserMenuDTO.getUrl())); + SysUserEntity sysUserEntity = UpdateEntity.of(SysUserEntity.class); + sysUserEntity.setUrl(sysUserMenuDTO.getUrl()); + sysUserService.update(sysUserEntity, QueryWrapper.create().eq(SysUserEntity::getId, sysUserMenuDTO.getUserId())); + + save(ConvertUtils.sourceToTarget(sysUserMenuDTO, SysUserMenuEntity.class)); + } + + @Override + public SysUserMenuDTO getSysUserMenuByUserId(Long userId){ + SysUserMenuEntity sysUserMenuEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(SysUserMenuEntity::getUserId, userId).limit(1)); + if(sysUserMenuEntity != null) { + sysUserMenuEntity.setLogo(OSSFactory.splice(sysUserMenuEntity.getLogo())); + } + return ConvertUtils.sourceToTarget(sysUserMenuEntity, SysUserMenuDTO.class); + } + + @Override + public void deleteByIds(Long[] ids) { + Arrays.asList(ids).forEach(id->{ + SysUserMenuEntity sysUserMenuEntity = mapper.selectOneById(id); + + SysUserEntity sysUserEntity = UpdateEntity.of(SysUserEntity.class); + sysUserEntity.setUrl(null); + sysUserService.update(sysUserEntity, QueryWrapper.create().eq(SysUserEntity::getId, sysUserMenuEntity.getUserId())); + + }); + batchDelete(ids); + } + + @Override + public SysUserMenuDTO queryUrlByTenantcode(Long tenantCode) { + SysUserMenuEntity sysUserMenuEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(SysUserMenuEntity::getTenantCode, tenantCode).limit(1)); + if(ObjectUtil.isNotNull(sysUserMenuEntity)){ + // sysUserMenuEntity.setLogo(OSSFactory.splice(sysUserMenuEntity.getLogo())); + + return ConvertUtils.convertWithTypeAdapt(sysUserMenuEntity,SysUserMenuDTO.class); + } + return new SysUserMenuDTO(); + } + + @Override + public SysUserMenuDTO get(Long id) { + return ConvertUtils.sourceToTarget(mapper.selectOneById(id), SysUserMenuDTO.class); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserPostServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserPostServiceImpl.java new file mode 100644 index 0000000..171248f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserPostServiceImpl.java @@ -0,0 +1,66 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysUserPostEntity; +import com.thing.sys.biz.mapper.SysUserPostMapper; +import com.thing.sys.biz.service.SysUserPostService; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 用户岗位关系 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@Primary +public class SysUserPostServiceImpl extends BaseServiceImpl implements SysUserPostService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public void saveOrUpdate(Long userId, List postIdList) { + //先删除用户岗位关系 + deleteByUserIds(new Long[]{userId}); + + //用户没有一个岗位权限的情况 + if(CollUtil.isEmpty(postIdList)){ + return ; + } + + //保存角色用户关系 + for(Long postId : postIdList){ + SysUserPostEntity sysUserPostEntity = new SysUserPostEntity(); + sysUserPostEntity.setUserId(userId); + sysUserPostEntity.setPostId(postId); + + //保存 + save(sysUserPostEntity); + } + } + + @Override + public void deleteByPostIds(Long[] postIds) { + mapper.deleteByPostIds(postIds); + } + + @Override + public void deleteByUserIds(Long[] userIds) { + mapper.deleteByUserIds(userIds); + } + + @Override + public List getPostIdList(Long userId) { + return mapper.getPostIdList(userId); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserServiceImpl.java new file mode 100644 index 0000000..c618e1f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserServiceImpl.java @@ -0,0 +1,384 @@ +package com.thing.sys.biz.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Maps; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.SuperTenantEnum; +import com.thing.common.core.enumeration.TenantGroupEnum; +import com.thing.common.core.enumeration.UserStatusEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import com.thing.password.PasswordUtils; +import com.thing.sys.biz.dto.BindDTO; +import com.thing.sys.biz.dto.CtlUserDTO; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.dto.SysUserMenuDTO; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.biz.service.*; +import com.thing.sys.biz.utils.HttpClientUtil; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.thing.sys.biz.entity.table.SysUserEntityTableDef.SYS_USER_ENTITY; + +/** + * 系统用户 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +public class SysUserServiceImpl extends BaseServiceImpl implements SysUserService { + private final SysRoleUserService sysRoleUserService; + private final SysUserPostService sysUserPostService; + private final SysDeptService sysDeptService; + private final SysParamsService sysParamsService; + private final MsgUserService msgUserService; + private final SysTenantService sysTenantService; + private final SysUserMenuService sysUserMenuService; + private final SysRoleService sysRoleService; + private static final String WECHAT_BIND_PARAMS = "WECHAT_BIND_PARAMS"; + + @Value("${spring.title}") + private String title; + + @Value("${spring.sign}") + private String sign; + + + @Value("${spring.technical}") + private String technical ; + + @Value("${spring.phone}") + private String phone ; + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public PageData page(Map params) { + //分页 + String keyword = (String) params.get("keyword"); + QueryWrapper queryWrapper = QueryWrapper.create(); + if(StringUtils.isNotBlank(keyword)){ + queryWrapper.where(SYS_USER_ENTITY.MOBILE.like(keyword).or(SYS_USER_ENTITY.EMAIL.like(keyword))); + } + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() != SuperAdminEnum.NO.value() + && user.getSuperTenant() == SuperTenantEnum.NO.value()) { + List subDeptIdList = sysDeptService.getSubDeptIdList(user.getDeptId()); + queryWrapper.in(SysUserEntity::getDeptId, subDeptIdList,CollectionUtils.isNotEmpty(subDeptIdList)); + } + queryWrapper.eq(SysUserEntity::getTenantCode,TenantContext.getTenantCode(user)); + //转换成like + String username = (String) params.get("username"); + queryWrapper.like(SysUserEntity::getUsername, username,StringUtils.isNotBlank(username)); + Long deptId = MapUtil.getLong(params, "deptId"); + queryWrapper.eq(SysUserEntity::getDeptId, deptId, !Objects.isNull(deptId)); + Page page = mapper.paginate(new Page<>(MapUtil.getInt(params, "page", 1), MapUtil.getInt(params, "limit", 10)), queryWrapper); + PageData data = getPageData(page.getRecords(), page.getTotalRow(), SysUserDTO.class); + data.getList().forEach(temp -> { + List roleIdList = sysRoleUserService.getRoleIdList(temp.getId()); + if (!roleIdList.isEmpty()) { + roleIdList.forEach(info -> { + SysRoleEntity sysRoleEntity = sysRoleService.getMapper().selectOneById(info); + if (ObjectUtil.isNotNull(sysRoleEntity)) { + if (temp.getRoleNameStr() != null) { + temp.setRoleNameStr(temp.getRoleNameStr() + "," + sysRoleEntity.getName()); + } else { + temp.setRoleNameStr(sysRoleEntity.getName()); + } + } + }); + } + }); + return data; + } + + @Override + public List list(Map params) { + List list = mapper.getList(getQueryParams(params)); + + return ConvertUtils.sourceToTarget(list, SysUserDTO.class); + } + + @Override + public List listWithOutAuth() { + List list = mapper.selectListByQuery(QueryWrapper.create().eq(SysUserEntity::getTenantCode, TenantContext.getTenantCode(SecurityUser.getUser()))); + return ConvertUtils.sourceToTarget(list, SysUserDTO.class); + } + + /** + * 系统用户 + * + * @param params 入参 + * @return list + */ + @Override + public List ctlList(Map params) { + //查询 + List list = mapper.getList(getQueryParams(params)); + return ConvertUtils.sourceToTarget(list, CtlUserDTO.class); + } + + private Map getQueryParams(Map params) { + //普通管理员,只能查询所属部门及子部门的数据 + UserDetail user = SecurityUser.getUser(); + if (user.getSuperAdmin() != SuperAdminEnum.NO.value() + && user.getSuperTenant() == SuperTenantEnum.NO.value()) { + params.put("deptIdList", sysDeptService.getSubDeptIdList(user.getDeptId())); + } + //转换成like + paramsToLike(params, "username", "keyword"); + String deptId = (String) params.get("deptId"); + params.put("deptId", StringUtils.isNotEmpty(deptId) ? Long.valueOf(deptId) : null); + //租户 + params.put(Constant.TENANT_CODE, TenantContext.getTenantCode(user)); + + return params; + } + + @Override + public SysUserDTO get(Long id) { + SysUserEntity entity = mapper.getById(id); + + return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); + } + + @Override + public SysUserDTO getByUsername(String username) { + SysUserEntity entity = mapper.getByUsername(username); + return ConvertUtils.sourceToTarget(entity, SysUserDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysUserDTO dto) { + SysUserEntity entity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + + //密码加密 + String password = PasswordUtils.encode(entity.getPassword()); + entity.setPassword(password); + + + //保存用户 + entity.setSuperTenant(SuperTenantEnum.NO.value()); + entity.setSuperAdmin(SuperAdminEnum.NO.value()); + entity.setTenantGroup(TenantGroupEnum.NO.value()); + entity.setTenantCode(TenantContext.getTenantCode(SecurityUser.getUser())); + SysUserMenuDTO sysUserMenuDTO = sysUserMenuService.queryUrlByTenantcode(TenantContext.getTenantCode(SecurityUser.getUser())); + + entity.setUrl(sysUserMenuDTO.getUrl()); + save(entity); + + //保存角色用户关系 + sysRoleUserService.saveOrUpdate(entity.getId(), dto.getRoleIdList()); + + //保存用户岗位关系 + sysUserPostService.saveOrUpdate(entity.getId(), dto.getPostIdList()); + + MsgUserDTO msgUserDTO = new MsgUserDTO(); + msgUserDTO.setUserId(entity.getId()); + msgUserDTO.setEmailUsername(entity.getEmail()); + msgUserDTO.setDingdingPhone(entity.getMobile()); + msgUserDTO.setWxPhone(entity.getMobile()); + msgUserDTO.setWxOpenId(entity.getOpenId()); + msgUserService.saveDto(msgUserDTO); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysUserDTO dto) { + SysUserEntity entity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + + //密码加密 + if (StringUtils.isBlank(dto.getPassword())) { + entity.setPassword(null); + } else { + String password = PasswordUtils.encode(entity.getPassword()); + entity.setPassword(password); + } + + //更新用户 + updateById(entity); + + //更新角色用户关系 + sysRoleUserService.saveOrUpdate(entity.getId(), dto.getRoleIdList()); + + //更新用户岗位关系 + sysUserPostService.saveOrUpdate(entity.getId(), dto.getPostIdList()); + + //保存用户配置信息 + MsgUserDTO msgUserDTO = msgUserService.getInfoByUserId(entity.getId()); + if (ObjectUtil.isNotNull(msgUserDTO)) { + msgUserDTO.setEmailUsername(entity.getEmail()); + msgUserDTO.setDingdingPhone(entity.getMobile()); + msgUserDTO.setWxPhone(entity.getMobile()); + msgUserDTO.setWxOpenId(entity.getOpenId()); + msgUserService.updateDto(msgUserDTO); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //删除用户 + mapper.deleteBatchByIds(Arrays.asList(ids)); + + //删除角色用户关系 + sysRoleUserService.deleteByUserIds(ids); + + //删除用户岗位关系 + sysUserPostService.deleteByUserIds(ids); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updatePassword(Long id, String newPassword) { + newPassword = PasswordUtils.encode(newPassword); + + mapper.updatePassword(id, newPassword); + } + + @Override + public int getCountByDeptId(Long deptId) { + return mapper.getCountByDeptId(deptId); + } + + @Override + public List getUserIdListByDeptId(List deptIdList) { + return mapper.getUserIdListByDeptId(deptIdList); + } + + @Override + public List getSysUserDTOList(List idList) { + List sysUserEntities = mapper.selectListByQuery(QueryWrapper.create().in(SysUserEntity::getId, idList)); + return ConvertUtils.sourceToTarget(sysUserEntities, SysUserDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void bindOpenId(BindDTO bindDTO) { + //正则校验用户名是否符合用户名,手机,邮箱的准则,并校验信息 + String emailRegex = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$"; + String phoneRegex = "^[1][3578]\\d{9}$"; + List entityList; + if (bindDTO.getUsername().matches(emailRegex)) { + entityList = mapper.selectListByQuery(QueryWrapper.create().eq(SysUserEntity::getEmail, bindDTO.getUsername())); + } else if (bindDTO.getUsername().matches(phoneRegex)) { + entityList = mapper.selectListByQuery(QueryWrapper.create().eq(SysUserEntity::getMobile, bindDTO.getUsername())); + } else { + entityList = mapper.selectListByQuery(QueryWrapper.create().eq(SysUserEntity::getUsername, bindDTO.getUsername())); + } + if (CollectionUtil.isEmpty(entityList)) + throw new SysException("您输入的用户名/手机/邮箱并不存在,请输入正确的用户名/手机/邮箱"); + if (entityList.size() > 1) + throw new SysException("您输入的并不是唯一的对应关系,请输入唯一手机/邮箱,或联系管理员"); + SysUserEntity entity = entityList.get(0); + //密码错误 + if (!PasswordUtils.matches(bindDTO.getPassword(), entity.getPassword())) + throw new SysException("您输入的密码错误,请输入正确的密码"); + //账号停用 + if (entity.getStatus() == UserStatusEnum.DISABLE.value()) + throw new SysException("您输入的账号已被停用,请联系管理员"); + //租户停用 + SysTenantDTO tenantDTO = sysTenantService.getTenantCode(entity.getTenantCode()); + if (tenantDTO == null || tenantDTO.getStatus() == 0) + throw new SysException("该账号的租户已被停用,请联系管理员"); + if (StringUtils.isNotBlank(entity.getOpenId())) throw new SysException("已存在绑定信息"); + + String bindParams = sysParamsService.getValue(WECHAT_BIND_PARAMS); + if (StringUtils.isBlank(bindParams)) { + throw new SysException("系统参数:WECHAT_BIND_PARAMS 未配置,格式示例:{\"weChatAppId\":\"wxed829a289e1b8ce6\",\"weChatSecret\":\"ad001be8995572b0677696818ba475f7\",\"weChatAuthorizationGrantType\":\"authorization_code\"}"); + } + Map map = JSON.parseObject(bindParams, Map.class); + String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + map.get("weChatAppId") + "&secret=" + map.get("weChatSecret") + "&code=" + bindDTO.getCode() + "&grant_type=" + map.get("weChatAuthorizationGrantType"); + String data = HttpClientUtil.post(url, Maps.newHashMap()); + JSONObject object = JSONObject.parseObject(data); + if (ObjectUtil.isEmpty(object.get("openid"))) throw new SysException("绑定失败:" + object); + entity.setOpenId(String.valueOf(object.get("openid"))); + mapper.update(entity); + MsgUserDTO msgUserDTO = msgUserService.getInfoByUserId(entity.getId()); + if (ObjectUtil.isNotNull(msgUserDTO)) { + msgUserDTO.setWxOpenId(entity.getOpenId()); + msgUserService.updateDto(msgUserDTO); + } + } + + @Override + public String title() { + return title; + } + + @Override + public String sign() { + return sign; + } + + + + @Override + public String support() { + if(StringUtils.isEmpty(technical) && StringUtils.isEmpty(phone)){ + return ""; + }else if(StringUtils.isEmpty(technical) || StringUtils.isEmpty(phone)){ + return technical+phone; + } + return technical+" | " + phone; + } + + @Override + public SysUserEntity getUserbySuperAdmin() { + return CollectionUtils.isNotEmpty(mapper.getListbySuperAdmin()) ? mapper.getListbySuperAdmin().get(0) : null; + } + + @Override + public List queryUserList(String tenantCode) { + List list = mapper.selectListByQuery(QueryWrapper.create().eq(SysUserEntity::getTenantCode, Long.valueOf(tenantCode))); + return ConvertUtils.sourceToTarget(list, SysUserDTO.class); } + + @Override + public List getUserNameList(String[] wxUser) { + return mapper.getUserNameList(wxUser); + } + + @Override + public List getUserNameLists(List longs) { + return mapper.getUserNameLists(longs); + } + + @Override + public List getUserName(List longs) { + return mapper.getUserName(longs); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserTokenServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserTokenServiceImpl.java new file mode 100644 index 0000000..257bacf --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/service/impl/SysUserTokenServiceImpl.java @@ -0,0 +1,119 @@ +package com.thing.sys.biz.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.TokenGenerator; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.entity.SysOnlineEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; +import com.thing.sys.biz.mapper.SysUserTokenMapper; +import com.thing.sys.biz.service.SysUserTokenService; +import com.thing.sys.security.service.ShiroService; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Service +@Primary +public class SysUserTokenServiceImpl extends BaseServiceImpl implements SysUserTokenService { + + //private final static int EXPIRE = 3600 * 12; + + @Value(value = "${thing.token.expire:2592000}") + private Long expire; + + @Autowired + private ShiroService shiroService; + + @Override + public QueryWrapper getWrapper(Map params) { + return null; + } + + @Override + public Result createToken(Long userId) { + //用户token + String token; + + //当前时间 + Date now = new Date(); + //过期时间 + Date expireTime = new Date(now.getTime() + expire * 1000L); + + //判断是否生成过token + SysUserTokenEntity tokenEntity = mapper.getByUserId(userId); + if(tokenEntity == null){ + //生成一个token + token = TokenGenerator.generateValue(); + + tokenEntity = new SysUserTokenEntity(); + tokenEntity.setUserId(userId); + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //保存token + save(tokenEntity); + }else{ + //判断token是否过期 + if(tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){ + //token过期,重新生成token + token = TokenGenerator.generateValue(); + }else { + token = tokenEntity.getToken(); + } + + tokenEntity.setToken(token); + tokenEntity.setUpdateDate(now); + tokenEntity.setExpireDate(expireTime); + + //更新token + this.updateById(tokenEntity); + } + + Map map = new HashMap<>(2); + map.put(Constant.TOKEN_HEADER, token); + map.put("expire", expire); + SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId()); + map.put("tenantCode",userEntity.getTenantCode()); + map.put("username",userEntity.getUsername()); + map.put("userid",userEntity.getId()); + map.put("superAdmin",userEntity.getSuperAdmin()); + map.put("superTenant",userEntity.getSuperTenant()); + return new Result().ok(map); + } + + @Override + public void logout(Long userId) { + Date expireDate = DateUtils.addMinutes(new Date(), -1); + mapper.logout(userId, expireDate); + } + + @Override + public PageData onlinePage(Map params) { + //转换成like + paramsToLike(params, "username"); + + //分页 + Page page = getPage(params); + + //查询 + params.put("expireDate", new Date()); + List list = mapper.getOnlineList(params); + + return new PageData<>(list, page.getTotalRow()); + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/biz/utils/HttpClientUtil.java b/modules/thing/src/main/java/com/thing/sys/biz/utils/HttpClientUtil.java new file mode 100644 index 0000000..0014a3c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/biz/utils/HttpClientUtil.java @@ -0,0 +1,83 @@ +package com.thing.sys.biz.utils; + +import org.apache.http.HttpEntity; +import org.apache.http.HttpResponse; +import org.apache.http.NameValuePair; +import org.apache.http.client.entity.UrlEncodedFormEntity; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.message.BasicNameValuePair; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class HttpClientUtil { + public static CloseableHttpClient httpClient = HttpClientBuilder.create().build(); + /** + * get请求获取String类型数据 + * @param url 请求链接 + * @return + */ + public static String get(String url){ + StringBuffer sb = new StringBuffer(); + HttpGet httpGet = new HttpGet(url); + try { + HttpResponse response = httpClient.execute(httpGet); + + HttpEntity entity = response.getEntity(); + InputStreamReader reader = new InputStreamReader(entity.getContent(),"utf-8"); + char [] charbufer; + while (0 data){ + StringBuffer sb = new StringBuffer(); + HttpPost httpPost = new HttpPost(url); + List valuePairs = new ArrayList<>(); + if(null != data) { + for (String key : data.keySet()) { + valuePairs.addAll((Collection) new BasicNameValuePair(key, data.get(key))); + } + } + try { + httpPost.setEntity(new UrlEncodedFormEntity(valuePairs)); + HttpResponse response = httpClient.execute(httpPost); + HttpEntity httpEntity = response.getEntity(); + BufferedInputStream bis = new BufferedInputStream(httpEntity.getContent()); + byte [] buffer; + while (0> page( @RequestParam Map params) { + PageData page = sysLogErrorService.page(params); + return new Result>().ok(page); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @RequiresPermissions("sys:log:error") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + List list = sysLogErrorService.list(params); + List idList = Lists.newArrayList(ids); + if (CollectionUtil.isNotEmpty(idList)) + list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + List excelList = ConvertUtils.sourceToTarget(list, SysLogErrorExcel.class); + ExcelUtils.exportExcel(excelList, null, "异常日志", SysLogErrorExcel.class, "异常日志.xls", response); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogLoginController.java b/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogLoginController.java new file mode 100644 index 0000000..941240b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogLoginController.java @@ -0,0 +1,77 @@ +package com.thing.sys.log.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.log.dto.SysLogLoginDTO; +import com.thing.sys.log.excel.SysLogLoginExcel; +import com.thing.sys.log.service.SysLogLoginService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/log/login") +@Tag(name = "登录日志") +@RequiredArgsConstructor +public class SysLogLoginController { + private final SysLogLoginService sysLogLoginService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "status",description ="状态 0:失败 1:成功 2:账号已锁定"), + @Parameter(name = "creatorName",description ="用户名") + }) + @RequiresPermissions("sys:log:login") + public Result> page( @RequestParam Map params) { + PageData page = sysLogLoginService.page(params); + + return new Result>().ok(page); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @Parameters({ + @Parameter(name = "status",description ="状态 0:失败 1:成功 2:账号已锁定"), + @Parameter(name = "creatorName",description ="用户名") + }) + @RequiresPermissions("sys:log:login") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + List list = sysLogLoginService.list(params); + List idList = Lists.newArrayList(ids); + if (CollectionUtil.isNotEmpty(idList)) + list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + List excelList = ConvertUtils.sourceToTarget(list, SysLogLoginExcel.class); + ExcelUtils.exportExcel(excelList, null, "登录日志", SysLogLoginExcel.class, "登录日志.xls", response); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogOperationController.java b/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogOperationController.java new file mode 100644 index 0000000..fc9f807 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/controller/SysLogOperationController.java @@ -0,0 +1,72 @@ +package com.thing.sys.log.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.log.dto.SysLogOperationDTO; +import com.thing.sys.log.excel.SysLogOperationExcel; +import com.thing.sys.log.service.SysLogOperationService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@RestController +@RequestMapping("sys/log/operation") +@Tag(name = "操作日志") +@RequiredArgsConstructor +public class SysLogOperationController { + private final SysLogOperationService sysLogOperationService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "status",description ="状态 0:失败 1:成功") + }) + @RequiresPermissions("sys:log:operation") + public Result> page( @RequestParam Map params) { + PageData page = sysLogOperationService.page(params); + + return new Result>().ok(page); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + @RequiresPermissions("sys:log:operation") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + List list = sysLogOperationService.list(params); + List idList = Lists.newArrayList(ids); + if (CollectionUtil.isNotEmpty(idList)) + list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + List excelList = ConvertUtils.sourceToTarget(list, SysLogOperationExcel.class); + ExcelUtils.exportExcel(excelList, null, "操作日志", SysLogOperationExcel.class, "操作日志.xls", response); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogErrorDTO.java b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogErrorDTO.java new file mode 100644 index 0000000..0e735ef --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogErrorDTO.java @@ -0,0 +1,41 @@ +package com.thing.sys.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "异常日志") +public class SysLogErrorDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "请求URI") + private String requestUri; + @Schema(description = "请求方式") + private String requestMethod; + @Schema(description = "请求参数") + private String requestParams; + @Schema(description = "用户代理") + private String userAgent; + @Schema(description = "操作IP") + private String ip; + @Schema(description = "异常信息") + private String errorInfo; + @Schema(description = "创建时间") + // @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogLoginDTO.java b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogLoginDTO.java new file mode 100644 index 0000000..5bbe63a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogLoginDTO.java @@ -0,0 +1,48 @@ + + +package com.thing.sys.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "登录日志") +public class SysLogLoginDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "用户操作 0:用户登录 1:用户退出") + private Integer operation; + + @Schema(description = "状态 0:失败 1:成功 2:账号已锁定") + private Integer status; + + @Schema(description = "用户代理") + private String userAgent; + + @Schema(description = "操作IP") + private String ip; + + @Schema(description = "用户名") + private String creatorName; + + @Schema(description = "创建时间") + // @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogOperationDTO.java b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogOperationDTO.java new file mode 100644 index 0000000..30a76b3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/dto/SysLogOperationDTO.java @@ -0,0 +1,59 @@ + + +package com.thing.sys.log.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@Schema( name= "操作日志") +public class SysLogOperationDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "用户操作") + private String operation; + + @Schema(description = "请求URI") + private String requestUri; + + @Schema(description = "请求方式") + private String requestMethod; + + @Schema(description = "请求参数") + private String requestParams; + + @Schema(description = "请求时长(毫秒)") + private Integer requestTime; + + @Schema(description = "用户代理") + private String userAgent; + + @Schema(description = "操作IP") + private String ip; + + @Schema(description = "状态 0:失败 1:成功") + private Integer status; + + @Schema(description = "用户名") + private String creatorName; + + @Schema(description = "创建时间") + // @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogErrorEntity.java b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogErrorEntity.java new file mode 100644 index 0000000..7c558b7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogErrorEntity.java @@ -0,0 +1,59 @@ + + +package com.thing.sys.log.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.sys.log.dto.SysLogErrorDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_log_error") +public class SysLogErrorEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 请求URI + */ + private String requestUri; + /** + * 请求方式 + */ + private String requestMethod; + /** + * 请求参数 + */ + private String requestParams; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + /** + * 异常信息 + */ + private String errorInfo; + + public SysLogErrorDTO toDto() { + SysLogErrorDTO dto = ConvertUtils.sourceToTarget(this, SysLogErrorDTO.class); + dto.setCreateDate(new Date(getCreateDate())); + return dto; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogLoginEntity.java b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogLoginEntity.java new file mode 100644 index 0000000..718fe87 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogLoginEntity.java @@ -0,0 +1,57 @@ + + +package com.thing.sys.log.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.IpUtils; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.sys.log.dto.SysLogLoginDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_log_login") +public class SysLogLoginEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户操作 0:用户登录 1:用户退出 + */ + private Integer operation; + /** + * 状态 0:失败 1:成功 2:账号已锁定 + */ + private Integer status; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + + private String internalIp; + /** + * 用户名 + */ + private String creatorName; + + public SysLogLoginDTO toDto() { + SysLogLoginDTO dto = ConvertUtils.sourceToTarget(this, SysLogLoginDTO.class); + dto.setCreateDate(new Date(getCreateDate())); + return dto; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogOperationEntity.java b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogOperationEntity.java new file mode 100644 index 0000000..2001b3b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/entity/SysLogOperationEntity.java @@ -0,0 +1,73 @@ +package com.thing.sys.log.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.entity.BaseEntity; +import com.thing.sys.log.dto.SysLogOperationDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Table("sys_log_operation") +public class SysLogOperationEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户操作 + */ + private String operation; + /** + * 请求URI + */ + private String requestUri; + /** + * 请求方式 + */ + private String requestMethod; + /** + * 请求参数 + */ + private String requestParams; + /** + * 请求时长(毫秒) + */ + private Integer requestTime; + /** + * 用户代理 + */ + private String userAgent; + /** + * 操作IP + */ + private String ip; + + /** + * 内网IP + */ + private String internalIp; + /** + * 状态 0:失败 1:成功 + */ + private Integer status; + /** + * 用户名 + */ + private String creatorName; + + public SysLogOperationDTO toDto() { + SysLogOperationDTO dto = ConvertUtils.sourceToTarget(this, SysLogOperationDTO.class); + dto.setCreateDate(new Date(getCreateDate())); + return dto; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginOperationEnum.java b/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginOperationEnum.java new file mode 100644 index 0000000..e8b0ba6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginOperationEnum.java @@ -0,0 +1,28 @@ +package com.thing.sys.log.enumeration; + +/** + * 登录操作枚举 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public enum LoginOperationEnum { + /** + * 用户登录 + */ + LOGIN(0), + /** + * 用户退出 + */ + LOGOUT(1); + + private final int value; + + LoginOperationEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginStatusEnum.java b/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginStatusEnum.java new file mode 100644 index 0000000..1190841 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/enumeration/LoginStatusEnum.java @@ -0,0 +1,32 @@ +package com.thing.sys.log.enumeration; + +/** + * 登录状态枚举 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public enum LoginStatusEnum { + /** + * 失败 + */ + FAIL(0), + /** + * 成功 + */ + SUCCESS(1), + /** + * 账号已锁定 + */ + LOCK(2); + + private final int value; + + LoginStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/enumeration/OperationStatusEnum.java b/modules/thing/src/main/java/com/thing/sys/log/enumeration/OperationStatusEnum.java new file mode 100644 index 0000000..0abc3ed --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/enumeration/OperationStatusEnum.java @@ -0,0 +1,28 @@ +package com.thing.sys.log.enumeration; + +/** + * 操作状态枚举 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public enum OperationStatusEnum { + /** + * 失败 + */ + FAIL(0), + /** + * 成功 + */ + SUCCESS(1); + + private final int value; + + OperationStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogErrorExcel.java b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogErrorExcel.java new file mode 100644 index 0000000..a38801a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogErrorExcel.java @@ -0,0 +1,32 @@ +package com.thing.sys.log.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class SysLogErrorExcel { + @Excel(name="请求URI", orderNum = "1", height = 20, width = 25) + private String requestUri; + + @Excel(name="请求方式", orderNum = "2", height = 20, width = 25) + private String requestMethod; + + @Excel(name="请求参数", orderNum = "3", height = 20, width = 25) + private String requestParams; + + @Excel(name="User-Agent", orderNum = "4", height = 20, width = 25) + private String userAgent; + + @Excel(name="操作IP", orderNum = "5", height = 20, width = 25) + private String ip; + + @Excel(name="创建时间", orderNum = "6", height = 20, width = 25, format = "yyyy-MM-dd HH:mm:ss", exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date createDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogLoginExcel.java b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogLoginExcel.java new file mode 100644 index 0000000..8027ad8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogLoginExcel.java @@ -0,0 +1,34 @@ +package com.thing.sys.log.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class SysLogLoginExcel { + @Excel(name="用户操作", height = 20, width = 25) + private String operation; + + //@Excel(name = "状态", replace = {"失败_0", "成功_1", "账号已锁定_1"}) + @Excel(name="状态", height = 20, width = 25) + private Integer status; + + @Excel(name="User-Agent", height = 20, width = 25) + private String userAgent; + + @Excel(name="操作IP", height = 20, width = 25) + private String ip; + + @Excel(name="用户名", height = 20, width = 25) + private String creatorName; + + @Excel(name="创建时间", height = 20, width = 25, format = "yyyy-MM-dd HH:mm:ss", exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogOperationExcel.java b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogOperationExcel.java new file mode 100644 index 0000000..911d433 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/excel/SysLogOperationExcel.java @@ -0,0 +1,45 @@ +package com.thing.sys.log.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.util.Date; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class SysLogOperationExcel { + @Excel(name="用户操作", height = 20, width = 25) + private String operation; + + @Excel(name="请求URI", height = 20, width = 25) + private String requestUri; + + @Excel(name="请求方式", height = 20, width = 25) + private String requestMethod; + + @Excel(name="请求参数", height = 20, width = 25) + private String requestParams; + + @Excel(name="请求时长(毫秒)", height = 20, width = 25) + private Integer requestTime; + + @Excel(name="User-Agent", height = 20, width = 25) + private String userAgent; + + @Excel(name="操作IP", height = 20, width = 25) + private String ip; + + @Excel(name="状态", height = 20, width = 25, replace = {"失败_0", "成功_1"}) + private Integer status; + + @Excel(name="用户名", height = 20, width = 25) + private String creatorName; + + @Excel(name="创建时间", height = 20, width = 25, format = "yyyy-MM-dd HH:mm:ss", exportFormat = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/handler/SysExceptionHandler.java b/modules/thing/src/main/java/com/thing/sys/log/handler/SysExceptionHandler.java new file mode 100644 index 0000000..7e74b71 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/handler/SysExceptionHandler.java @@ -0,0 +1,85 @@ +package com.thing.sys.log.handler; + +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.ExceptionUtils; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.core.utils.IpUtils; +import com.thing.common.core.web.response.Result; +import com.thing.sys.log.entity.SysLogErrorEntity; +import com.thing.sys.log.service.SysLogErrorService; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.shiro.authz.UnauthorizedException; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Map; + +@Slf4j +@RestControllerAdvice +@RequiredArgsConstructor +public class SysExceptionHandler { + + private final SysLogErrorService sysLogErrorService; + + /** + * 处理自定义异常 + */ + @ExceptionHandler(SysException.class) + public Result handleSysException(SysException ex){ + Result result = new Result<>(); + result.error(ex.getCode(), ex.getMsg()); + return result; + } + + @ExceptionHandler(DuplicateKeyException.class) + public Result handleDuplicateKeyException(DuplicateKeyException ex){ + Result result = new Result<>(); + result.error(ErrorCode.DB_RECORD_EXISTS); + return result; + } + + @ExceptionHandler(Exception.class) + public Result handleException(Exception ex){ + log.error(ex.getMessage(), ex); + saveLog(ex); + return new Result<>().error(); + } + + @ExceptionHandler(UnauthorizedException.class) + public Result handleUnauthorizedException(UnauthorizedException ex){ + Result result = new Result<>(); + result.error(ErrorCode.FORBIDDEN); + return result; + } + + /** + * 保存异常日志 + */ + private void saveLog(Exception ex){ + SysLogErrorEntity log = new SysLogErrorEntity(); + + //请求相关信息 + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setRequestUri(request.getRequestURI()); + log.setRequestMethod(request.getMethod()); + Map params = HttpContextUtils.getParameterMap(request); + if(MapUtil.isNotEmpty(params)){ + log.setRequestParams(JSON.toJSONString(params)); + } + + //异常信息 + log.setErrorInfo(ExceptionUtils.getErrorStackTrace(ex)); + + //保存 + sysLogErrorService.save(log); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogErrorMapper.java b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogErrorMapper.java new file mode 100644 index 0000000..ea3b99e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogErrorMapper.java @@ -0,0 +1,16 @@ +package com.thing.sys.log.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.log.entity.SysLogErrorEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysLogErrorMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogLoginMapper.java b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogLoginMapper.java new file mode 100644 index 0000000..5ad9bbc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogLoginMapper.java @@ -0,0 +1,16 @@ +package com.thing.sys.log.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.log.entity.SysLogLoginEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysLogLoginMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogOperationMapper.java b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogOperationMapper.java new file mode 100644 index 0000000..4cad18f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/mapper/SysLogOperationMapper.java @@ -0,0 +1,16 @@ +package com.thing.sys.log.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.log.entity.SysLogOperationEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Mapper +public interface SysLogOperationMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/SysLogErrorService.java b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogErrorService.java new file mode 100644 index 0000000..2235030 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogErrorService.java @@ -0,0 +1,25 @@ +package com.thing.sys.log.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.log.dto.SysLogErrorDTO; +import com.thing.sys.log.entity.SysLogErrorEntity; + +import java.util.List; +import java.util.Map; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysLogErrorService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + boolean save(SysLogErrorEntity entity); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/SysLogLoginService.java b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogLoginService.java new file mode 100644 index 0000000..765be90 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogLoginService.java @@ -0,0 +1,24 @@ +package com.thing.sys.log.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.log.dto.SysLogLoginDTO; +import com.thing.sys.log.entity.SysLogLoginEntity; + +import java.util.List; +import java.util.Map; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysLogLoginService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + boolean save(SysLogLoginEntity entity); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/SysLogOperationService.java b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogOperationService.java new file mode 100644 index 0000000..8677f84 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/SysLogOperationService.java @@ -0,0 +1,24 @@ +package com.thing.sys.log.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.log.dto.SysLogOperationDTO; +import com.thing.sys.log.entity.SysLogOperationEntity; + +import java.util.List; +import java.util.Map; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +public interface SysLogOperationService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + boolean save(SysLogOperationEntity entity); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogErrorServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogErrorServiceImpl.java new file mode 100644 index 0000000..b4059d6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogErrorServiceImpl.java @@ -0,0 +1,57 @@ +package com.thing.sys.log.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.log.dto.SysLogErrorDTO; +import com.thing.sys.log.entity.SysLogErrorEntity; +import com.thing.sys.log.mapper.SysLogErrorMapper; +import com.thing.sys.log.service.SysLogErrorService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 异常日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +public class SysLogErrorServiceImpl extends BaseServiceImpl implements SysLogErrorService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + List dtoList = page.getRecords().stream().map(SysLogErrorEntity::toDto).collect(Collectors.toList()); + return new PageData<>(dtoList, page.getTotalRow()); + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogErrorDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(SysLogErrorEntity entity) { + return mapper.insertSelective(entity) > 0; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogLoginServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogLoginServiceImpl.java new file mode 100644 index 0000000..b014fc0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogLoginServiceImpl.java @@ -0,0 +1,66 @@ +package com.thing.sys.log.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.log.dto.SysLogLoginDTO; +import com.thing.sys.log.entity.SysLogLoginEntity; +import com.thing.sys.log.mapper.SysLogLoginMapper; +import com.thing.sys.log.service.SysLogLoginService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 登录日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +public class SysLogLoginServiceImpl extends BaseServiceImpl implements SysLogLoginService { + + + @Override + public QueryWrapper getWrapper(Map params) { + String status = (String) params.get("status"); + String creatorName = (String) params.get("creatorName"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("status", status, StringUtils.isNotBlank(status)); + wrapper.like("creator_name", creatorName, StringUtils.isNotBlank(creatorName)); + + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + List dtoList = page.getRecords().stream().map(SysLogLoginEntity::toDto).collect(Collectors.toList()); + return new PageData<>(dtoList, page.getTotalRow()); + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogLoginDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(SysLogLoginEntity entity) { + return mapper.insertSelective(entity) > 0; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogOperationServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogOperationServiceImpl.java new file mode 100644 index 0000000..8f00ba4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/log/service/impl/SysLogOperationServiceImpl.java @@ -0,0 +1,64 @@ +package com.thing.sys.log.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.log.dto.SysLogOperationDTO; +import com.thing.sys.log.entity.SysLogOperationEntity; +import com.thing.sys.log.mapper.SysLogOperationMapper; +import com.thing.sys.log.service.SysLogOperationService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 操作日志 + * + * @author Mark sunlightcs@gmail.com + * @since 1.0.0 + */ +@Service +public class SysLogOperationServiceImpl extends BaseServiceImpl implements SysLogOperationService { + + @Override + public QueryWrapper getWrapper(Map params) { + String status = (String) params.get("status"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("status", status, StringUtils.isNotBlank(status)); + + return wrapper; + } + + @Override + public PageData page(Map params) { + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + List dtoList = page.getRecords().stream().map(SysLogOperationEntity::toDto).collect(Collectors.toList()); + return new PageData<>(dtoList, page.getTotalRow()); + } + + @Override + public List list(Map params) { + List entityList = mapper.selectListByQuery(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysLogOperationDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean save(SysLogOperationEntity entity) { + return mapper.insertSelective(entity) > 0; + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/controller/MailLogController.java b/modules/thing/src/main/java/com/thing/sys/message/controller/MailLogController.java new file mode 100644 index 0000000..dcd920e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/controller/MailLogController.java @@ -0,0 +1,63 @@ +package com.thing.sys.message.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.message.dto.SysMailLogDTO; +import com.thing.sys.message.service.SysMailLogService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 邮件发送记录 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/maillog") +@Tag(name="邮件发送记录") +@RequiredArgsConstructor +public class MailLogController { + private final SysMailLogService sysMailLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "templateId",description ="templateId"), + @Parameter(name = "mailTo",description ="mailTo"), + @Parameter(name = "status",description ="status") + }) + @RequiresPermissions("sys:mail:log") + public Result> page( @RequestParam Map params){ + PageData page = sysMailLogService.page(params); + + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:mail:log") + public Result delete(@RequestBody Long[] ids){ + sysMailLogService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/controller/MailTemplateController.java b/modules/thing/src/main/java/com/thing/sys/message/controller/MailTemplateController.java new file mode 100644 index 0000000..c1541c5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/controller/MailTemplateController.java @@ -0,0 +1,131 @@ +package com.thing.sys.message.controller; + +import com.google.gson.Gson; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.email.EmailConfig; +import com.thing.sys.message.service.SysMailTemplateService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 邮件模板 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/mailtemplate") +@Tag(name="邮件模板") +@RequiredArgsConstructor +public class MailTemplateController { + private final SysMailTemplateService sysMailTemplateService; + private final SysParamsService sysParamsService; + + private final static String KEY = Constant.MAIL_CONFIG_KEY; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "name",description ="name") + }) + @RequiresPermissions("sys:mail:all") + public Result> page( @RequestParam Map params){ + PageData page = sysMailTemplateService.getPageData(params, SysMailTemplateDTO.class); + return new Result>().ok(page); + } + + @GetMapping("/config") + @Operation(summary="获取配置信息") + @RequiresPermissions("sys:mail:all") + public Result config(){ + EmailConfig config = sysParamsService.getValueObject(KEY, EmailConfig.class); + return new Result().ok(config); + } + + @PostMapping("/saveConfig") + @Operation(summary="保存配置信息") + @LogOperation("保存配置信息") + @RequiresPermissions("sys:mail:all") + public Result saveConfig(@RequestBody EmailConfig config){ + //校验数据 + ValidatorUtils.validateEntity(config); + sysParamsService.updateValueByCode(KEY, new Gson().toJson(config)); + return new Result<>(); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:mail:all") + public Result info(@PathVariable("id") Long id){ + SysMailTemplateDTO sysMailTemplate = sysMailTemplateService.getByIdAs(id, SysMailTemplateDTO.class); + return new Result().ok(sysMailTemplate); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:mail:all") + public Result save(SysMailTemplateDTO dto){ + //校验类型 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + sysMailTemplateService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:mail:all") + public Result update(SysMailTemplateDTO dto){ + //校验类型 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + sysMailTemplateService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:mail:all") + public Result delete(@RequestBody Long[] ids){ + sysMailTemplateService.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("/send") + @Operation(summary="发送邮件") + @LogOperation("发送邮件") + @RequiresPermissions("sys:mail:all") + public Result send(Long id, String mailTo, String mailCc, String params) throws Exception{ + boolean flag = sysMailTemplateService.sendMail(id, mailTo, mailCc, params); + if(flag){ + return new Result<>(); + } + return new Result<>().error("邮件发送失败"); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/controller/SmsController.java b/modules/thing/src/main/java/com/thing/sys/message/controller/SmsController.java new file mode 100644 index 0000000..e726884 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/controller/SmsController.java @@ -0,0 +1,119 @@ +package com.thing.sys.message.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SmsService; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AliyunGroup; +import com.thing.common.core.validator.group.QcloudGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.message.dto.SysSmsDTO; +import com.thing.sys.message.service.SysSmsService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/sms") +@Tag(name="短信服务") +@RequiredArgsConstructor +public class SmsController { + private final SysSmsService sysSmsService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + @RequiresPermissions("sys:sms:all") + public Result> page( @RequestParam Map params){ + PageData page = sysSmsService.getPageData(params, SysSmsDTO.class); + return new Result>().ok(page); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:sms:all") + public Result save(@RequestBody SysSmsDTO dto){ + //校验数据 + if(dto.getPlatform() == SmsService.ALIYUN.getValue()){ + //校验阿里云数据 + ValidatorUtils.validateEntity(dto.getConfig(), AliyunGroup.class); + }else if(dto.getPlatform() == SmsService.QCLOUD.getValue()){ + //校验腾讯云数据 + ValidatorUtils.validateEntity(dto.getConfig(), QcloudGroup.class); + } + sysSmsService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:sms:all") + public Result update(@RequestBody SysSmsDTO dto){ + //校验数据 + if(dto.getPlatform() == SmsService.ALIYUN.getValue()){ + //校验阿里云数据 + ValidatorUtils.validateEntity(dto.getConfig(), AliyunGroup.class); + }else if(dto.getPlatform() == SmsService.QCLOUD.getValue()){ + //校验腾讯云数据 + ValidatorUtils.validateEntity(dto.getConfig(), QcloudGroup.class); + } + sysSmsService.updateDto(dto); + return new Result<>(); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:sms:all") + public Result info(@PathVariable("id") Long id){ + SysSmsDTO sms = sysSmsService.getByIdAs(id, SysSmsDTO.class); + return new Result().ok(sms); + } + + @PostMapping("send") + @Operation(summary="发送短信") + @LogOperation("发送短信") + @Parameters({ + @Parameter(name = "smsCode",description ="短信编码"), + @Parameter(name = "mobile",description ="手机好号"), + @Parameter(name = "params",description ="参数") + }) + @RequiresPermissions("sys:sms:all") + public Result send(String smsCode, String mobile, String params){ + sysSmsService.send(smsCode, mobile, params); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:sms:all") + public Result delete(@RequestBody Long[] ids){ + sysSmsService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/controller/SysSmsLogController.java b/modules/thing/src/main/java/com/thing/sys/message/controller/SysSmsLogController.java new file mode 100644 index 0000000..823e59d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/controller/SysSmsLogController.java @@ -0,0 +1,58 @@ +package com.thing.sys.message.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.message.dto.SysSmsLogDTO; +import com.thing.sys.message.service.SysSmsLogService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/smslog") +@Tag(name="短信日志") +@RequiredArgsConstructor +public class SysSmsLogController { + private final SysSmsLogService sysSmsLogService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + @RequiresPermissions("sys:smslog:all") + public Result> page( @RequestParam Map params){ + PageData page = sysSmsLogService.getPageData(params, SysSmsLogDTO.class); + return new Result>().ok(page); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:smslog:all") + public Result delete(@RequestBody Long[] ids){ + sysSmsLogService.batchDelete(ids); + return new Result<>(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailLogDTO.java b/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailLogDTO.java new file mode 100644 index 0000000..dc41a97 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailLogDTO.java @@ -0,0 +1,54 @@ +package com.thing.sys.message.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 邮件发送记录 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "邮件发送记录") +public class SysMailLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "邮件模板ID") + private Long templateId; + + @Schema(description = "发送者") + private String mailFrom; + + @Schema(description = "收件人") + private String mailTo; + + @Schema(description = "抄送者") + private String mailCc; + + @Schema(description = "邮件主题") + private String subject; + + @Schema(description = "邮件正文") + private String content; + + @Schema(description = "发送状态 0:失败 1:成功") + private Integer status; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailTemplateDTO.java b/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailTemplateDTO.java new file mode 100644 index 0000000..e55f236 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/dto/SysMailTemplateDTO.java @@ -0,0 +1,53 @@ +package com.thing.sys.message.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Null; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 邮件模板 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "邮件模板") +public class SysMailTemplateDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @Null(message="{id.null}", groups = AddGroup.class) + @NotNull(message="{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "模板名称") + @NotBlank(message="{mail.name.require}", groups = DefaultGroup.class) + private String name; + + @Schema(description = "邮件主题") + @NotBlank(message="{mail.subject.require}", groups = DefaultGroup.class) + private String subject; + + @Schema(description = "邮件正文") + @NotBlank(message="{mail.content.require}", groups = DefaultGroup.class) + private String content; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Long createDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsDTO.java b/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsDTO.java new file mode 100644 index 0000000..76a062e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsDTO.java @@ -0,0 +1,44 @@ +package com.thing.sys.message.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.sys.message.sms.SmsConfig; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "短信") +public class SysSmsDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "短信编码") + private String smsCode; + + @Schema(description = "平台类型") + private Integer platform; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "短信配置") + private SmsConfig config; + + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsLogDTO.java b/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsLogDTO.java new file mode 100644 index 0000000..2650512 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/dto/SysSmsLogDTO.java @@ -0,0 +1,60 @@ +package com.thing.sys.message.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "短信日志") +public class SysSmsLogDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "短信编码") + private String smsCode; + + @Schema(description = "平台类型") + private Integer platform; + + @Schema(description = "手机号") + private String mobile; + + @Schema(description = "参数1") + private String params1; + + @Schema(description = "参数2") + private String params2; + + @Schema(description = "参数3") + private String params3; + + @Schema(description = "参数4") + private String params4; + + @Schema(description = "发送状态 0:失败 1:成功") + private Integer status; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/email/EmailConfig.java b/modules/thing/src/main/java/com/thing/sys/message/email/EmailConfig.java new file mode 100644 index 0000000..a714a88 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/email/EmailConfig.java @@ -0,0 +1,68 @@ +package com.thing.sys.message.email; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 邮件配置信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Schema( name= "邮件配置信息") +public class EmailConfig implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "SMTP") + @NotBlank(message="{email.smtp.require}") + private String smtp; + + @Schema(description = "端口号") + @NotNull(message="{email.port.require}") + private Integer port; + + @Schema(description = "邮箱账号") + @NotBlank(message="{email.username.require}") + private String username; + + @Schema(description = "邮箱密码") + @NotBlank(message="{email.password.require}") + private String password; + + public String getSmtp() { + return smtp; + } + + public void setSmtp(String smtp) { + this.smtp = smtp; + } + + public Integer getPort() { + return port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/email/EmailUtils.java b/modules/thing/src/main/java/com/thing/sys/message/email/EmailUtils.java new file mode 100644 index 0000000..e917074 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/email/EmailUtils.java @@ -0,0 +1,174 @@ +package com.thing.sys.message.email; + +import cn.hutool.core.map.MapUtil; + +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.message.mapper.SysMailTemplateMapper; +import com.thing.sys.message.entity.SysMailTemplateEntity; +import com.thing.sys.message.service.SysMailLogService; + +import freemarker.template.Template; + +import jakarta.mail.internet.MimeMessage; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.io.IOUtils; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Component; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.Map; +import java.util.Properties; + +/** + * 邮件工具类 + * + * @author Mark sunlightcs@gmail.com + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class EmailUtils { + + private final SysParamsService sysParamsService; + private final SysMailTemplateMapper sysMailTemplateMapper; + private final SysMailLogService sysMailLogService; + + private final static String KEY = Constant.MAIL_CONFIG_KEY; + + private JavaMailSenderImpl createMailSender(EmailConfig config) { + JavaMailSenderImpl sender = new JavaMailSenderImpl(); + sender.setHost(config.getSmtp()); + sender.setPort(config.getPort()); + sender.setUsername(config.getUsername()); + sender.setPassword(config.getPassword()); + sender.setDefaultEncoding("Utf-8"); + Properties p = new Properties(); + p.setProperty("mail.smtp.timeout", "10000"); + p.setProperty("mail.smtp.auth", "false"); + sender.setJavaMailProperties(p); + return sender; + } + + /** + * 发送邮件 + * @param templateId 模板ID + * @param to 收件人 + * @param cc 抄送 + * @param params 模板参数 + * @return true:成功 false:失败 + */ + public boolean sendMail(Long templateId, String[] to, String[] cc, Map params) throws Exception { + SysMailTemplateEntity template = sysMailTemplateMapper.selectOneById(templateId); + if(template == null){ + throw new SysException(ErrorCode.MAIL_TEMPLATE_NOT_EXISTS); + } + + EmailConfig config = sysParamsService.getValueObject(KEY, EmailConfig.class); + JavaMailSenderImpl mailSender = createMailSender(config); + MimeMessage mimeMessage = mailSender.createMimeMessage(); + + //设置utf-8编码 + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); + messageHelper.setFrom(config.getUsername()); + + //收件人 + messageHelper.setTo(to); + //抄送 + if(cc != null && cc.length > 0){ + messageHelper.setCc(cc); + } + //主题 + messageHelper.setSubject(template.getSubject()); + + //邮件正文 + String content = getFreemarkerContent(template.getContent(), params); + messageHelper.setText(content, true); + + int status = Constant.SUCCESS; + //发送邮件 + try { + mailSender.send(mimeMessage); + }catch (Exception e){ + status = Constant.FAIL; + log.error("send error", e); + } + + sysMailLogService.save(templateId, config.getUsername(), to, cc, template.getSubject(), content, status); + + return status == Constant.SUCCESS; + } + + /** + * 获取Freemarker渲染后的内容 + * @param content 模板内容 + * @param params 参数 + */ + private String getFreemarkerContent(String content, Map params) throws Exception { + if(MapUtil.isEmpty(params)){ + return content; + } + + //模板 + StringReader reader = new StringReader(content); + Template template = new Template("mail", reader, null, "utf-8"); + + //渲染模板 + StringWriter sw = new StringWriter(); + template.process(params, sw); + + content = sw.toString(); + IOUtils.closeQuietly(sw); + + return content; + } + + /** + * 发送邮件 + * @param to 收件人 + * @param cc 抄送 + * @param subject 主题 + * @param content 邮件正文 + * @return true:成功 false:失败 + */ + public boolean sendMail(String[] to, String[] cc, String subject, String content) throws Exception { + EmailConfig config = sysParamsService.getValueObject(KEY, EmailConfig.class); + JavaMailSenderImpl mailSender = createMailSender(config); + MimeMessage mimeMessage = mailSender.createMimeMessage(); + //设置utf-8编码 + MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage, true, "UTF-8"); + messageHelper.setFrom(config.getUsername()); + + //收件人 + messageHelper.setTo(to); + //抄送 + if(cc != null && cc.length > 0){ + messageHelper.setCc(cc); + } + //主题 + messageHelper.setSubject(subject); + //邮件正文 + messageHelper.setText(content, true); + + int status = Constant.SUCCESS; + //发送邮件 + try { + mailSender.send(mimeMessage); + }catch (Exception e){ + status = Constant.FAIL; + log.error("send error", e); + } + + sysMailLogService.save(null, config.getUsername(), to, cc, subject, content, status); + + return status == Constant.SUCCESS; + } + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailLogEntity.java b/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailLogEntity.java new file mode 100644 index 0000000..7f97537 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailLogEntity.java @@ -0,0 +1,56 @@ + + +package com.thing.sys.message.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 邮件发送记录 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_mail_log") +public class SysMailLogEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 邮件模板ID + */ + private Long templateId; + /** + * 发送者 + */ + private String mailFrom; + /** + * 收件人 + */ + private String mailTo; + /** + * 抄送者 + */ + private String mailCc; + /** + * 邮件主题 + */ + private String subject; + /** + * 邮件正文 + */ + private String content; + /** + * 发送状态 0:失败 1:成功 + */ + private Integer status; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailTemplateEntity.java b/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailTemplateEntity.java new file mode 100644 index 0000000..da00d80 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/entity/SysMailTemplateEntity.java @@ -0,0 +1,37 @@ +package com.thing.sys.message.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 邮件模板 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_mail_template") +public class SysMailTemplateEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模板名称 + */ + private String name; + /** + * 邮件主题 + */ + private String subject; + /** + * 邮件正文 + */ + private String content; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsEntity.java b/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsEntity.java new file mode 100644 index 0000000..1c30716 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsEntity.java @@ -0,0 +1,41 @@ +package com.thing.sys.message.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_sms") +public class SysSmsEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 短信编码 + */ + private String smsCode; + /** + * 平台类型 + */ + private Integer platform; + /** + * 短信配置 + */ + private String smsConfig; + /** + * 备注 + */ + private String remark; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsLogEntity.java b/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsLogEntity.java new file mode 100644 index 0000000..dc0056f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/entity/SysSmsLogEntity.java @@ -0,0 +1,62 @@ +package com.thing.sys.message.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_sms_log") +public class SysSmsLogEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 短信编码 + */ + private String smsCode; + /** + * 平台类型 + */ + private Integer platform; + /** + * 手机号 + */ + private String mobile; + /** + * 参数1 + */ + @Column("params_1") + private String params1; + /** + * 参数2 + */ + @Column("params_2") + private String params2; + /** + * 参数3 + */ + @Column("params_3") + private String params3; + /** + * 参数4 + */ + @Column("params_4") + private String params4; + /** + * 发送状态 0:失败 1:成功 + */ + private Integer status; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailLogMapper.java b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailLogMapper.java new file mode 100644 index 0000000..830a9d7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailLogMapper.java @@ -0,0 +1,15 @@ +package com.thing.sys.message.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.message.entity.SysMailLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 邮件发送记录 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysMailLogMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailTemplateMapper.java b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailTemplateMapper.java new file mode 100644 index 0000000..cf8593b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysMailTemplateMapper.java @@ -0,0 +1,17 @@ +package com.thing.sys.message.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.entity.SysMailTemplateEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 邮件模板 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysMailTemplateMapper extends PowerBaseMapper { + + SysMailTemplateDTO getName(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsLogMapper.java b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsLogMapper.java new file mode 100644 index 0000000..ab93fb9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsLogMapper.java @@ -0,0 +1,15 @@ +package com.thing.sys.message.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.message.entity.SysSmsLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysSmsLogMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsMapper.java b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsMapper.java new file mode 100644 index 0000000..f992829 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/mapper/SysSmsMapper.java @@ -0,0 +1,15 @@ +package com.thing.sys.message.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.message.entity.SysSmsEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysSmsMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/SysMailLogService.java b/modules/thing/src/main/java/com/thing/sys/message/service/SysMailLogService.java new file mode 100644 index 0000000..f6427fb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/SysMailLogService.java @@ -0,0 +1,31 @@ +package com.thing.sys.message.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.message.dto.SysMailLogDTO; +import com.thing.sys.message.entity.SysMailLogEntity; + +import java.util.Map; + +/** + * 邮件发送记录 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysMailLogService extends IBaseService { + + PageData page(Map params); + + /** + * 保存邮件发送记录 + * @param templateId 模板ID + * @param from 发送者 + * @param to 收件人 + * @param cc 抄送 + * @param subject 主题 + * @param content 邮件正文 + * @param status 状态 + */ + void save(Long templateId, String from, String[] to, String[] cc, String subject, String content, Integer status); +} + diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/SysMailTemplateService.java b/modules/thing/src/main/java/com/thing/sys/message/service/SysMailTemplateService.java new file mode 100644 index 0000000..e462eb4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/SysMailTemplateService.java @@ -0,0 +1,25 @@ +package com.thing.sys.message.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.entity.SysMailTemplateEntity; + +/** + * 邮件模板 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysMailTemplateService extends IBaseService { + + /** + * 发送邮件 + * @param id 邮件模板ID + * @param mailTo 收件人 + * @param mailCc 抄送 + * @param params 模板参数 + */ + boolean sendMail(Long id, String mailTo, String mailCc, String params) throws Exception; + + SysMailTemplateDTO getName(); + +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsLogService.java b/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsLogService.java new file mode 100644 index 0000000..0c01163 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsLogService.java @@ -0,0 +1,25 @@ +package com.thing.sys.message.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.message.entity.SysSmsLogEntity; + +import java.util.LinkedHashMap; + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysSmsLogService extends IBaseService { + + /** + * 保存短信发送记录 + * @param smsCode 短信编码 + * @param platform 平台 + * @param mobile 手机号 + * @param params 短信参数 + * @param status 发送状态 + */ + void save(String smsCode, Integer platform, String mobile, LinkedHashMap params, Integer status); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsService.java b/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsService.java new file mode 100644 index 0000000..54e65b2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/SysSmsService.java @@ -0,0 +1,24 @@ +package com.thing.sys.message.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.message.entity.SysSmsEntity; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysSmsService extends IBaseService { + + /** + * 发送短信 + * @param smsCode 短信编码 + * @param mobile 手机号 + * @param params 短信参数 + */ + void send(String smsCode, String mobile, String params); + + SysSmsEntity getBySmsCode(String smsCode); + +} + diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailLogServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailLogServiceImpl.java new file mode 100644 index 0000000..9c31c3b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailLogServiceImpl.java @@ -0,0 +1,54 @@ +package com.thing.sys.message.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.message.mapper.SysMailLogMapper; +import com.thing.sys.message.dto.SysMailLogDTO; +import com.thing.sys.message.entity.SysMailLogEntity; +import com.thing.sys.message.service.SysMailLogService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class SysMailLogServiceImpl extends BaseServiceImpl implements SysMailLogService { + + @Override + public PageData page(Map params) { + return getPageData(params, SysMailLogDTO.class); + } + + @Override + public QueryWrapper getWrapper(Map params){ + String templateId = (String)params.get("templateId"); + String mailTo = (String)params.get("mailTo"); + String status = (String)params.get("status"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("template_id", templateId, StringUtils.isNotBlank(templateId)); + wrapper.like( "mail_to", mailTo, StringUtils.isNotBlank(mailTo)); + wrapper.eq("status", status, StringUtils.isNotBlank(status)); + + return wrapper; + } + + @Override + public void save(Long templateId, String from, String[] to, String[] cc, String subject, String content, Integer status) { + SysMailLogEntity log = new SysMailLogEntity(); + log.setTemplateId(templateId); + log.setMailFrom(from); + log.setMailTo(JSON.toJSONString(to)); + if(cc != null){ + log.setMailCc(JSON.toJSONString(cc)); + } + log.setSubject(subject); + log.setContent(content); + log.setStatus(status); + save(log); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailTemplateServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailTemplateServiceImpl.java new file mode 100644 index 0000000..9657818 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysMailTemplateServiceImpl.java @@ -0,0 +1,55 @@ +package com.thing.sys.message.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.message.dto.SysMailTemplateDTO; +import com.thing.sys.message.mapper.SysMailTemplateMapper; +import com.thing.sys.message.email.EmailUtils; +import com.thing.sys.message.entity.SysMailTemplateEntity; +import com.thing.sys.message.service.SysMailTemplateService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Map; + +@Service +public class SysMailTemplateServiceImpl extends BaseServiceImpl implements SysMailTemplateService { + @Autowired + private EmailUtils emailUtils; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String)params.get("name"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("name", name, StringUtils.isNotBlank(name)); + + return wrapper; + } + + @Override + public boolean sendMail(Long id, String mailTo, String mailCc, String params) throws Exception{ + Map map = null; + try { + if(StringUtils.isNotEmpty(params)){ + map = JSON.parseObject(params, Map.class); + } + }catch (Exception e){ + throw new SysException(ErrorCode.JSON_FORMAT_ERROR); + } + String[] to = new String[]{mailTo}; + String[] cc = StringUtils.isBlank(mailCc) ? null : new String[]{mailCc}; + + return emailUtils.sendMail(id, to, cc, map); + } + + @Override + public SysMailTemplateDTO getName() { + return mapper.getName(); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsLogServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsLogServiceImpl.java new file mode 100644 index 0000000..49ea121 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsLogServiceImpl.java @@ -0,0 +1,68 @@ +package com.thing.sys.message.service.impl; + +import cn.hutool.core.map.MapUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.message.mapper.SysSmsLogMapper; +import com.thing.sys.message.entity.SysSmsLogEntity; +import com.thing.sys.message.service.SysSmsLogService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * 短信日志 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +public class SysSmsLogServiceImpl extends BaseServiceImpl implements SysSmsLogService { + + @Override + public QueryWrapper getWrapper(Map params){ + String smsCode = (String)params.get("smsCode"); + String mobile = (String)params.get("mobile"); + String status = (String)params.get("status"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("sms_code", smsCode, StringUtils.isNotBlank(smsCode)); + wrapper.like("mobile", mobile, StringUtils.isNotBlank(mobile)); + wrapper.eq("status", status, StringUtils.isNotBlank(status)); + + return wrapper; + } + + @Override + public void save(String smsCode, Integer platform, String mobile, LinkedHashMap params, Integer status) { + + SysSmsLogEntity smsLog = new SysSmsLogEntity(); + smsLog.setSmsCode(smsCode); + smsLog.setPlatform(platform); + smsLog.setMobile(mobile); + + //设置短信参数 + if(MapUtil.isNotEmpty(params)){ + int index = 1; + for(String value : params.values()){ + if(index == 1){ + smsLog.setParams1(value); + }else if(index == 2){ + smsLog.setParams2(value); + }else if(index == 3){ + smsLog.setParams3(value); + }else if(index == 4){ + smsLog.setParams4(value); + } + index++; + } + } + + smsLog.setStatus(status); + + save(smsLog); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsServiceImpl.java new file mode 100644 index 0000000..12a049d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/service/impl/SysSmsServiceImpl.java @@ -0,0 +1,77 @@ + +package com.thing.sys.message.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.message.mapper.SysSmsMapper; +import com.thing.sys.message.dto.SysSmsDTO; +import com.thing.sys.message.entity.SysSmsEntity; +import com.thing.sys.message.service.SysSmsService; +import com.thing.sys.message.sms.AbstractSmsService; +import com.thing.sys.message.sms.SmsConfig; +import com.thing.sys.message.sms.SmsFactory; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.LinkedHashMap; +import java.util.Map; + +@Service +public class SysSmsServiceImpl extends BaseServiceImpl implements SysSmsService { + + @Override + public QueryWrapper getWrapper(Map params){ + String platform = (String)params.get("platform"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("platform", platform, StringUtils.isNotBlank(platform)); + return wrapper; + } + + public SysSmsDTO get(Long id) { + SysSmsEntity entity = getById(id); + SysSmsDTO dto = ConvertUtils.sourceToTarget(entity, SysSmsDTO.class); + dto.setConfig(JSON.parseObject(entity.getSmsConfig(), SmsConfig.class)); + return dto; + } + + @Override + public void send(String smsCode, String mobile, String params) { + LinkedHashMap map; + try { + map = JSON.parseObject(params, LinkedHashMap.class); + }catch (Exception e){ + throw new SysException(ErrorCode.JSON_FORMAT_ERROR); + } + + //短信服务 + AbstractSmsService service = SmsFactory.build(smsCode); + if(service == null){ + throw new SysException(ErrorCode.SMS_CONFIG); + } + + //发送短信 + service.sendSms(smsCode, mobile, map); + } + + @Override + public SysSmsEntity getBySmsCode(String smsCode) { + return mapper.selectOneByQuery(QueryWrapper.create().eq(SysSmsEntity::getSmsCode, smsCode)); + } + + public void save(SysSmsDTO dto) { + SysSmsEntity entity = ConvertUtils.sourceToTarget(dto, SysSmsEntity.class); + entity.setSmsConfig(JSON.toJSONString(dto.getConfig())); + save(entity); + } + + public void update(SysSmsDTO dto) { + SysSmsEntity entity = ConvertUtils.sourceToTarget(dto, SysSmsEntity.class); + entity.setSmsConfig(JSON.toJSONString(dto.getConfig())); + updateById(entity); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/sms/AbstractSmsService.java b/modules/thing/src/main/java/com/thing/sys/message/sms/AbstractSmsService.java new file mode 100644 index 0000000..9c73534 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/sms/AbstractSmsService.java @@ -0,0 +1,36 @@ + + +package com.thing.sys.message.sms; + +import java.util.LinkedHashMap; + +/** + * 短信 + * + * @author Mark sunlightcs@gmail.com + */ +public abstract class AbstractSmsService { + /** + * 短信配置信息 + */ + SmsConfig config; + + /** + * 发送短信 + * @param smsCode 短信编码 + * @param mobile 手机号 + * @param params 参数 + */ + public abstract void sendSms(String smsCode, String mobile, LinkedHashMap params); + + /** + * + * 发送短信 + * @param smsCode 短信编码 + * @param mobile 手机号 + * @param params 参数 + * @param signName 短信签名 + * @param template 短信模板 + */ + public abstract void sendSms(String smsCode, String mobile, LinkedHashMap params, String signName, String template); +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/sms/AliyunSmsService.java b/modules/thing/src/main/java/com/thing/sys/message/sms/AliyunSmsService.java new file mode 100644 index 0000000..c6ebfcb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/sms/AliyunSmsService.java @@ -0,0 +1,103 @@ +package com.thing.sys.message.sms; + +import cn.hutool.core.map.MapUtil; +import com.alibaba.fastjson.JSON; +import com.aliyuncs.DefaultAcsClient; +import com.aliyuncs.IAcsClient; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest; +import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse; +import com.aliyuncs.exceptions.ClientException; +import com.aliyuncs.http.MethodType; +import com.aliyuncs.profile.DefaultProfile; +import com.aliyuncs.profile.IClientProfile; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SmsService; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.sys.message.service.SysSmsLogService; + +import java.util.LinkedHashMap; + +/** + * 阿里云短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +public class AliyunSmsService extends AbstractSmsService { + /** + * 短信API产品名称(短信产品名固定,无需修改) + */ + private final String PRODUCT = "Dysmsapi"; + /** + * 短信API产品域名(接口地址固定,无需修改) + */ + private final String DOMAIN = "dysmsapi.aliyuncs.com"; + + private IClientProfile profile; + + + public AliyunSmsService(SmsConfig config){ + this.config = config; + + //初始化 + init(); + } + + private void init(){ + try { + //初始化acsClient,暂不支持region化 + profile = DefaultProfile.getProfile("cn-hangzhou", config.getAliyunAccessKeyId(), config.getAliyunAccessKeySecret()); + DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", PRODUCT, DOMAIN); + } catch (ClientException e) { + e.printStackTrace(); + } + } + + @Override + public void sendSms(String smsCode, String mobile, LinkedHashMap params) { + this.sendSms(smsCode, mobile, params, config.getAliyunSignName(), config.getAliyunTemplateCode()); + } + + @Override + public void sendSms(String smsCode, String mobile, LinkedHashMap params, String signName, String template) { + System.setProperty("sun.net.client.defaultConnectTimeout", "30000"); + System.setProperty("sun.net.client.defaultReadTimeout", "30000"); + + //组装请求对象 + SendSmsRequest request = new SendSmsRequest(); + request.setMethod(MethodType.POST); + //待发送手机号,支持以逗号分隔的形式进行批量调用,批量上限为1000个手机号码,批量调用相对于单条调用及时性稍有延迟,验证码类型的短信推荐使用单条调用的方式 + //发送国际/港澳台消息时,接收号码格式为00+国际区号+号码,如"0085200000000" + request.setPhoneNumbers(mobile); + //短信签名-可在短信控制台中找到 + request.setSignName(signName); + //短信模板-可在短信控制台中找到 + request.setTemplateCode(template); + //参数 + if(MapUtil.isNotEmpty(params)){ + request.setTemplateParam(JSON.toJSONString(params)); + } + + SendSmsResponse response; + try { + IAcsClient acsClient = new DefaultAcsClient(profile); + response = acsClient.getAcsResponse(request); + } catch (ClientException e) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, e, ""); + } + + int status = Constant.SUCCESS; + if(!Constant.OK.equalsIgnoreCase(response.getCode())){ + status = Constant.FAIL; + } + + //保存短信记录 + SysSmsLogService sysSmsLogService = SpringContextUtils.getBean(SysSmsLogService.class); + sysSmsLogService.save(smsCode, SmsService.ALIYUN.getValue(), mobile, params, status); + + if(status == Constant.FAIL){ + throw new SysException(ErrorCode.SEND_SMS_ERROR, response.getMessage()); + } + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/sms/QcloudSmsService.java b/modules/thing/src/main/java/com/thing/sys/message/sms/QcloudSmsService.java new file mode 100644 index 0000000..8be491e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/sms/QcloudSmsService.java @@ -0,0 +1,62 @@ +package com.thing.sys.message.sms; + +import cn.hutool.core.map.MapUtil; +import com.github.qcloudsms.SmsSingleSender; +import com.github.qcloudsms.SmsSingleSenderResult; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SmsService; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.sys.message.service.SysSmsLogService; + +import java.util.ArrayList; +import java.util.LinkedHashMap; + +/** + * 腾讯云短信服务 + * + * @author Mark sunlightcs@gmail.com + */ +public class QcloudSmsService extends AbstractSmsService { + public QcloudSmsService(SmsConfig config){ + this.config = config; + } + + @Override + public void sendSms(String smsCode, String mobile, LinkedHashMap params) { + this.sendSms(smsCode, mobile, params, config.getQcloudSignName(), config.getQcloudTemplateId()); + } + + @Override + public void sendSms(String smsCode, String mobile, LinkedHashMap params, String signName, String template) { + SmsSingleSender sender = new SmsSingleSender(config.getQcloudAppId(), config.getQcloudAppKey()); + + //短信参数 + ArrayList paramsList = new ArrayList<>(); + if(MapUtil.isNotEmpty(params)){ + for(String value : params.values()){ + paramsList.add(value); + } + } + SmsSingleSenderResult result; + try { + result = sender.sendWithParam("86", mobile, Integer.parseInt(template), paramsList, signName, null, null); + } catch (Exception e) { + throw new SysException(ErrorCode.SEND_SMS_ERROR, e, ""); + } + + int status = Constant.SUCCESS; + if(result.result != 0){ + status = Constant.FAIL; + } + + //保存短信记录 + SysSmsLogService sysSmsLogService = SpringContextUtils.getBean(SysSmsLogService.class); + sysSmsLogService.save(smsCode, SmsService.QCLOUD.getValue(), mobile, params, status); + + if(status == Constant.FAIL){ + throw new SysException(ErrorCode.SEND_SMS_ERROR, result.errMsg); + } + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/message/sms/SmsConfig.java b/modules/thing/src/main/java/com/thing/sys/message/sms/SmsConfig.java new file mode 100644 index 0000000..399ec28 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/sms/SmsConfig.java @@ -0,0 +1,67 @@ +package com.thing.sys.message.sms; + +import com.thing.common.core.validator.group.AliyunGroup; +import com.thing.common.core.validator.group.QcloudGroup; +import com.thing.common.core.validator.group.QiniuGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serializable; + +/** + * 短信配置信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "短信配置信息") +public class SmsConfig implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "阿里云AccessKeyId") + @NotBlank(message="{aliyun.accesskeyid.require}", groups = AliyunGroup.class) + private String aliyunAccessKeyId; + + @Schema(description = "阿里云AccessKeySecret") + @NotBlank(message="{aliyun.accesskeysecret.require}", groups = AliyunGroup.class) + private String aliyunAccessKeySecret; + + @Schema(description = "阿里云短信签名") + @NotBlank(message="{aliyun.signname.require}", groups = AliyunGroup.class) + private String aliyunSignName; + + @Schema(description = "阿里云短信模板") + @NotBlank(message="{aliyun.templatecode.require}", groups = AliyunGroup.class) + private String aliyunTemplateCode; + + @Schema(description = "腾讯云AppId") + @NotNull(message="{qcloud.appid.require}", groups = QcloudGroup.class) + private Integer qcloudAppId; + + @Schema(description = "腾讯云AppKey") + @NotBlank(message="{qcloud.appkey.require}", groups = QcloudGroup.class) + private String qcloudAppKey; + + @Schema(description = "腾讯云短信签名") + @NotBlank(message="{qcloud.signname.require}", groups = QcloudGroup.class) + private String qcloudSignName; + + @Schema(description = "腾讯云短信模板ID") + @NotBlank(message="{qcloud.templateid.require}", groups = QcloudGroup.class) + private String qcloudTemplateId; + + @Schema(description = "七牛accesskey") + @NotNull(message="{qiniu.accesskey.require}", groups = QiniuGroup.class) + private String qiniuAccessKey; + + @Schema(description = "七牛SecretKey") + @NotBlank(message="{qiniu.secretkey.require}", groups = QiniuGroup.class) + private String qiniuSecretKey; + + @Schema(description = "七牛短信模板ID") + @NotBlank(message="{qiniu.templateId.require}", groups = QiniuGroup.class) + private String qiniuTemplateId; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/message/sms/SmsFactory.java b/modules/thing/src/main/java/com/thing/sys/message/sms/SmsFactory.java new file mode 100644 index 0000000..892d939 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/message/sms/SmsFactory.java @@ -0,0 +1,34 @@ +package com.thing.sys.message.sms; + +import com.alibaba.fastjson.JSON; +import com.thing.common.core.enumeration.SmsService; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.sys.message.entity.SysSmsEntity; +import com.thing.sys.message.service.SysSmsService; + +/** + * 短信Factory + * + * @author Mark sunlightcs@gmail.com + */ +public class SmsFactory { + private static SysSmsService sysSmsService; + + static { + SmsFactory.sysSmsService = SpringContextUtils.getBean(SysSmsService.class); + } + + public static AbstractSmsService build(String smsCode){ + //获取短信配置信息 + SysSmsEntity smsEntity = sysSmsService.getBySmsCode(smsCode); + SmsConfig config = JSON.parseObject(smsEntity.getSmsConfig(), SmsConfig.class); + + if(smsEntity.getPlatform() == SmsService.ALIYUN.getValue()){ + return new AliyunSmsService(config); + }else if(smsEntity.getPlatform() == SmsService.QCLOUD.getValue()){ + return new QcloudSmsService(config); + } + + return null; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/controller/SysNoticeController.java b/modules/thing/src/main/java/com/thing/sys/notice/controller/SysNoticeController.java new file mode 100644 index 0000000..16d29a2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/controller/SysNoticeController.java @@ -0,0 +1,143 @@ + +package com.thing.sys.notice.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.notice.service.SysNoticeService; +import com.thing.sys.notice.service.SysNoticeUserService; +import com.thing.sys.security.domain.SecurityUser; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; + +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +/** +* 通知管理 +* +* @author Mark sunlightcs@gmail.com +*/ +@RestController +@RequestMapping("sys/notice") +@Tag(name="通知管理") +@RequiredArgsConstructor +public class SysNoticeController { + private final SysNoticeService sysNoticeService; + private final SysNoticeUserService sysNoticeUserService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + @RequiresPermissions("sys:notice:all") + public Result> page( @RequestParam Map params){ + PageData page = sysNoticeService.getPageData(params, SysNoticeDTO.class); + return new Result>().ok(page); + } + + @GetMapping("user/page") + @Operation(summary="获取被通知的用户") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + }) + @RequiresPermissions("sys:notice:all") + public Result> userPage( @RequestParam Map params){ + PageData page = sysNoticeService.getNoticeUserPage(params); + return new Result>().ok(page); + } + + @GetMapping("mynotice/page") + @Operation(summary="获取我的通知") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + }) + public Result> myNoticePage( @RequestParam Map params){ + PageData page = sysNoticeService.getMyNoticePage(params); + return new Result>().ok(page); + } + + @PutMapping("mynotice/read/{noticeId}") + @Operation(summary="标记我的通知为已读") + public Result read(@PathVariable("noticeId") Long noticeId){ + sysNoticeUserService.updateReadStatus(SecurityUser.getUserId(), noticeId); + return new Result<>(); + } + + @GetMapping("mynotice/unread") + @Operation(summary="我的通知未读读") + public Result unRead(){ + int count = sysNoticeUserService.getUnReadNoticeCount(SecurityUser.getUserId()); + + return new Result().ok(count); + } + + @GetMapping("{id}") + @Operation(summary="信息") + @RequiresPermissions("sys:notice:all") + public Result get(@PathVariable("id") Long id){ + SysNoticeDTO data = sysNoticeService.getByIdAs(id, SysNoticeDTO.class); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @RequiresPermissions("sys:notice:all") + public Result save(@RequestBody SysNoticeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + sysNoticeService.saveDto(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @RequiresPermissions("sys:notice:all") + public Result update(@RequestBody SysNoticeDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + sysNoticeService.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + @RequiresPermissions("sys:notice:all") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + sysNoticeService.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/dto/SysNoticeDTO.java b/modules/thing/src/main/java/com/thing/sys/notice/dto/SysNoticeDTO.java new file mode 100644 index 0000000..137ce68 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/dto/SysNoticeDTO.java @@ -0,0 +1,59 @@ + +package com.thing.sys.notice.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.thing.common.core.utils.DateTimeUtils; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** +* 通知管理 +* +* @author Mark sunlightcs@gmail.com +*/ +@Data +@Schema( name= "通知管理") +public class SysNoticeDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "通知类型") + private Integer type; + @Schema(description = "标题") + private String title; + @Schema(description = "内容") + private String content; + @Schema(description = "接收者类型 0:全部 1:部门") + private Integer receiverType; + @Schema(description = "接收者ID,用逗号分开") + private String receiverTypeIds; + @Schema(description = "接收者ID列表") + private List receiverTypeList; + @Schema(description = "发送状态 0:草稿 1:已发布") + private Integer status; + @Schema(description = "发送者") + private String senderName; + @Schema(description = "发送时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date senderDate; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date createDate; + @Schema(description = "接收者") + private String receiverName; + @Schema(description = "阅读时间") + @JsonFormat(pattern = DateTimeUtils.DATE_TIME_PATTERN_STR) + private Date readDate; + @Schema(description = "阅读状态 0:未读 1:已读") + private Integer readStatus; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeEntity.java b/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeEntity.java new file mode 100644 index 0000000..493da5a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeEntity.java @@ -0,0 +1,89 @@ +package com.thing.sys.notice.entity; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Date; + +/** + * 通知管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_notice") +public class SysNoticeEntity { + @Serial + private static final long serialVersionUID = 1536009703312548328L; + + @Id + private Long id; + + /** + * 创建者 + */ + private Long creator; + + /** + * 创建时间 + */ + @JsonFormat(shape = JsonFormat.Shape.STRING,pattern = "yyyy-MM-dd HH:mm:ss") + private Date createDate; + + /** + * 通知类型 + */ + private Integer type; + /** + * 标题 + */ + private String title; + /** + * 内容 + */ + private String content; + /** + * 接收者 0:全部 1:部门 + */ + private Integer receiverType; + /** + * 接收者ID,用逗号分开 + */ + private String receiverTypeIds; + /** + * 发送状态 0:草稿 1:已发布 + */ + private Integer status; + /** + * 发送者 + */ + private String senderName; + /** + * 发送时间 + */ + private Date senderDate; + /** + * 接收者 + */ + @Column(ignore = true) + private String receiverName; + /** + * 阅读状态 0:未读 1:已读 + */ + @Column(ignore = true) + private Integer readStatus; + /** + * 阅读时间 + */ + @Column(ignore = true) + private Date readDate; +} diff --git a/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeUserEntity.java b/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeUserEntity.java new file mode 100644 index 0000000..b1c73ab --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/entity/SysNoticeUserEntity.java @@ -0,0 +1,38 @@ +package com.thing.sys.notice.entity; + +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 我的通知 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@Table("sys_notice_user") +public class SysNoticeUserEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + /** + * 通知ID + */ + private Long noticeId; + /** + * 接收者ID + */ + private Long receiverId; + /** + * 阅读状态 0:未读 1:已读 + */ + private Integer readStatus; + /** + * 阅读时间 + */ + private Date readDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeReadStatusEnum.java b/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeReadStatusEnum.java new file mode 100644 index 0000000..1331d29 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeReadStatusEnum.java @@ -0,0 +1,29 @@ + + +package com.thing.sys.notice.enums; + +/** + * 通知阅读状态枚举 + * + * @author Mark sunlightcs@gmail.com + */ +public enum NoticeReadStatusEnum { + /** + * 未读 + */ + UNREAD(0), + /** + * 已读 + */ + READ(1); + + private int value; + + NoticeReadStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeStatusEnum.java b/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeStatusEnum.java new file mode 100644 index 0000000..873ae2b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/enums/NoticeStatusEnum.java @@ -0,0 +1,29 @@ + + +package com.thing.sys.notice.enums; + +/** + * 通知状态枚举 + * + * @author Mark sunlightcs@gmail.com + */ +public enum NoticeStatusEnum { + /** + * 草稿 + */ + DRAFT(0), + /** + * 发送 + */ + SEND(1); + + private int value; + + NoticeStatusEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/notice/enums/ReceiverTypeEnum.java b/modules/thing/src/main/java/com/thing/sys/notice/enums/ReceiverTypeEnum.java new file mode 100644 index 0000000..6238da0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/enums/ReceiverTypeEnum.java @@ -0,0 +1,29 @@ + + +package com.thing.sys.notice.enums; + +/** + * 接受者类型枚举 + * + * @author Mark sunlightcs@gmail.com + */ +public enum ReceiverTypeEnum { + /** + * 全部 + */ + ALL(0), + /** + * 部门 + */ + DEPT(1); + + private int value; + + ReceiverTypeEnum(int value) { + this.value = value; + } + + public int value() { + return this.value; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeMapper.java b/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeMapper.java new file mode 100644 index 0000000..dd889eb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeMapper.java @@ -0,0 +1,27 @@ +package com.thing.sys.notice.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.notice.entity.SysNoticeEntity; + +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 通知管理 +* +* @author Mark sunlightcs@gmail.com +*/ +@Mapper +public interface SysNoticeMapper extends PowerBaseMapper { + /** + * 获取被通知的用户列表 + */ + List getNoticeUserList(Map params); + + /** + * 获取我的通知列表 + */ + List getMyNoticeList(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeUserMapper.java b/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeUserMapper.java new file mode 100644 index 0000000..52f8420 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/mapper/SysNoticeUserMapper.java @@ -0,0 +1,25 @@ +package com.thing.sys.notice.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.notice.entity.SysNoticeUserEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** +* 我的通知 +* +* @author Mark sunlightcs@gmail.com +*/ +@Mapper +public interface SysNoticeUserMapper extends PowerBaseMapper { + /** + * 通知全部用户 + */ + void insertAllUser(SysNoticeUserEntity entity); + + /** + * 未读的通知数 + * @param receiverId 接收者ID + */ + int getUnReadNoticeCount(Long receiverId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeService.java b/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeService.java new file mode 100644 index 0000000..c78599c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeService.java @@ -0,0 +1,27 @@ +package com.thing.sys.notice.service; + + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.notice.entity.SysNoticeEntity; + +import java.util.Map; + +/** + * 通知管理 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysNoticeService extends IBaseService { + + /** + * 获取被通知的用户 + */ + PageData getNoticeUserPage(Map params); + + /** + * 获取我的通知列表 + */ + PageData getMyNoticePage(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeUserService.java b/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeUserService.java new file mode 100644 index 0000000..4dba12c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/service/SysNoticeUserService.java @@ -0,0 +1,34 @@ + +package com.thing.sys.notice.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.notice.entity.SysNoticeUserEntity; + +import org.apache.ibatis.annotations.Param; + +/** + * 我的通知 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysNoticeUserService extends IBaseService { + + /** + * 通知全部用户 + */ + void insertAllUser(SysNoticeUserEntity entity); + + /** + * 标记我的通知为已读 + * @param receiverId 接收者ID + * @param noticeId 通知ID + */ + void updateReadStatus(@Param("receiverId") Long receiverId, @Param("noticeId") Long noticeId); + + + /** + * 未读的通知数 + * @param receiverId 接收者ID + */ + int getUnReadNoticeCount(Long receiverId); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeServiceImpl.java new file mode 100644 index 0000000..6ebe08e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeServiceImpl.java @@ -0,0 +1,154 @@ +package com.thing.sys.notice.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.message.MessageData; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.notice.mapper.SysNoticeMapper; +import com.thing.sys.notice.dto.SysNoticeDTO; +import com.thing.sys.notice.entity.SysNoticeEntity; +import com.thing.sys.notice.entity.SysNoticeUserEntity; +import com.thing.sys.notice.enums.NoticeReadStatusEnum; +import com.thing.sys.notice.enums.NoticeStatusEnum; +import com.thing.sys.notice.enums.ReceiverTypeEnum; +import com.thing.sys.notice.service.SysNoticeService; +import com.thing.sys.notice.service.SysNoticeUserService; +import com.thing.sys.security.domain.SecurityUser; + +import com.thing.websocket.WebSocketServer; +import lombok.RequiredArgsConstructor; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +/** + * 通知管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +public class SysNoticeServiceImpl extends BaseServiceImpl implements SysNoticeService { + private final SysNoticeUserService sysNoticeUserService; + private final SysUserService sysUserService; + private final WebSocketServer webSocketServer; + + @Override + public QueryWrapper getWrapper(Map params){ + String type = (String)params.get("type"); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(SysNoticeEntity::getType, type, StringUtils.isNotBlank(type)).orderBy(SysNoticeEntity::getCreateDate).desc(); + return wrapper; + } + + @Override + public PageData getNoticeUserPage(Map params) { + return getPageData(params, SysNoticeDTO.class); + } + + @Override + public PageData getMyNoticePage(Map params) { + //查询 + params.put("receiverId", SecurityUser.getUserId()); + return getPageData(params, SysNoticeDTO.class); + } + + @Transactional(rollbackFor = Exception.class) + public void save(SysNoticeDTO dto) { + SysNoticeEntity entity = ConvertUtils.sourceToTarget(dto, SysNoticeEntity.class); + + //更新发送者信息 + if(dto.getStatus() == NoticeStatusEnum.SEND.value()){ + entity.setSenderName(SecurityUser.getUser().getRealName()); + entity.setSenderDate(new Date()); + } + + save(entity); + + //发送通知 + dto.setId(entity.getId()); + sendNotice(dto); + } + + @Transactional(rollbackFor = Exception.class) + public void update(SysNoticeDTO dto) { + SysNoticeEntity entity = ConvertUtils.sourceToTarget(dto, SysNoticeEntity.class); + + //更新发送者信息 + if(dto.getStatus() == NoticeStatusEnum.SEND.value()){ + entity.setSenderName(SecurityUser.getUser().getRealName()); + entity.setSenderDate(new Date()); + } + + this.updateById(entity); + + //发送通知 + sendNotice(dto); + } + + /** + * 发送通知 + */ + public void sendNotice(SysNoticeDTO notice){ + //如果是草稿,在不发送通知 + if(notice.getStatus() == NoticeStatusEnum.DRAFT.value()){ + return; + } + + //全部用户 + if(notice.getReceiverType() == ReceiverTypeEnum.ALL.value()){ + //发送给全部用户 + sendAllUser(notice); + + //通过WebSocket,提示全部用户,有新通知 + MessageData message = new MessageData().msg(notice.getTitle()); + webSocketServer.sendMessageAll(message); + + }else { //选中用户 + List userIdList = sysUserService.getUserIdListByDeptId(notice.getReceiverTypeList()); + if(userIdList.isEmpty()){ + return; + } + + //发送给选中用户 + sendUser(notice, userIdList); + + //通过WebSocket,提示选中用户,有新通知 + MessageData message = new MessageData().msg(notice.getTitle()); + webSocketServer.sendMessage(userIdList, message); + } + } + + /** + * 发送给全部用户 + */ + public void sendAllUser(SysNoticeDTO notice){ + SysNoticeUserEntity noticeUser = new SysNoticeUserEntity() + .setNoticeId(notice.getId()) + .setReadStatus(NoticeReadStatusEnum.UNREAD.value()); + sysNoticeUserService.insertAllUser(noticeUser); + } + + /** + * 发送给选中用户 + */ + public void sendUser(SysNoticeDTO notice, List userIdList){ + userIdList.forEach(userId -> { + SysNoticeUserEntity noticeUser = new SysNoticeUserEntity() + .setNoticeId(notice.getId()) + .setReceiverId(userId) + .setReadStatus(NoticeReadStatusEnum.UNREAD.value()); + + sysNoticeUserService.save(noticeUser); + }); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeUserServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeUserServiceImpl.java new file mode 100644 index 0000000..3a24df1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/notice/service/impl/SysNoticeUserServiceImpl.java @@ -0,0 +1,53 @@ +package com.thing.sys.notice.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.notice.mapper.SysNoticeUserMapper; +import com.thing.sys.notice.entity.SysNoticeUserEntity; +import com.thing.sys.notice.enums.NoticeReadStatusEnum; +import com.thing.sys.notice.service.SysNoticeUserService; + +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.Map; + +/** + * 我的通知 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +public class SysNoticeUserServiceImpl extends BaseServiceImpl implements SysNoticeUserService { + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public void insertAllUser(SysNoticeUserEntity entity) { + mapper.insertAllUser(entity); + } + + @Override + public void updateReadStatus(Long receiverId, Long noticeId) { + SysNoticeUserEntity entity = new SysNoticeUserEntity() + .setReceiverId(receiverId) + .setNoticeId(noticeId) + .setReadStatus(NoticeReadStatusEnum.READ.value()) + .setReadDate(new Date()); + + //标记为已读 + QueryWrapper query = new QueryWrapper(); + query.eq("receiver_id", receiverId); + query.eq("notice_id", noticeId); + mapper.updateByQuery(entity, query); + } + + @Override + public int getUnReadNoticeCount(Long receiverId) { + return mapper.getUnReadNoticeCount(receiverId); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/AbstractCloudStorageService.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/AbstractCloudStorageService.java new file mode 100644 index 0000000..8630c65 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/AbstractCloudStorageService.java @@ -0,0 +1,98 @@ +package com.thing.sys.oss.cloud; + +import com.thing.common.core.utils.DateTimeUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.InputStream; +import java.util.Date; +import java.util.UUID; + +/** + * 云存储(支持七牛、阿里云、腾讯云、又拍云) + * + * @author Mark sunlightcs@gmail.com + */ +public abstract class AbstractCloudStorageService { + /** 云存储配置信息 */ + CloudStorageConfig config; + + /** + * 文件路径 + * @param prefix 前缀 + * @param suffix 后缀 + * @return 返回上传路径 + */ + public String getPath(String prefix, String suffix) { + //生成uuid + String uuid = generateShortUUID(); + //文件路径 + String path = DateTimeUtils.format(new Date(), "yyyyMMdd") + "/" + uuid; + if(StringUtils.isNotBlank(prefix)){ + path = prefix + "/" + path; + } + + return path + "." + suffix; + } + + /** + * 文件上传 + * @param data 文件字节数组 + * @param path 文件路径,包含文件名 + * @return 返回http地址 + */ + public abstract String upload(byte[] data, String path); + + /** + * 文件上传 + * @param data 文件字节数组 + * @param suffix 后缀 + * @return 返回http地址 + */ + public abstract String uploadSuffix(byte[] data, String suffix); + + /** + * 文件上传 + * @param inputStream 字节流 + * @param path 文件路径,包含文件名 + * @return 返回http地址 + */ + public abstract String upload(InputStream inputStream, String path); + + /** + * 文件上传 + * @param inputStream 字节流 + * @param suffix 后缀 + * @return 返回http地址 + */ + public abstract String uploadSuffix(InputStream inputStream, String suffix); + + /** + * 拼接地址 + * @param path 路径 + * @return url + path + */ + protected abstract String splice(String path); + + /** + * 截取地址 + * @param urlPath 路径 + * @return path + */ + protected abstract String cutOut(String urlPath); + + + public abstract String delFile(String urlPath); + + public abstract String testLink(CloudStorageConfig config); + + public abstract String getConfigUrl(); + + public static String generateShortUUID() { + UUID uuid = UUID.randomUUID(); + long mostSignificantBits = uuid.getMostSignificantBits(); + long leastSignificantBits = uuid.getLeastSignificantBits(); + String combinedBits = Long.toHexString(mostSignificantBits) + Long.toHexString(leastSignificantBits); + return combinedBits.substring(0, 12); // 取前12位作为短 UUID + } + +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/AliyunCloudStorageService.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/AliyunCloudStorageService.java new file mode 100644 index 0000000..78dbc04 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/AliyunCloudStorageService.java @@ -0,0 +1,88 @@ + + +package com.thing.sys.oss.cloud; + + +import com.aliyun.oss.OSSClient; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * 阿里云存储 + * + * @author Mark sunlightcs@gmail.com + */ +public class AliyunCloudStorageService extends AbstractCloudStorageService { + + public AliyunCloudStorageService(CloudStorageConfig config){ + this.config = config; + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String path) { + OSSClient client = new OSSClient(config.getAliyunEndPoint(), config.getAliyunAccessKeyId(), + config.getAliyunAccessKeySecret()); + try { + client.putObject(config.getAliyunBucketName(), path, inputStream); + client.shutdown(); + } catch (Exception e){ + throw new SysException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + return path; + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getAliyunPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getAliyunPrefix(), suffix)); + } + + /** + * 拼接地址 + * + * @param path 路径 + * @return url + path + */ + @Override + public String splice(String path) { + return path.startsWith(config.getAliyunDomain() + "/") ? path : config.getAliyunDomain() + "/" + path; + } + + /** + * 截取地址 + * + * @param urlPath 路径 + * @return path + */ + @Override + public String cutOut(String urlPath) { + return urlPath.replace(config.getAliyunDomain() + "/", ""); + } + + @Override + public String delFile(String urlPath) { + return null; + } + + @Override + public String testLink(CloudStorageConfig config) { + return null; + } + + @Override + public String getConfigUrl() { + return null; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/CloudStorageConfig.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/CloudStorageConfig.java new file mode 100644 index 0000000..c54b634 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/CloudStorageConfig.java @@ -0,0 +1,138 @@ + + +package com.thing.sys.oss.cloud; + +import com.thing.common.core.validator.group.*; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.Range; +import org.hibernate.validator.constraints.URL; + +import java.io.Serializable; + +/** + * 云存储配置信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "云存储配置信息") +public class CloudStorageConfig implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "类型 1:七牛 2:阿里云 3:腾讯云 4:FastDFS 5:本地上传 6:MinIO") + @Range(min=1, max=6, message = "{oss.type.range}") + private Integer type; + + @Schema(description = "七牛绑定的域名") + @NotBlank(message="{qiniu.entity.require}", groups = QiniuGroup.class) + @URL(message = "{qiniu.entity.url}", groups = QiniuGroup.class) + private String qiniuDomain; + + @Schema(description = "七牛路径前缀") + private String qiniuPrefix; + + @Schema(description = "七牛ACCESS_KEY") + @NotBlank(message="{qiniu.accesskey.require}", groups = QiniuGroup.class) + private String qiniuAccessKey; + + @Schema(description = "七牛SECRET_KEY") + @NotBlank(message="{qiniu.secretkey.require}", groups = QiniuGroup.class) + private String qiniuSecretKey; + + @Schema(description = "七牛存储空间名") + @NotBlank(message="{qiniu.bucketname.require}", groups = QiniuGroup.class) + private String qiniuBucketName; + + @Schema(description = "阿里云绑定的域名") + @NotBlank(message="{aliyun.entity.require}", groups = AliyunGroup.class) + @URL(message = "{aliyun.entity.url}", groups = AliyunGroup.class) + private String aliyunDomain; + + @Schema(description = "阿里云路径前缀") + private String aliyunPrefix; + + @Schema(description = "阿里云EndPoint") + @NotBlank(message="{aliyun.endPoint.require}", groups = AliyunGroup.class) + private String aliyunEndPoint; + + @Schema(description = "阿里云AccessKeyId") + @NotBlank(message="{aliyun.accesskeyid.require}", groups = AliyunGroup.class) + private String aliyunAccessKeyId; + + @Schema(description = "阿里云AccessKeySecret") + @NotBlank(message="{aliyun.accesskeysecret.require}", groups = AliyunGroup.class) + private String aliyunAccessKeySecret; + + @Schema(description = "阿里云BucketName") + @NotBlank(message="{aliyun.bucketname.require}", groups = AliyunGroup.class) + private String aliyunBucketName; + + @Schema(description = "腾讯云绑定的域名") + @NotBlank(message="{qcloud.entity.require}", groups = QcloudGroup.class) + @URL(message = "{qcloud.entity.url}", groups = QcloudGroup.class) + private String qcloudDomain; + + @Schema(description = "腾讯云路径前缀") + private String qcloudPrefix; + + @Schema(description = "腾讯云AppId") + @NotNull(message="{qcloud.appid.require}", groups = QcloudGroup.class) + private Integer qcloudAppId; + + @Schema(description = "腾讯云SecretId") + @NotBlank(message="{qcloud.secretId.require}", groups = QcloudGroup.class) + private String qcloudSecretId; + + @Schema(description = "腾讯云SecretKey") + @NotBlank(message="{qcloud.secretkey.require}", groups = QcloudGroup.class) + private String qcloudSecretKey; + + @Schema(description = "腾讯云BucketName") + @NotBlank(message="{qcloud.bucketname.require}", groups = QcloudGroup.class) + private String qcloudBucketName; + + @Schema(description = "腾讯云COS所属地区") + @NotBlank(message="{qcloud.region.require}", groups = QcloudGroup.class) + private String qcloudRegion; + + @Schema(description = "FastDFS绑定的域名") + @NotBlank(message="{fastdfs.entity.require}", groups = FastDFSGroup.class) + @URL(message = "{fastdfs.entity.url}", groups = FastDFSGroup.class) + private String fastdfsDomain; + + @Schema(description = "本地上传绑定的域名") + @NotBlank(message="{local.entity.require}", groups = LocalGroup.class) + @URL(message = "{local.entity.url}", groups = LocalGroup.class) + private String localDomain; + + @Schema(description = "本地上传路径前缀") + private String localPrefix; + + @Schema(description = "本地上传存储目录") + @NotBlank(message="{local.path.url}", groups = LocalGroup.class) + private String localPath; + + @Schema(description = "Minio EndPoint") + @NotBlank(message="{minio.endPoint.require}", groups = MinioGroup.class) + private String minioEndPoint; + + @Schema(description = "accessKey") + @NotBlank(message="{minio.accesskey.require}", groups = MinioGroup.class) + private String minioAccessKey; + + @Schema(description = "secretKey") + @NotBlank(message="{minio.secretkey.require}", groups = MinioGroup.class) + private String minioSecretKey; + + @Schema(description = "BucketName") + @NotBlank(message="{minio.bucketname.require}", groups = MinioGroup.class) + private String minioBucketName; + + @Schema(description = "MinIO上传路径前缀") + private String minioPrefix; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSCloudStorageService.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSCloudStorageService.java new file mode 100644 index 0000000..306949a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSCloudStorageService.java @@ -0,0 +1,90 @@ +package com.thing.sys.oss.cloud; + +import com.github.tobato.fastdfs.domain.fdfs.StorePath; +import com.github.tobato.fastdfs.service.DefaultGenerateStorageClient; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.SpringContextUtils; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +/** + * FastDFS + * + * @author Mark sunlightcs@gmail.com + */ +public class FastDFSCloudStorageService extends AbstractCloudStorageService { + private static final DefaultGenerateStorageClient defaultGenerateStorageClient; + + static { + defaultGenerateStorageClient = (DefaultGenerateStorageClient) SpringContextUtils.getBean("defaultGenerateStorageClient"); + } + + public FastDFSCloudStorageService(CloudStorageConfig config){ + this.config = config; + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String suffix) { + StorePath storePath; + try { + storePath = defaultGenerateStorageClient.uploadFile("group1", inputStream, inputStream.available(), suffix); + }catch (Exception ex){ + throw new SysException(ErrorCode.OSS_UPLOAD_FILE_ERROR, ex, ex.getMessage()); + } + return storePath.getPath(); + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, suffix); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, suffix); + } + + /** + * 拼接地址 + * + * @param path 路径 + * @return url + path + */ + @Override + protected String splice(String path) { + return path.startsWith(config.getFastdfsDomain() + "/") ? path : config.getFastdfsDomain() + "/" + path; + } + + /** + * 截取地址 + * + * @param urlPath 路径 + * @return path + */ + @Override + protected String cutOut(String urlPath) { + return urlPath.replace(config.getFastdfsDomain() + "/", ""); + } + + @Override + public String delFile(String urlPath) { + return null; + } + + @Override + public String testLink(CloudStorageConfig config) { + return null; + } + + @Override + public String getConfigUrl() { + return null; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSImport.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSImport.java new file mode 100644 index 0000000..fbe3889 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/FastDFSImport.java @@ -0,0 +1,19 @@ + + +package com.thing.sys.oss.cloud; + +import com.github.tobato.fastdfs.FdfsClientConfig; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.EnableMBeanExport; +import org.springframework.context.annotation.Import; +import org.springframework.jmx.support.RegistrationPolicy; + +/** + * 导入FastDFS-Client组件 + */ +@Configuration +@Import(FdfsClientConfig.class) +@EnableMBeanExport(registration = RegistrationPolicy.IGNORE_EXISTING) +public class FastDFSImport { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/LocalCloudStorageService.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/LocalCloudStorageService.java new file mode 100644 index 0000000..3b22842 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/LocalCloudStorageService.java @@ -0,0 +1,85 @@ +package com.thing.sys.oss.cloud; + +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import org.apache.commons.io.FileUtils; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; + +/** + * 本地上传 + * + * @author Mark sunlightcs@gmail.com + */ +public class LocalCloudStorageService extends AbstractCloudStorageService { + + public LocalCloudStorageService(CloudStorageConfig config){ + this.config = config; + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String path) { + File file = new File(config.getLocalPath() + File.separator + path); + try { + FileUtils.copyToFile(inputStream, file); + } catch (IOException e) { + throw new SysException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + return path; + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getLocalPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getLocalPrefix(), suffix)); + } + + /** + * 拼接地址 + * + * @param path 路径 + * @return url + path + */ + @Override + protected String splice(String path) { + return path.startsWith(config.getLocalDomain() + "/") ? path : config.getLocalDomain() + "/" + path; + } + + /** + * 截取地址 + * + * @param urlPath 路径 + * @return path + */ + @Override + protected String cutOut(String urlPath) { + return urlPath.replace(config.getLocalDomain() + "/", ""); + } + + @Override + public String delFile(String urlPath) { + return null; + } + + @Override + public String testLink(CloudStorageConfig config) { + return null; + } + + @Override + public String getConfigUrl() { + return null; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/MinioCloudStorageService.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/MinioCloudStorageService.java new file mode 100644 index 0000000..1675e7d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/MinioCloudStorageService.java @@ -0,0 +1,156 @@ +package com.thing.sys.oss.cloud; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.enumeration.MinioFileContentTypeEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import io.minio.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Map; + +/** + * MinIO 存储 + * + * @author Mark sunlightcs@gmail.com + */ +@Slf4j +public class MinioCloudStorageService extends AbstractCloudStorageService { + private MinioClient minioClient; + + public MinioCloudStorageService(CloudStorageConfig config) { + this.config = config; + //初始化 + init(); + } + + private void init() { + minioClient = MinioClient.builder() + .endpoint(config.getMinioEndPoint()) + .credentials(config.getMinioAccessKey(), config.getMinioSecretKey()) + .build(); + } + + @Override + public String upload(byte[] data, String path) { + return upload(new ByteArrayInputStream(data), path); + } + + @Override + public String upload(InputStream inputStream, String path) { + try { + //如果BucketName不存在,则创建 + boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(config.getMinioBucketName()).build()); + if (!found) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(config.getMinioBucketName()).build()); + } + Map keyCodeMap = MinioFileContentTypeEnum.getKeyCodeMap(); + //判断文件尾缀,进行contentType的判定 + String suffix = path.substring(path.lastIndexOf(".")); + String contentType = "application/octet-stream"; + if (CollectionUtil.isNotEmpty(keyCodeMap) && ObjectUtil.isNotNull(keyCodeMap.get(suffix))) { + contentType = String.valueOf(keyCodeMap.get(suffix)); + } + minioClient.putObject(PutObjectArgs.builder().object(path) + .bucket(config.getMinioBucketName()) + .contentType(contentType) + .stream(inputStream, inputStream.available(), -1).build()); + } catch (Exception e) { + log.error("上传文件错误:", e); + throw new SysException(ErrorCode.OSS_UPLOAD_FILE_ERROR, e, ""); + } + + return path; + } + + @Override + public String uploadSuffix(byte[] data, String suffix) { + return upload(data, getPath(config.getMinioPrefix(), suffix)); + } + + @Override + public String uploadSuffix(InputStream inputStream, String suffix) { + return upload(inputStream, getPath(config.getMinioPrefix(), suffix)); + } + + /** + * 拼接地址 + * + * @param path 路径 + * @return url + path + */ + @Override + protected String splice(String path) { + String urlPath = path.replace(config.getMinioEndPoint() + "/" + config.getMinioBucketName() + "/", ""); + return urlPath.startsWith(config.getMinioEndPoint() + "/" + config.getMinioBucketName() + "/") ? path : config.getMinioEndPoint() + "/" + config.getMinioBucketName() + "/" + urlPath; + } + + /** + * 截取地址 + * + * @param urlPath 路径 + * @return path + */ + @Override + protected String cutOut(String urlPath) { + return urlPath.replace(config.getMinioEndPoint() + "/" + config.getMinioBucketName() + "/", ""); + } + + @Override + public String delFile(String urlPath) { + try { + minioClient.removeObject(RemoveObjectArgs.builder().bucket(config.getMinioBucketName()).object(urlPath).build()); + } catch (Exception e) { + return "1"; + } + return "0"; + } + + + @Override + public String testLink(CloudStorageConfig configInfo) { + try { + MinioClient client = MinioClient.builder() + .endpoint(configInfo.getMinioEndPoint()) + .credentials(configInfo.getMinioAccessKey(), configInfo.getMinioSecretKey()) + .build(); + client.listBuckets(); + } catch (Exception e) { + return MinioException.getMessageByCode(e.getMessage()); + } + return MinioException.SUCCESS.getMessage(); + } + + @Override + public String getConfigUrl() { + return config.getMinioEndPoint() + "/" + config.getMinioBucketName() + "/"; + } + + + @Getter + @AllArgsConstructor + public enum MinioException { + SUCCESS("200","minio链接成功"), + REQUEST_SIGNATURE ("The request signature we calculated does not match the signature you provided. Check your key and signing method.","密码错误"), + ACCESS_KEY("The Access Key Id you provided does not exist in our records.","用户名不存在"), + LANDMARK("Non-XML response from service. Response code: 400","IP端口错误"), + FAILED_CONNECT("Failed to connect to", "IP错误"); + private final String code; + private final String message; + + public static String getMessageByCode(String code) { + for (int i = 0; i < values().length; i++) { + MinioException type = values()[i]; + if(type.code.equals(code)|| code.contains(type.code)){ + return type.message; + } + } + return "其他错误"; + } + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/cloud/OSSFactory.java b/modules/thing/src/main/java/com/thing/sys/oss/cloud/OSSFactory.java new file mode 100644 index 0000000..33fb35a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/cloud/OSSFactory.java @@ -0,0 +1,68 @@ +package com.thing.sys.oss.cloud; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.CloudService; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.sys.biz.service.SysParamsService; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +/** + * 文件上传Factory + * @author Mark sunlightcs@gmail.com + */ +public final class OSSFactory { + private static SysParamsService sysParamsService; + public static CloudStorageConfig config; + private static final AbstractCloudStorageService storageService; + + static { + OSSFactory.sysParamsService = SpringContextUtils.getBean(SysParamsService.class); + //获取云存储配置信息 + OSSFactory.config = sysParamsService.getValueObject(Constant.CLOUD_STORAGE_CONFIG_KEY, CloudStorageConfig.class); + storageService = build(); + } + + public static AbstractCloudStorageService build(){ + if (Objects.nonNull(storageService)) { + return storageService; + } + CloudService cloudService = CloudService.match(config.getType()); + return switch (cloudService) { + case ALIYUN -> new AliyunCloudStorageService(config); + case FASTDFS -> new FastDFSCloudStorageService(config); + case LOCAL -> new LocalCloudStorageService(config); + case MINIO -> new MinioCloudStorageService(config); + default -> null; + }; + } + + public static LocalCloudStorageService buildLocalStorageService() { + CloudStorageConfig config = new CloudStorageConfig(); + config.setLocalDomain("/upload"); + config.setLocalPath("/upload"); + config.setLocalPrefix(""); + return new LocalCloudStorageService(config); + } + + /** + * 拼接地址 + * @param path 路径 + * @return url + path + */ + public static String splice(String path) { + AbstractCloudStorageService storageService = build(); + return storageService == null || StringUtils.isBlank(path) ? path : storageService.splice(path); + } + + /** + * 截取地址 + * @param urlPath 路径 + * @return path + */ + public static String cutOut(String urlPath) { + AbstractCloudStorageService storageService = build(); + return storageService == null || StringUtils.isBlank(urlPath) ? urlPath : storageService.cutOut(urlPath); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/controller/SysOssController.java b/modules/thing/src/main/java/com/thing/sys/oss/controller/SysOssController.java new file mode 100644 index 0000000..959c4a8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/controller/SysOssController.java @@ -0,0 +1,291 @@ + + +package com.thing.sys.oss.controller; + +import cn.hutool.core.map.MapUtil; +import com.clickhouse.client.internal.google.gson.Gson; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.CloudService; +import com.thing.common.core.enumeration.MimeTypeEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AliyunGroup; +import com.thing.common.core.validator.group.QcloudGroup; +import com.thing.common.core.validator.group.QiniuGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.service.SysParamsService; +import com.thing.sys.oss.cloud.AbstractCloudStorageService; +import com.thing.sys.oss.cloud.CloudStorageConfig; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.oss.entity.SysOssEntity; +import com.thing.sys.oss.entity.UpLoadDao; +import com.thing.sys.oss.service.SysOssService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.shiro.authz.annotation.RequiresPermissions; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 文件上传 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("sys/oss") +@Tag(name="aa-文件上传") +@RequiredArgsConstructor +public class SysOssController { + private final SysOssService sysOssService; + private final SysParamsService sysParamsService; + + private final static String KEY = Constant.CLOUD_STORAGE_CONFIG_KEY; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "type",description ="类型") + }) + @RequiresPermissions("sys:oss:all") + public Result> page( @RequestParam Map params){ + PageData page = sysOssService.page(params); + page.getList().stream().peek(item -> item.setUrl(OSSFactory.splice(item.getUrl()))).collect(Collectors.toList()); + return new Result>().ok(page); + } + + @GetMapping("info") + @Operation(summary="云存储配置信息") + @RequiresPermissions("sys:oss:all") + public Result info(){ + CloudStorageConfig config = sysParamsService.getValueObject(KEY, CloudStorageConfig.class); + + return new Result().ok(config); + } + + @PostMapping + @Operation(summary= "保存云存储配置信息") + @LogOperation("保存云存储配置信息") + @RequiresPermissions("sys:oss:all") + public Result saveConfig(@RequestBody CloudStorageConfig config){ + //校验类型 + ValidatorUtils.validateEntity(config); + + if(config.getType() == CloudService.QINIU.getValue()){ + //校验七牛数据 + ValidatorUtils.validateEntity(config, QiniuGroup.class); + }else if(config.getType() == CloudService.ALIYUN.getValue()){ + //校验阿里云数据 + ValidatorUtils.validateEntity(config, AliyunGroup.class); + }else if(config.getType() == CloudService.QCLOUD.getValue()){ + //校验腾讯云数据 + ValidatorUtils.validateEntity(config, QcloudGroup.class); + } + + sysParamsService.updateValueByCode(KEY, new Gson().toJson(config)); + OSSFactory.config = config; + return new Result(); + } + + @PostMapping("upload") + @Operation(summary="上传文件") + public Result> upload(@RequestParam("file") MultipartFile file) throws Exception { + AbstractCloudStorageService storageService = OSSFactory.build(); + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + //上传文件 + String url = OSSFactory.splice(storageService.uploadSuffix(file.getInputStream(), FilenameUtils.getExtension(file.getOriginalFilename()))); + Map data = new HashMap<>(1); + data.put("src", url); + return new Result>().ok(data); + } + + @PostMapping("uploadSuffix") + @Operation(summary="上传文件 -返回文件地址,不包含ip端口及桶名 ") + public Result> uploadSuffix(@RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + //上传文件 + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + String url = OSSFactory.build().uploadSuffix(file.getBytes(), extension); + + Map data = new HashMap<>(1); + data.put("src", OSSFactory.cutOut(url)); + + return new Result>().ok(data); + } + + @GetMapping("getBucketUrl") + @Operation(summary= "获取文件上传得前缀 ip+端口+桶名") + public Result getBucketUrl(){ + return new Result().ok(OSSFactory.build().getConfigUrl()); + } + + @PostMapping("uploadNew") + @Operation(summary="上传文件 -url全路径") + public Result> uploadNew(@RequestParam("file") MultipartFile file) throws Exception { + AbstractCloudStorageService storageService = OSSFactory.build(); + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + + //上传文件 + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); +// String url = OSSFactory.build().uploadSuffix(file.getBytes(), extension); + String url = OSSFactory.splice(storageService.uploadSuffix(file.getInputStream(), FilenameUtils.getExtension(file.getOriginalFilename()))); +// String url = storageService.uploadSuffix(file.getInputStream(), FilenameUtils.getExtension(file.getOriginalFilename())); + //保存文件信息 + SysOssEntity ossEntity = new SysOssEntity(); + ossEntity.setUrl(url); + ossEntity.setCreateDate(new Date().getTime()); + sysOssService.save(ossEntity); + + Map data = new HashMap<>(1); + data.put("fileName", file.getOriginalFilename()); + data.put("fileType", extension); + data.put("src", url); + data.put("id", ossEntity.getId()); + + return new Result>().ok(data); + } + + @PostMapping("uploadByType") + @Operation(summary= "上传含类型文件") + public Result> uploadByType(@RequestParam("type") String type, @RequestParam("single") Boolean single, @RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return new Result>().error(ErrorCode.UPLOAD_FILE_EMPTY); + } + //上传文件 + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + String url = OSSFactory.build().uploadSuffix(file.getBytes(), extension); + + //保存文件信息 + + SysOssEntity ossEntity = new SysOssEntity(); + ossEntity.setUrl(url); + ossEntity.setType(type); + ossEntity.setSingle(single); + sysOssService.saveOrUpdate(ossEntity); + + Map data = new HashMap<>(1); + data.put("src", url); + + return new Result>().ok(data); + } + + @PostMapping("tinymce/upload") + @Operation(summary= "TinyMCE上传文件") + public Map tinymceUpload(@RequestParam("file") MultipartFile file) throws Exception { + if (file.isEmpty()) { + return MapUtil.newHashMap(); + } + + //上传文件 + String extension = FilenameUtils.getExtension(file.getOriginalFilename()); + String url = OSSFactory.build().uploadSuffix(file.getBytes(), extension); + + //保存文件信息 + SysOssEntity ossEntity = new SysOssEntity(); + ossEntity.setUrl(url); + ossEntity.setCreateDate(System.currentTimeMillis()); + sysOssService.save(ossEntity); + + Map data = new HashMap<>(1); + data.put("location", url); + + return data; + } + + @DeleteMapping + @Operation(summary= "删除") + @LogOperation("删除") + @RequiresPermissions("sys:oss:all") + public Result delete(@RequestBody Long[] ids){ + sysOssService.getMapper().deleteBatchByIds(Arrays.asList(ids)); + + return new Result(); + } + + + + @PostMapping("/uploadBatch") + @Operation(summary = "批量上传") + public Result> uploadBatch(@RequestParam(value = "files") MultipartFile[] multipartFiles) throws IOException { + List resultList = new ArrayList<>(); + AbstractCloudStorageService storageService = OSSFactory.build(); + if(storageService == null) { + return new Result().error("获取云存储配置信息失败"); + } + for (MultipartFile multipartFile : multipartFiles) { + UpLoadDao upLoadDao = new UpLoadDao(); + String url=""; + if(!FilenameUtils.isExtension(StringUtils.lowerCase(multipartFile.getOriginalFilename()), MimeTypeEnum.getImageMimeType())) { + upLoadDao.setStatus("1"); + upLoadDao.setMsg("请上传图片类型文件"); + }else { + try { + url = storageService.uploadSuffix(multipartFile.getInputStream(), FilenameUtils.getExtension(multipartFile.getOriginalFilename())); + url=OSSFactory.splice(url); + upLoadDao.setStatus("0"); + upLoadDao.setMsg("上传成功"); + } catch (IOException e) { + upLoadDao.setStatus("1"); + upLoadDao.setMsg("网络异常,上传失败"); + } + } + upLoadDao.setOldName(multipartFile.getOriginalFilename()); + upLoadDao.setUrl(url); + resultList.add(upLoadDao); + } + return new Result().ok(resultList); + } + @PostMapping("testLink") + @Operation(summary= "测试链接") + public Result testLink(@RequestBody CloudStorageConfig config){ + return new Result().ok(sysOssService.testLink(config)); + } + + + @GetMapping("/download") + public void downloadImageFromURL(@RequestParam String imageUrl, @RequestParam String fileName, HttpServletResponse response) throws IOException { + URL url = new URL(imageUrl); + URLConnection connection = url.openConnection(); + response.setContentType("application/octet-stream"); + response.setHeader("content-type", "application/octet-stream"); + response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes("utf-8"),"ISO-8859-1")); + InputStream in = connection.getInputStream(); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + response.getOutputStream().write(buffer, 0, bytesRead); + } + } + + @GetMapping("getMapBucketUrl") + @Operation(summary= "获取地图json文件路径前缀") + public Result getMapBucketUrl(){ + return sysOssService.getConfigUrl(); + } + +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/entity/SysOssEntity.java b/modules/thing/src/main/java/com/thing/sys/oss/entity/SysOssEntity.java new file mode 100644 index 0000000..71b6980 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/entity/SysOssEntity.java @@ -0,0 +1,30 @@ +package com.thing.sys.oss.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * 文件上传 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Table("sys_oss") +public class SysOssEntity extends BaseEntity { + + /** + * URL地址 + */ + private String url; + /** + * 文件类型 + */ + private String type; + /** + * 文件类型 + */ + private Boolean single; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/oss/entity/UpLoadDao.java b/modules/thing/src/main/java/com/thing/sys/oss/entity/UpLoadDao.java new file mode 100644 index 0000000..0b96e09 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/entity/UpLoadDao.java @@ -0,0 +1,22 @@ +package com.thing.sys.oss.entity; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +@Data +@Schema( name= "批量上传返回结果") +public class UpLoadDao { + + @Schema(description = "上传状态0成功 1失败", required = true) + private String status; + @Schema(description = "上传结果中文描述", required = true) + private String msg; + @Schema(description = "上传文件名称", required = true) + private String oldName; + @Schema(description = "返回文件存储url", required = true) + private String url; + + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/mapper/SysOssMapper.java b/modules/thing/src/main/java/com/thing/sys/oss/mapper/SysOssMapper.java new file mode 100644 index 0000000..4afcaa3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/mapper/SysOssMapper.java @@ -0,0 +1,15 @@ +package com.thing.sys.oss.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.oss.entity.SysOssEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 文件上传 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysOssMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/service/SysOssService.java b/modules/thing/src/main/java/com/thing/sys/oss/service/SysOssService.java new file mode 100644 index 0000000..2a23052 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/service/SysOssService.java @@ -0,0 +1,26 @@ +package com.thing.sys.oss.service; + +import com.mybatisflex.core.service.IService; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.oss.cloud.CloudStorageConfig; +import com.thing.sys.oss.entity.SysOssEntity; + +import java.util.List; +import java.util.Map; + +/** + * 文件上传 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysOssService extends IService { + + PageData page(Map params); + + List selectUrlByType(String type); + + String testLink(CloudStorageConfig config); + + Result getConfigUrl(); +} diff --git a/modules/thing/src/main/java/com/thing/sys/oss/service/impl/SysOssServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/oss/service/impl/SysOssServiceImpl.java new file mode 100644 index 0000000..11316c9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/oss/service/impl/SysOssServiceImpl.java @@ -0,0 +1,85 @@ +package com.thing.sys.oss.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.oss.cloud.CloudStorageConfig; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.oss.entity.SysOssEntity; +import com.thing.sys.oss.mapper.SysOssMapper; +import com.thing.sys.oss.service.SysOssService; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + + +@Service +@Primary +public class SysOssServiceImpl extends BaseServiceImpl implements SysOssService { + + @Value("${spring.map-bucket}") + private String mapBucket; + + @Override + public QueryWrapper getWrapper(Map params) { + return QueryWrapper.create(); + } + + @Override + public PageData page(Map params) { + String type = (String) params.get("type"); + Page page = mapper.paginate( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false).eq("type", type, StringUtils.isNotBlank(type)) + ); + return getPageData(page, SysOssEntity.class); + } + + @Override + public boolean saveOrUpdate(SysOssEntity ossEntity) { + //唯一时 存在就覆盖 + if (ossEntity.getSingle()) { + QueryWrapper queryWrapper = new QueryWrapper() + .eq(SysOssEntity::getType, ossEntity.getType()).orderBy(SysOssEntity::getCreateDate).desc().limit(1); + SysOssEntity sysOssEntity = mapper.selectOneByQuery(queryWrapper); + ossEntity.setUrl(OSSFactory.cutOut(ossEntity.getUrl())); + if (sysOssEntity == null) { + ossEntity.setCreateDate(System.currentTimeMillis()); + save(ossEntity); + } else { + ossEntity.setId(sysOssEntity.getId()); + updateById(ossEntity); + } + } else { + save(ossEntity); + } + return true; + } + + @Override + public List selectUrlByType(String type) { + QueryWrapper queryWrapper = new QueryWrapper() + .eq(SysOssEntity::getType, type).orderBy(SysOssEntity::getCreateDate).desc(); + return mapper.selectListByQuery(queryWrapper).stream().map(item -> OSSFactory.splice(item.getUrl())).collect(Collectors.toList()); + } + + @Override + public String testLink(CloudStorageConfig config) { + return OSSFactory.build().testLink(config); + } + + @Override + public Result getConfigUrl() { + return new Result().ok(OSSFactory.build().getConfigUrl()+mapBucket+"/"); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/aspect/DataFilterAspect.java b/modules/thing/src/main/java/com/thing/sys/security/aspect/DataFilterAspect.java new file mode 100644 index 0000000..72c350b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/aspect/DataFilterAspect.java @@ -0,0 +1,126 @@ +package com.thing.sys.security.aspect; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.TenantGroupEnum; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import lombok.extern.slf4j.Slf4j; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Before; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Method; +import java.util.List; +import java.util.Objects; + +/** + * 建议把查询条件全部显示地写在服务类中,不要过分依赖该切面 + * 这个切面之所以还存在,是为了兼容老版本的写法 + */ +@Aspect +@Component +@Slf4j +public class DataFilterAspect { + + @Pointcut("@annotation(com.thing.common.orm.annotation.DataFilter)") + public void dataFilterCut() { + + } + + @Before("dataFilterCut()") + public void dataFilter(JoinPoint point) throws Exception { + MethodSignature signature = (MethodSignature)point.getSignature(); + Method method = point.getTarget().getClass().getMethod(signature.getName(), signature.getParameterTypes()); + DataFilter dataFilter = method.getAnnotation(DataFilter.class); + if(point.getArgs().length == 0 || Objects.isNull(dataFilter)) return; + + List paramList = Lists.newArrayList(point.getArgs()); + Class parameterFetch = dataFilter.parameterFetch(); + Object param = paramList.stream().filter(parameterFetch::isInstance).findFirst().orElse(null); + if(Objects.isNull(param)) return; + + if (!(param instanceof QueryWrapper)) { + return; + } + + // admin、租户管理员、普通用户数据过滤 + UserDetail userDetail = SecurityUser.getUser(); + if (SuperAdminEnum.isSuperAdmin(userDetail.getSuperAdmin())) { + handleSuperAdminSql((QueryWrapper) param, dataFilter); + } else if (TenantGroupEnum.isTenantGroup(userDetail.getTenantGroup())) { + handleTenantGroupSql((QueryWrapper) param, dataFilter); + } else { + handleGeneralUserSql((QueryWrapper) param, dataFilter); + } + + // 返回数量限制 + int limitSize = dataFilter.limitSize(); + if (limitSize > 0) { + ((QueryWrapper) param).limit(limitSize); + } + } + + /** 超级管理员数据过滤 */ + private void handleSuperAdminSql(QueryWrapper wrapper, DataFilter dataFilter) { + boolean isCompleteData = dataFilter.completeDataMark(); + // 如果超级管理员, 未切换租户且获取所有数据的标记为true,则不过滤数据 + if (checkShouldGetCompleteData(isCompleteData)) { + return; + } + handleGeneralUserSql(wrapper, dataFilter); + } + + /** 租户管理员数据过滤 */ + private void handleTenantGroupSql(QueryWrapper wrapper, DataFilter dataFilter) { + boolean isCompleteData = dataFilter.completeDataMark(); + UserDetail user = SecurityUser.getUser(); + Long currentTenantCode = TenantContext.getTenantCode(user); + List tenantCodeList = + CollectionUtil.isNotEmpty(user.getTenantCodeList()) + ? user.getTenantCodeList() + : Lists.newArrayList(); + tenantCodeList.add(user.getTenantCode()); + if (checkShouldGetCompleteData(isCompleteData)) { + wrapper.in( + dataFilter.tenantCode(), + tenantCodeList, + !CollectionUtils.isEmpty(tenantCodeList)); + } else if (CollectionUtil.contains(tenantCodeList, currentTenantCode)) { + handleGeneralUserSql(wrapper, dataFilter); + } + } + + private boolean checkShouldGetCompleteData(boolean isCompleteData) { + UserDetail user = SecurityUser.getUser(); + Long tenantCode = user.getTenantCode(); + Long companyId = user.getCompanyId(); + Long httpTenantCode = HttpContextUtils.getTenantCode(); + Long httpCompanyId = HttpContextUtils.getCompanyId(); + return ((Objects.isNull(httpTenantCode) && Objects.isNull(httpCompanyId)) + || (Objects.equals(httpTenantCode, tenantCode) + && Objects.equals(httpCompanyId, companyId))) + && isCompleteData; + } + + /** + * 普通用户数据过滤 + */ + private void handleGeneralUserSql(QueryWrapper wrapper, DataFilter dataFilter){ + Long tenantCode = UserContext.getTenantCode(); + Long deptId = UserContext.getDeptId(); + wrapper.eq(dataFilter.tenantCode(), tenantCode) + .eq(dataFilter.deptId(), deptId, Objects.nonNull(deptId)); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/security/context/TenantContext.java b/modules/thing/src/main/java/com/thing/sys/security/context/TenantContext.java new file mode 100644 index 0000000..2e978e5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/context/TenantContext.java @@ -0,0 +1,62 @@ +package com.thing.sys.security.context; + +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.TenantGroupEnum; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.sys.security.domain.UserDetail; +import org.apache.commons.lang3.StringUtils; + +import java.util.Objects; + +/** + * 租户 + * + * @author Mark sunlightcs@gmail.com + */ +public class TenantContext { + + public static Long getTenantCode(UserDetail user){ + if(user.getTenantCode() == null){ + return null; + } + + Long tenantCode = HttpContextUtils.getTenantCode(); + //超级管理员,才可以切换租户 + if(Objects.nonNull(tenantCode)){ + return tenantCode; + } + return user.getTenantCode(); + } + + + public static Long getCompanyId(UserDetail user) { + if (user.getCompanyId() == null) { + return null; + } + + Long companyId = HttpContextUtils.getTenantCode(); + //超级管理员,才可以切换租户 + if (user.getSuperAdmin() == SuperAdminEnum.YES.value() + || user.getTenantGroup() == TenantGroupEnum.YES.value()) { + return Objects.nonNull(companyId) ? companyId : user.getCompanyId(); + } + return user.getCompanyId(); + } + + public static Long getConsumerPid(UserDetail user) { + if(user.getConsumerPid() == null){ + return null; + } + + String consumerPid = HttpContextUtils.getConsumerPid(); + //超级管理员,才可以切换租户 + if(user.getSuperAdmin() == SuperAdminEnum.YES.value()){ + if(StringUtils.isNotBlank(consumerPid)){ + return Long.parseLong(consumerPid); + } + } + return user.getConsumerPid(); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/context/UserContext.java b/modules/thing/src/main/java/com/thing/sys/security/context/UserContext.java new file mode 100644 index 0000000..1b4230c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/context/UserContext.java @@ -0,0 +1,94 @@ +package com.thing.sys.security.context; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.TenantGroupEnum; +import com.thing.common.core.utils.HttpContextUtils; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; + +import java.util.Objects; + +/** + * 租户 + * + * @author Mark sunlightcs@gmail.com + */ +public class UserContext { + + public static Long getTenantCode() { + UserDetail user = SecurityUser.getUser(); + return TenantContext.getTenantCode(user); + } + + public static Long getCompanyId() { + UserDetail user = SecurityUser.getUser(); + return TenantContext.getCompanyId(user); + } + + public static Long getDeptId() { + UserDetail user = SecurityUser.getUser(); + return user.getDeptId(); + } + + public static Long getUserId() { + UserDetail user = SecurityUser.getUser(); + if (ObjectUtil.isNull(user.getId())) { + return null; + } + return user.getId(); + } + + public static Boolean isAdmin() { + UserDetail userDetail = SecurityUser.getUser(); + if (ObjectUtil.equal(SuperAdminEnum.YES.value(), userDetail.getSuperAdmin())) { + return true; + } + return false; + } + + /** + * 获取当前用户的真实租户编码 + */ + public static Long getRealTenantCode() { + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = userDetail.getTenantCode(); + Long companyId = userDetail.getCompanyId(); + Long httpTenantCode = HttpContextUtils.getTenantCode(); + Long httpCompanyId = HttpContextUtils.getCompanyId(); + Long currentTenantCode = TenantContext.getTenantCode(userDetail); + if (ObjectUtil.equal(SuperAdminEnum.YES.value(), userDetail.getSuperAdmin()) + || ObjectUtil.equal(TenantGroupEnum.YES.value(), userDetail.getTenantGroup())) { + // 如果超级管理员, 未切换租户且获取所有数据的标记为true,则不过滤数据 + if (((Objects.nonNull(httpTenantCode) && Objects.nonNull(httpCompanyId)) + && (!Objects.equals(httpTenantCode, tenantCode) + && !Objects.equals(httpCompanyId, companyId)))) { + return httpTenantCode; + } + } + return currentTenantCode; + } + + /** + * 获取当前用户的真实公司id + */ + public static Long getRealCompanyId() { + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = userDetail.getTenantCode(); + Long companyId = userDetail.getCompanyId(); + Long httpTenantCode = HttpContextUtils.getTenantCode(); + Long httpCompanyId = HttpContextUtils.getCompanyId(); + Long currentCompanyId = TenantContext.getCompanyId(userDetail); + + if (ObjectUtil.equal(SuperAdminEnum.YES.value(), userDetail.getSuperAdmin()) + || ObjectUtil.equal(TenantGroupEnum.YES.value(), userDetail.getTenantGroup())) { + // 如果超级管理员, 未切换租户且获取所有数据的标记为true,则不过滤数据 + if (((Objects.nonNull(httpTenantCode) && Objects.nonNull(httpCompanyId)) + && (!Objects.equals(httpTenantCode, tenantCode) + && !Objects.equals(httpCompanyId, companyId)))) { + return httpCompanyId; + } + } + return currentCompanyId; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/controller/LoginController.java b/modules/thing/src/main/java/com/thing/sys/security/controller/LoginController.java new file mode 100644 index 0000000..7b056f1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/controller/LoginController.java @@ -0,0 +1,219 @@ +package com.thing.sys.security.controller; + +import com.thing.common.core.enumeration.UserStatusEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.IpUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysUserDTO; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.biz.service.SysUserTokenService; +import com.thing.sys.log.entity.SysLogLoginEntity; +import com.thing.sys.log.enumeration.LoginOperationEnum; +import com.thing.sys.log.enumeration.LoginStatusEnum; +import com.thing.sys.log.service.SysLogLoginService; +import com.thing.sys.oss.service.SysOssService; +import com.thing.sys.security.domain.LoginDTO; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.password.PasswordUtils; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.service.SysTenantService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import jakarta.servlet.http.HttpServletRequest; + +import lombok.RequiredArgsConstructor; + +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.*; + +import java.util.Date; +import java.util.List; + +/** + * 登录 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@Tag(name = "登录管理") +@RequiredArgsConstructor +public class LoginController { + private final SysUserService sysUserService; + private final SysUserTokenService sysUserTokenService; + private final SysLogLoginService sysLogLoginService; + private final SysTenantService sysTenantService; + private final SysOssService sysOssService; + + @PostMapping("login/noCaptcha") + @Operation(summary= "无验证码登录") + public Result noCaptchaLogin(HttpServletRequest request, @RequestBody LoginDTO login) { + //效验数据 + ValidatorUtils.validateEntity(login); + + //用户信息 + SysUserDTO user = sysUserService.getByUsername(login.getUsername()); + + SysLogLoginEntity log = new SysLogLoginEntity(); + log.setOperation(LoginOperationEnum.LOGIN.value()); + log.setCreateDate(System.currentTimeMillis()); + log.setIp(IpUtils.getIpAddr(request)); + log.setInternalIp(IpUtils.getInternalIp(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setIp(IpUtils.getIpAddr(request)); + + //用户不存在 + if (user == null) { + log.setStatus(LoginStatusEnum.FAIL.value()); + log.setCreatorName(login.getUsername()); + sysLogLoginService.save(log); + + throw new SysException(ErrorCode.ACCOUNT_PASSWORD_ERROR); + } + + //密码错误 + if (!PasswordUtils.matches(login.getPassword(), user.getPassword())) { + log.setStatus(LoginStatusEnum.FAIL.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + sysLogLoginService.save(log); + + throw new SysException(ErrorCode.ACCOUNT_PASSWORD_ERROR); + } + + //账号停用 + if (user.getStatus() == UserStatusEnum.DISABLE.value()) { + log.setStatus(LoginStatusEnum.LOCK.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + sysLogLoginService.save(log); + + throw new SysException(ErrorCode.ACCOUNT_DISABLE); + } + + //租户停用 + SysTenantDTO tenantDTO = sysTenantService.getTenantCode(user.getTenantCode()); + if (tenantDTO == null || tenantDTO.getStatus() == 0) { + throw new SysException(ErrorCode.ACCOUNT_DISABLE); + } + + //登录成功 + log.setStatus(LoginStatusEnum.SUCCESS.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + //sysLogLoginService.saveOrUpdate(log); + + return sysUserTokenService.createToken(user.getId()); + } + + @PostMapping("logout") + @Operation(summary = "退出") + public Result logout(HttpServletRequest request) { + UserDetail user = SecurityUser.getUser(); + + //退出 + sysUserTokenService.logout(user.getId()); + + //用户信息 + SysLogLoginEntity log = new SysLogLoginEntity(); + log.setOperation(LoginOperationEnum.LOGOUT.value()); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setIp(IpUtils.getIpAddr(request)); + log.setStatus(LoginStatusEnum.SUCCESS.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + log.setCreateDate(System.currentTimeMillis()); + sysLogLoginService.save(log); + + return new Result<>(); + } + + @GetMapping("title") + @Operation(summary = "大标题") + public Result title() { + return new Result().ok(sysUserService.title()); + } + + @GetMapping("support") + @Operation(summary = "技术支持信息") + public Result support() { + return new Result().ok(sysUserService.support()); + } + + @GetMapping("sign") + @Operation(summary = "项目标识") + public Result sign() { + return new Result().ok(sysUserService.sign()); + } + + @GetMapping("logo/{type}") + @Operation(summary = "地址") + public Result> logo(@PathVariable String type) { + return new Result>().ok(sysOssService.selectUrlByType(type)); + } + + + @GetMapping("code") + @Operation(summary = "登录") + public Result code(HttpServletRequest request,@RequestBody String code) { + + //验证码是否正确 add by update renren_2.9 @SnailJian +// boolean flag = captchaService.validate(login.getUuid(), login.getCaptcha()); +// if(!flag){ +// return new Result().error(ErrorCode.CAPTCHA_ERROR); +// } + + //用户信息 + SysUserDTO user = sysUserService.getByUsername(code); + + SysLogLoginEntity log = new SysLogLoginEntity(); + log.setOperation(LoginOperationEnum.LOGIN.value()); + log.setCreateDate(new Date().getTime()); + log.setIp(IpUtils.getIpAddr(request)); + log.setUserAgent(request.getHeader(HttpHeaders.USER_AGENT)); + log.setIp(IpUtils.getIpAddr(request)); + + //用户不存在 + if(user == null){ + log.setStatus(LoginStatusEnum.FAIL.value()); + log.setCreatorName(code); + sysLogLoginService.save(log); + + throw new SysException(ErrorCode.ACCOUNT_PASSWORD_ERROR); + } + + //账号停用 + if(user.getStatus() == UserStatusEnum.DISABLE.value()){ + log.setStatus(LoginStatusEnum.LOCK.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + sysLogLoginService.save(log); + + throw new SysException(ErrorCode.ACCOUNT_DISABLE); + } + + //租户停用 + SysTenantDTO tenantDTO = sysTenantService.getTenantCode(user.getTenantCode()); + if(tenantDTO == null || tenantDTO.getStatus() == 0){ + throw new SysException(ErrorCode.ACCOUNT_DISABLE); + } + + //登录成功 + log.setStatus(LoginStatusEnum.SUCCESS.value()); + log.setCreator(user.getId()); + log.setCreatorName(user.getUsername()); + sysLogLoginService.save(log); + +// //登录成功后,重置登录时间,并且清零登录错误次数 +// updateLoginInfo(user.getId(), "0", new Date()); + + return sysUserTokenService.createToken(user.getId()); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/customrealm/Oauth2Realm.java b/modules/thing/src/main/java/com/thing/sys/security/customrealm/Oauth2Realm.java new file mode 100644 index 0000000..c5cf141 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/customrealm/Oauth2Realm.java @@ -0,0 +1,129 @@ +package com.thing.sys.security.customrealm; + +import cn.hutool.core.util.ObjectUtil; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.SuperTenantEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.MessageUtils; +import com.thing.sys.biz.dto.SysDeptDTO; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; +import com.thing.sys.biz.service.SysDeptService; +import com.thing.sys.security.domain.SysAuthInfo; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.security.service.ShiroService; +import com.thing.sys.tenant.service.SysTenantGroupService; +import jakarta.annotation.Resource; +import org.apache.shiro.authc.*; +import org.apache.shiro.authz.AuthorizationInfo; +import org.apache.shiro.authz.SimpleAuthorizationInfo; +import org.apache.shiro.subject.PrincipalCollection; +import org.springframework.stereotype.Component; +import com.thing.oauth2.AbstractOauth2Realm; +import com.thing.oauth2.Oauth2Token; + +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 认证 + * + * @author Mark sunlightcs@gmail.com + */ +@Component +public class Oauth2Realm extends AbstractOauth2Realm { + + @Resource + private ShiroService shiroService; + + @Resource + private SysDeptService sysDeptService; + + @Resource + private SysTenantGroupService tenantGroupService; + + @Override + public boolean supports(AuthenticationToken token) { + return token instanceof Oauth2Token; + } + + /** + * 授权(验证权限时调用) + */ + @Override + protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { + UserDetail user = (UserDetail)principals.getPrimaryPrincipal(); + + //用户权限列表 + Set permsSet = shiroService.getUserPermissions(user); + + SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); + info.setStringPermissions(permsSet); + return info; + } + + /** + * 认证(登录时调用) + */ + @Override + protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { + String accessToken = (String) token.getPrincipal(); + + //根据accessToken,查询用户信息 + SysUserTokenEntity tokenEntity = shiroService.getByToken(accessToken); + //token失效 + if(tokenEntity == null || tokenEntity.getExpireDate().getTime() < System.currentTimeMillis()){ + throw new IncorrectCredentialsException(MessageUtils.getMessage(ErrorCode.TOKEN_INVALID)); + } + + //查询用户信息 + SysUserEntity userEntity = shiroService.getUser(tokenEntity.getUserId()); + if(Objects.isNull(userEntity)) { + throw new AuthenticationException(MessageUtils.getMessage(ErrorCode.ACCOUNT_NOT_EXIST)); + } + //转换成UserDetail对象 + UserDetail userDetail = ConvertUtils.sourceToTarget(userEntity, UserDetail.class); + + //获取用户对应的部门数据权限 + List deptIdList = shiroService.getDataScopeList(userDetail.getId()); + userDetail.setDeptIdList(deptIdList); + + //当为租户组管理时 拿组配置的 tenantList 否则 拿 user表里面配置的 + if(userDetail.getTenantGroup().equals(1)) { + List tenantCodeList = tenantGroupService.getChildren(userDetail.getTenantCode()); + userDetail.setTenantCodeList(tenantCodeList); + }else { + if(userDetail.getTenantList()!=null){ + String[] tenantArray = userDetail.getTenantList().split(","); + List tenantList = Arrays.stream(tenantArray) + .map(Long::parseLong) + .collect(Collectors.toList()); + userDetail.setTenantCodeList(tenantList); + } + } + + //增加用户登录态中公司顶级节点的维护 + SysDeptDTO sysDeptDTO = sysDeptService.getDeptByPid(Constant.DEPT_ROOT, userDetail.getTenantCode()); + if(ObjectUtil.isNull(userDetail.getCompanyId())){ + userDetail.setCompanyId(sysDeptDTO.getId()); + userDetail.setCompanyName(sysDeptDTO.getName()); + } + if(ObjectUtil.isNull(userDetail.getDeptId()) + && ObjectUtil.equal(SuperTenantEnum.YES.value(), userDetail.getSuperTenant())){ + userDetail.setDeptId(sysDeptDTO.getId()); + } + + //账号锁定 + if(userDetail.getStatus() == 0){ + throw new LockedAccountException(MessageUtils.getMessage(ErrorCode.ACCOUNT_LOCK)); + } + + return new SysAuthInfo(userDetail, accessToken); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/security/customrealm/ShiroCustomConfig.java b/modules/thing/src/main/java/com/thing/sys/security/customrealm/ShiroCustomConfig.java new file mode 100644 index 0000000..84460f8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/customrealm/ShiroCustomConfig.java @@ -0,0 +1,36 @@ +package com.thing.sys.security.customrealm; + +import com.thing.cache.SecurityCacheManager; +import com.thing.config.ShiroConfig; +import com.thing.oauth2.AbstractOauth2Realm; +import org.apache.shiro.session.mgt.SessionManager; +import org.apache.shiro.web.mgt.DefaultWebSecurityManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({ShiroConfig.class}) +public class ShiroCustomConfig { + + + @Bean("securityManager") + public DefaultWebSecurityManager securityManager(AbstractOauth2Realm oAuth2Realm, SessionManager sessionManager) { + DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); + securityManager.setRealm(oAuth2Realm); + securityManager.setSessionManager(sessionManager); + securityManager.setRememberMeManager(null); + + // 认证授权缓存 + oAuth2Realm.setCacheManager(new SecurityCacheManager()); + oAuth2Realm.setCachingEnabled(true); + oAuth2Realm.setAuthorizationCachingEnabled(true); // 授权 + oAuth2Realm.setAuthenticationCachingEnabled(true);// 认证 + oAuth2Realm.setAuthenticationCacheName("authentication-cache"); + oAuth2Realm.setAuthorizationCacheName("authorization-cache"); + + return securityManager; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/domain/LoginDTO.java b/modules/thing/src/main/java/com/thing/sys/security/domain/LoginDTO.java new file mode 100644 index 0000000..11bb312 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/domain/LoginDTO.java @@ -0,0 +1,36 @@ +package com.thing.sys.security.domain; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serializable; + +/** + * 登录表单 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "登录表单") +public class LoginDTO implements Serializable { + private static final long serialVersionUID = 1L; + + @Schema(description = "用户名", required = true) + @NotBlank(message="{sysuser.username.require}") + private String username; + + @Schema(description = "密码") + @NotBlank(message="{sysuser.password.require}") + private String password; + + @Schema(description = "验证码") + @NotBlank(message="{sysuser.captcha.require}") + private String captcha; + + @Schema(description = "唯一标识") + @NotBlank(message="{sysuser.uuid.require}") + private String uuid; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/security/domain/SecurityUser.java b/modules/thing/src/main/java/com/thing/sys/security/domain/SecurityUser.java new file mode 100644 index 0000000..8e741ae --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/domain/SecurityUser.java @@ -0,0 +1,60 @@ +package com.thing.sys.security.domain; + +import org.apache.shiro.SecurityUtils; +import org.apache.shiro.subject.Subject; + +/** + * 用户 + * + * @author Mark sunlightcs@gmail.com + */ +public class SecurityUser { + + public static Subject getSubject() { + try { + return SecurityUtils.getSubject(); + }catch (Exception e){ + return null; + } + } + + /** + * 获取用户信息 + */ + public static UserDetail getUser() { + Subject subject = getSubject(); + if(subject == null){ + return new UserDetail(); + } + try { + UserDetail user = (UserDetail)subject.getPrincipal(); + if(user == null){ + return new UserDetail(); + } + return user; + } catch (Exception e) { + return new UserDetail(); + } + } + + /** + * 获取用户ID + */ + public static Long getUserId() { + return getUser().getId(); + } + + /** + * 获取部门ID + */ + public static Long getDeptId() { + return getUser().getDeptId(); + } + + /** + * 获取租户编码 + */ + public static Long getTenantCode() { + return getUser().getTenantCode(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthInfo.java b/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthInfo.java new file mode 100644 index 0000000..801a517 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthInfo.java @@ -0,0 +1,30 @@ +package com.thing.sys.security.domain; + +import lombok.Data; + +import lombok.NoArgsConstructor; +import org.apache.shiro.authc.AuthenticationInfo; +import org.apache.shiro.subject.PrincipalCollection; + +import java.io.Serial; + +/** + * @author siyang + * @date 2024-04-08 + * @description 系统认证信息 + */ +@Data +@NoArgsConstructor +public class SysAuthInfo implements AuthenticationInfo { + + @Serial private static final long serialVersionUID = 2405044073826863626L; + + private PrincipalCollection principals; + + private Object credentials; + + public SysAuthInfo(Object principal, Object credentials) { + this.principals = new SysAuthPrincipal(principal); + this.credentials = credentials; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthPrincipal.java b/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthPrincipal.java new file mode 100644 index 0000000..1cfb156 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/domain/SysAuthPrincipal.java @@ -0,0 +1,77 @@ +package com.thing.sys.security.domain; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import org.apache.shiro.subject.PrincipalCollection; +import org.jetbrains.annotations.NotNull; + +import java.io.Serial; +import java.util.*; + +/** + * @author siyang + * @date 2024-04-08 + * @description 极简系统认证principal + */ +@Data +@NoArgsConstructor +public class SysAuthPrincipal implements PrincipalCollection { + + @Serial private static final long serialVersionUID = 6544237441211252154L; + + private Object primaryPrincipal; + + public SysAuthPrincipal(Object principal) { + this.primaryPrincipal = principal; + } + + @Override + public Object getPrimaryPrincipal() { + return primaryPrincipal; + } + + @Override + public T oneByType(Class type) { + if (type.isAssignableFrom(primaryPrincipal.getClass())) { + return (T) primaryPrincipal; + } + return null; + } + + @Override + public Collection byType(Class type) { + return List.of(oneByType(type)); + } + + @Override + public List asList() { + return Collections.singletonList(primaryPrincipal); + } + + @Override + public Set asSet() { + return Set.of(primaryPrincipal); + } + + @Override + public Collection fromRealm(String realmName) { + return asList(); + } + + @Override + public Set getRealmNames() { + return null; + } + + @Override + public boolean isEmpty() { + return false; + } + + @NotNull + @Override + public Iterator iterator() { + return asList().iterator(); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/domain/UserDetail.java b/modules/thing/src/main/java/com/thing/sys/security/domain/UserDetail.java new file mode 100644 index 0000000..43e84e4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/domain/UserDetail.java @@ -0,0 +1,56 @@ +package com.thing.sys.security.domain; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 登录用户信息 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +public class UserDetail implements Serializable { + @Serial private static final long serialVersionUID = 1L; + + private Long id; + private String username; + private String realName; + private String headUrl; + private Integer gender; + private String email; + private String mobile; + private Long deptId; + private String password; + private Integer status; + private Integer superAdmin; + private Integer superTenant; + private Integer tenantGroup; + private String url; + + /** 租户编码 */ + private Long tenantCode; + + /** 企业顶级节点Id */ + private Long companyId; + + /** 企业顶级节点名称 */ + private String companyName; + + /** 部门数据权限 */ + private List deptIdList; + + /** 子租户列表,superAdmin = 0 且 tenantGroup = 1 时存在 */ + private List tenantCodeList; + /** + * 扩展字段,pid=0时的deptId公司id + */ + private Long consumerPid; + + /** + * 企业列表 + */ + private String tenantList; +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/service/ShiroService.java b/modules/thing/src/main/java/com/thing/sys/security/service/ShiroService.java new file mode 100644 index 0000000..75b7dec --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/service/ShiroService.java @@ -0,0 +1,36 @@ +package com.thing.sys.security.service; + + +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; +import com.thing.sys.security.domain.UserDetail; + +import java.util.List; +import java.util.Set; + +/** + * shiro相关接口 + * + * @author Mark sunlightcs@gmail.com + */ +public interface ShiroService { + /** + * 获取用户权限列表 + */ + Set getUserPermissions(UserDetail user); + + SysUserTokenEntity getByToken(String token); + + /** + * 根据用户ID,查询用户 + * @param userId + */ + SysUserEntity getUser(Long userId); + + /** + * 获取用户对应的部门数据权限 + * @param userId 用户ID + * @return 返回部门ID列表 + */ + List getDataScopeList(Long userId); +} diff --git a/modules/thing/src/main/java/com/thing/sys/security/service/impl/ShiroServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/security/service/impl/ShiroServiceImpl.java new file mode 100644 index 0000000..2addcd1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/security/service/impl/ShiroServiceImpl.java @@ -0,0 +1,65 @@ +package com.thing.sys.security.service.impl; + +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.entity.SysUserTokenEntity; +import com.thing.sys.biz.mapper.SysMenuMapper; +import com.thing.sys.biz.mapper.SysRoleDataScopeMapper; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.biz.mapper.SysUserTokenMapper; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.security.service.ShiroService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Service +@RequiredArgsConstructor +public class ShiroServiceImpl implements ShiroService { + private final SysMenuMapper sysMenuMapper; + private final SysUserMapper sysUserMapper; + private final SysUserTokenMapper sysUserTokenMapper; + private final SysRoleDataScopeMapper sysRoleDataScopeMapper; + + @Override + public Set getUserPermissions(UserDetail user) { + //系统管理员,拥有最高权限 + List permissionsList; + if (user.getSuperAdmin() == SuperAdminEnum.YES.value()) { + permissionsList = sysMenuMapper.getPermissionsList(); + } else { + permissionsList = sysMenuMapper.getUserPermissionsList(user.getId()); + } + + //用户权限列表 + Set permsSet = new HashSet<>(); + for (String permissions : permissionsList) { + if (StringUtils.isBlank(permissions)) { + continue; + } + permsSet.addAll(Arrays.asList(permissions.trim().split(","))); + } + + return permsSet; + } + + @Override + public SysUserTokenEntity getByToken(String token) { + return sysUserTokenMapper.getByToken(token); + } + + @Override + public SysUserEntity getUser(Long userId) { + return sysUserMapper.selectOneById(userId); + } + + @Override + public List getDataScopeList(Long userId) { + return sysRoleDataScopeMapper.getDataScopeList(userId); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantController.java b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantController.java new file mode 100644 index 0000000..cf28aa9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantController.java @@ -0,0 +1,245 @@ +package com.thing.sys.tenant.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.CtlUserDTO; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.mapper.SysRoleMapper; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.biz.service.SysUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupTreeDTO; +import com.thing.sys.tenant.dto.UpdateStatusDTO; +import com.thing.sys.tenant.service.SysTenantService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/tenant") +@Tag(name="租户管理") +public class SysTenantController { + @Autowired + private SysTenantService sysTenantService; + @Autowired + private SysRoleUserService sysRoleUserService; + @Autowired + private SysRoleMapper sysRoleMapper; + @Autowired + private SysUserService sysUserService; + + @GetMapping("page") + @Operation(summary="企业分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "tenantCode",description ="租户编码"), + @Parameter(name = "tenantName",description ="租户名"), + @Parameter(name = "tenantGroup",description ="租户组"), + @Parameter(name = "sysTenant",description ="是否查询系统租户 0:查询 其他值不查询") + }) + //@RequiresPermissions("sys:tenant:page") + public Result> page( @RequestParam Map params){ + PageData page = sysTenantService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("page/comp") + @Operation(summary="企业账号分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "tenantName",description ="租户名"), + @Parameter(name = "tenantType",description ="企业类型") + }) + public Result> compPage( @RequestParam Map params){ + PageData page = sysTenantService.groupPage(params); + + return new Result>().ok(page); + } + + @GetMapping("list/none") + @Operation(summary="无账号企业列表") + @Parameter(name = "id",description ="租户主键") + public Result> noneList(@RequestParam(required = false) Long id) { + List list = sysTenantService.noneList(id); + + return new Result>().ok(list); + } + + @Operation(summary="所有企业列表") + @GetMapping("list") + public Result> list(){ + List list = sysTenantService.listDTO(); + + return new Result>().ok(list); + } + + @GetMapping("page/group/tree") + @Operation(summary="企业分组分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantName",description ="租户名"), + @Parameter(name = "status",description ="状态") + }) + public Result> groupTreePage( @RequestParam Map params) { + PageData page = sysTenantService.groupTreePage(params); + + return new Result>().ok(page); + } + + @Operation(summary="排除当前企业后所有企业列表") + @GetMapping("excludePage") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantCode",description ="租户编码", required = true), + @Parameter(name = "tenantName",description ="租户名称") + }) + public Result> excludeList( @RequestParam Map params){ + PageData page = sysTenantService.excludePage(params); + return new Result>().ok(page); + } + + @Operation(summary="租户企业列表") + @GetMapping("tenantList") + public Result> tenantList(){ + List page = sysTenantService.tenantList(); + return new Result>().ok(page); + } + + @GetMapping("validate/user") + @Operation(summary="校验登录名是否存在") + @Parameters({ + @Parameter(name = "id",description ="当前记录主键"), + @Parameter(name = "username",description ="登录名") + }) + public Result validate( @RequestParam Map params) { + + return new Result().ok(sysTenantService.validateUser(params)); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + + + SysTenantDTO data = sysTenantService.get(id); + //用户角色列表 + List roleIdList = sysRoleUserService.getRoleIdLists(data.getUserId(),tenantCode); + List entityList = CollectionUtil.isEmpty(roleIdList) ? Lists.newArrayList() : sysRoleMapper.selectListByQuery(QueryWrapper.create().in(SysRoleEntity::getId, roleIdList)); + if (CollectionUtil.isNotEmpty(entityList)) { + List tenantRoleIdList = entityList.stream().map(SysRoleEntity::getId).collect(Collectors.toList()); + data.setRoleIdList(tenantRoleIdList); + } + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody SysTenantDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysTenantService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody SysTenantDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysTenantService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysTenantService.getMapper().deleteBatchByIds(Arrays.asList(ids)); + + return new Result(); + } + + @PostMapping("status") + @Operation(summary="更新状态") + @LogOperation("更新状态") + public Result updateStatus(@RequestBody UpdateStatusDTO updateStatusDTO){ + //效验数据 + ValidatorUtils.validateEntity(updateStatusDTO, DefaultGroup.class); + + sysTenantService.updateStatus(updateStatusDTO); + + return new Result(); + } + + + @Operation(summary="分配物企业下拉列表") + @GetMapping("list/select") + public Result> selectList(){ + List list = sysTenantService.selectList(); + + return new Result>().ok(list); + } + + @Operation(summary="用户列表") + @GetMapping("user") + public Result> ctlList(){ + ////企业用户 + //List list = sysTenantService.ctlList(); + //系统用户 + List list = sysUserService.ctlList(Maps.newHashMap()); + return new Result>().ok(list); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantDetailController.java b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantDetailController.java new file mode 100644 index 0000000..de5262c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantDetailController.java @@ -0,0 +1,328 @@ +package com.thing.sys.tenant.controller; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.MimeTypeEnum; +import com.thing.common.core.enumeration.TenantSaveType; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.oss.cloud.AbstractCloudStorageService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import com.thing.sys.tenant.dto.SysTenantUserDTO; +import com.thing.sys.tenant.dto.TenantDetailForm; +import com.thing.sys.tenant.excel.SysTenantDetailExcel; +import com.thing.sys.tenant.service.SysTenantDetailService; +import com.thing.thing.group.service.IotGroupInfoService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@RestController +@RequestMapping("sys/tenant/detail") +@Tag(name = "企业管理") +@Slf4j +@RequiredArgsConstructor +public class SysTenantDetailController { + + private final SysTenantDetailService sysTenantDetailService; + private final IotGroupInfoService iotGroupService; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "tenantName", description = "企业名称"), + @Parameter(name = "corporationCode", description = "统一社会信用编码"), + @Parameter(name = "tenantType", description = "企业类型(使用字典tenant_type)"), + @Parameter(name = "industryType", description = "行业类型(使用字典industry_type)") + }) + public Result> page(@RequestParam Map params) { + PageData page = sysTenantDetailService.page(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameters({ + @Parameter(name = "tenantType", description = "企业类型(租户:1,企业:0,不传查所有)"), + @Parameter(name = "regionCode", description = "区域") + }) + public Result> selectList(@RequestParam Map params) { + List page = sysTenantDetailService.selectList(params); + + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + SysTenantDetailDTO data = sysTenantDetailService.getDetail(id); + + return new Result().ok(data); + } + + @GetMapping("detail") + @Operation(summary = "企业详情-当前企业详情") + public Result detail() { + SysTenantDetailDTO data = sysTenantDetailService.getDetail(); + + return new Result().ok(data); + } + + @GetMapping("validate") + @Operation(summary = "校验") + @Parameters({ + @Parameter(name = "id", description = "当前记录主键"), + @Parameter(name = "code", description = "统一社会信用编码"), + @Parameter(name = "contactNumber", description = "联系电话"), + @Parameter(name = "email", description = "邮箱"), + + }) + public Result validate(@RequestParam Map params) { + String codeRegex = "^([159Y]{1})([1239]{1})([0-9ABCDEFGHJKLMNPQRTUWXY]{6})([0-9ABCDEFGHJKLMNPQRTUWXY]{9})([0-90-9ABCDEFGHJKLMNPQRTUWXY])$"; + String contactNumberRegex = "^((13[0-9])|(15[^4,\\D])|(14[57])|(17[0])|(17[7])|(18[0,0-9]))\\d{8}$"; + String emailRegex = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + + String code = (String) params.get("code"); + String contactNumber = (String) params.get("contactNumber"); + String email = (String) params.get("email"); + if (StringUtils.isNotBlank(code) && !Pattern.matches(codeRegex, code)) { + return new Result().error("请输入正确社会信用代码"); + } + if (StringUtils.isNotBlank(contactNumber)) { + if (!Pattern.matches(contactNumberRegex, contactNumber)) { + return new Result().error("请输入正确的电话号码"); + } + } + if (StringUtils.isNotBlank(email)) { + if (!Pattern.matches(emailRegex, email)) { + return new Result().error("请输入正确的邮箱"); + } + } + return new Result().ok(sysTenantDetailService.validate(params)); + } + + @PostMapping("base") + @Operation(summary = "基础信息保存") + @LogOperation("基础信息保存") + public Result saveDetail(@RequestBody TenantDetailForm detailForm) { + //效验企业详情数据 + ValidatorUtils.validateEntity(detailForm.getTenantDetail(), AddGroup.class, DefaultGroup.class); + sysTenantDetailService.saveDetail(detailForm, CollectionUtil.newArrayList(TenantSaveType.base)); + + return new Result(); + } + + @PostMapping("user") + @Operation(summary = "基础信息+账号保存") + @LogOperation("基础信息+账号保存") + public Result saveUser(@RequestBody TenantDetailForm detailForm) { + //效验企业详情数据 + ValidatorUtils.validateEntity(detailForm.getTenantDetail(), AddGroup.class, DefaultGroup.class); + //创建用户 + ValidatorUtils.validateEntity(detailForm.getTenantUser(), AddGroup.class, DefaultGroup.class); + sysTenantDetailService.saveDetail(detailForm, + CollectionUtil.newArrayList(TenantSaveType.base, TenantSaveType.user)); + + return new Result(); + } + + @PostMapping("menu") + @Operation(summary = "基础信息+账号+菜单保存") + @LogOperation("基础信息+账号+菜单保存") + public Result saveMenu(@RequestBody TenantDetailForm detailForm) { + //效验企业详情数据 + ValidatorUtils.validateEntity(detailForm.getTenantDetail(), AddGroup.class, DefaultGroup.class); + //创建用户 + ValidatorUtils.validateEntity(detailForm.getTenantUser(), AddGroup.class, DefaultGroup.class); + //配置菜单 + ValidatorUtils.validateEntity(detailForm.getTenantRole(), AddGroup.class, DefaultGroup.class); + sysTenantDetailService.saveDetail(detailForm, + CollectionUtil.newArrayList(TenantSaveType.base, TenantSaveType.user, TenantSaveType.menu)); + + return new Result(); + } + + @PostMapping("thing") + @Operation(summary = "基础信息+账号+菜单+物保存") + @LogOperation("基础信息+账号+菜单+物保存") + public Result saveThing(@RequestBody TenantDetailForm detailForm) { + //效验企业详情数据 + ValidatorUtils.validateEntity(detailForm.getTenantDetail(), AddGroup.class, DefaultGroup.class); + //创建用户 + ValidatorUtils.validateEntity(detailForm.getTenantUser(), AddGroup.class, DefaultGroup.class); + //配置菜单 + ValidatorUtils.validateEntity(detailForm.getTenantRole(), AddGroup.class, DefaultGroup.class); + sysTenantDetailService.saveDetail(detailForm, + CollectionUtil.newArrayList(TenantSaveType.base, TenantSaveType.user, + TenantSaveType.menu, TenantSaveType.thing)); + + return new Result(); + } + + @PostMapping("all") + @Operation(summary = "基础信息+账号+菜单+物+企业保存") + @LogOperation("基础信息+账号+菜单+物+企业保存") + public Result saveAll(@RequestBody TenantDetailForm detailForm) { + //效验企业详情数据 + ValidatorUtils.validateEntity(detailForm.getTenantDetail(), AddGroup.class, DefaultGroup.class); + //创建用户 + ValidatorUtils.validateEntity(detailForm.getTenantUser(), AddGroup.class, DefaultGroup.class); + //配置菜单 + ValidatorUtils.validateEntity(detailForm.getTenantRole(), AddGroup.class, DefaultGroup.class); + sysTenantDetailService.saveDetail(detailForm, + CollectionUtil.newArrayList(TenantSaveType.base, TenantSaveType.user, + TenantSaveType.menu, TenantSaveType.thing, TenantSaveType.comp)); + + return new Result(); + } + + @PutMapping + @Operation(summary = "修改基础数据") + @LogOperation("修改基础数据") + public Result update(@RequestBody @Validated SysTenantDetailDTO detailDTO) { + //效验企业详情数据 + String codeRegex = "^([159Y]{1})([1239]{1})([0-9ABCDEFGHJKLMNPQRTUWXY]{6})([0-9ABCDEFGHJKLMNPQRTUWXY]{9})([0-90-9ABCDEFGHJKLMNPQRTUWXY])$"; + String contactNumberRegex = "^((13[0-9])|(15[^4,\\D])|(14[57])|(17[0])|(17[7])|(18[0,0-9]))\\d{8}$"; + String emailRegex = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + + String code = detailDTO.getCorporationCode(); + String contactNumber = detailDTO.getContactNumber(); + String email = detailDTO.getEmail(); + if (StringUtils.isNotBlank(code) && !Pattern.matches(codeRegex, code)) { + return new Result().error("请输入正确社会信用代码"); + } + if (StringUtils.isNotBlank(contactNumber)) { + if (!Pattern.matches(contactNumberRegex, contactNumber)) { + return new Result().error("请输入正确的电话号码"); + } + } + if (StringUtils.isNotBlank(email)) { + if (!Pattern.matches(emailRegex, email)) { + return new Result().error("请输入正确的邮箱"); + } + } + sysTenantDetailService.updateDetail(detailDTO); +// int integer = sysTenantMapper.selectOrgById(detailDTO.getId()); +// if(integer == 0){ +// // 新增租户保存公共机构 +// sysTenantMapper.insertList(detailDTO.getId(),detailDTO.getName(),detailDTO.getId(),detailDTO.getId(),null,System.currentTimeMillis()); +// } + return new Result(); + } + + + @PostMapping("comp") + @Operation(summary = "租户账号保存") + @LogOperation("租户账号保存") + public Result compSave(@RequestBody SysTenantUserDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysTenantDetailService.compSave(dto); + + return new Result(); + } + + @PutMapping("comp") + @Operation(summary = "租户账号修改") + @LogOperation("租户账号修改") + public Result compUpdate(@RequestBody SysTenantUserDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysTenantDetailService.compUpdate(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysTenantDetailService.removeByIds(Arrays.asList(ids)); + + return new Result(); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + List excelList = sysTenantDetailService.getExportList(Lists.newArrayList(ids), params); + ExcelUtils.exportExcel(excelList, null, "企业详情", SysTenantDetailExcel.class, "企业详情.xls", response); + } + + @PostMapping("/upload") + @Operation(summary = "图片上传") + public Result uploadPicture(@RequestParam(value = "file") MultipartFile multipartFile) throws IOException { + AbstractCloudStorageService storageService = OSSFactory.build(); + if (storageService == null) { + return new Result().error("获取云存储配置信息失败"); + } + if (!FilenameUtils.isExtension(StringUtils.lowerCase(multipartFile.getOriginalFilename()), MimeTypeEnum.getImageMimeType())) { + return new Result().error("请上传图片文件"); + } + String url = storageService.uploadSuffix(multipartFile.getInputStream(), FilenameUtils.getExtension(multipartFile.getOriginalFilename())); + return new Result().ok(OSSFactory.splice(url)); + } + + @PostMapping("/upload/doc") + @Operation(summary = "图片和文档上传") + public Result uploadDoc(@RequestParam(value = "file") MultipartFile multipartFile) throws IOException { + AbstractCloudStorageService storageService = OSSFactory.build(); + if (storageService == null) { + return new Result().error("获取云存储配置信息失败"); + } + if (!FilenameUtils.isExtension(StringUtils.lowerCase(multipartFile.getOriginalFilename()), MimeTypeEnum.getAllMimeType())) { + return new Result().error("不支持的文件上传类型"); + } + String url = storageService.uploadSuffix(multipartFile.getInputStream(), FilenameUtils.getExtension(multipartFile.getOriginalFilename())); + return new Result().ok(OSSFactory.splice(url)); + } + + + @GetMapping("sync/{id}") + @Operation(summary = "同步基础数据") + public Result syncBaseData(@PathVariable Long id) { + iotGroupService.transferGroupInfo(id); + return new Result(); + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantGroupController.java b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantGroupController.java new file mode 100644 index 0000000..94504bc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantGroupController.java @@ -0,0 +1,65 @@ +package com.thing.sys.tenant.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.web.response.Result; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupForm; +import com.thing.sys.tenant.service.SysTenantGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2021-12-27 + */ +@RestController +@RequestMapping("sys/tenant/group") +@Tag(name = "租户组关系表") +public class SysTenantGroupController { + @Autowired + private SysTenantGroupService sysTenantGroupService; + + + @PostMapping + @Operation(summary="保存") + public Result save(@RequestBody SysTenantGroupForm form){ + //效验数据 + ValidatorUtils.validateEntity(form, DefaultGroup.class); + + sysTenantGroupService.save(form); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysTenantGroupService.deleteBatchCodes(Arrays.asList(ids)); + + return new Result(); + } + + @Operation(summary="已选企业列表") + @GetMapping("{tenantCode}/children") + public Result> groupChildren(@PathVariable Long tenantCode){ + List list = sysTenantGroupService.groupChildren(tenantCode); + + return new Result>().ok(list); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantRoleController.java b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantRoleController.java new file mode 100644 index 0000000..f0790b3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/controller/SysTenantRoleController.java @@ -0,0 +1,140 @@ +package com.thing.sys.tenant.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.tenant.service.SysTenantRoleService; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; +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 Mark sunlightcs@gmail.com + */ +@RestController +@RequestMapping("/sys/tenant/role") +@Tag(name="租户角色管理") +public class SysTenantRoleController { + @Autowired + private SysTenantRoleService sysTenantRoleService; + @Autowired + private SysRoleMenuService sysRoleMenuService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "name",description ="角色名") + }) + //@RequiresPermissions("tenant:role:page") + public Result> page( @RequestParam Map params){ + PageData page = sysTenantRoleService.page(params); + + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="列表") + //@RequiresPermissions("tenant:role:list") + public Result> list(){ + List data = sysTenantRoleService.list(new HashMap<>(1)); + + return new Result>().ok(data); + } + + @GetMapping("page/comp") + @Operation(summary="企业角色分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") , + @Parameter(name = "name",description ="角色名") + }) + public Result> compPage( @RequestParam Map params){ + PageData page = sysTenantRoleService.compPage(params); + + return new Result>().ok(page); + } + + @GetMapping("list/comp") + @Operation(summary="企业角色列表") + public Result> compList(){ + List data = sysTenantRoleService.compList(new HashMap<>(1)); + + return new Result>().ok(data); + } + + @GetMapping("{id}") + @Operation(summary="信息") + //@RequiresPermissions("tenant:role:info") + public Result get(@PathVariable("id") Long id){ + SysRoleDTO data = sysTenantRoleService.get(id); + + //查询角色对应的菜单 + List menuIdList = sysRoleMenuService.getMenuIdList(id); + data.setMenuIdList(menuIdList); + + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + //@RequiresPermissions("tenant:role:save") + public Result save(@RequestBody SysRoleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + + sysTenantRoleService.save(dto); + + return new Result(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + //@RequiresPermissions("tenant:role:update") + public Result update(@RequestBody SysRoleDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + + sysTenantRoleService.update(dto); + + return new Result(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + //@RequiresPermissions("tenant:role:delete") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + + sysTenantRoleService.delete(ids); + + return new Result(); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/RegionIndustryTenantInfoDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/RegionIndustryTenantInfoDTO.java new file mode 100644 index 0000000..54b7fe2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/RegionIndustryTenantInfoDTO.java @@ -0,0 +1,30 @@ +package com.thing.sys.tenant.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +/** + * @author xiezw + * @date 2023-08-09 + **/ +@Data +public class RegionIndustryTenantInfoDTO { + + @Schema(description = "企业名称") + private String tenantName ; + @Schema(description = "租户编码") + private Long tenantCode ; + @Schema(description = "物编码") + private String thingCode ; + + @Schema(description = "行业大类") + private String industryCategory; + + @Schema(description = "行业门类") + private String industryType; + + @Schema(description = "区域code") + private Long regionCode; +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDTO.java new file mode 100644 index 0000000..26a7ef7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDTO.java @@ -0,0 +1,117 @@ +package com.thing.sys.tenant.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Schema( name= "租户管理") +public class SysTenantDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "租户名称") + @NotBlank(message = "{tenant.tenantName.require}", groups = DefaultGroup.class) + private String tenantName; + + @Schema(description = "登录账号") + @NotBlank(message = "{tenant.username.require}", groups = DefaultGroup.class) + private String username; + + //@JsonIgnore + @Schema(description = "登录账号主键") + private Long userId; + + @Schema(description = "登录密码") + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + @NotBlank(message = "{tenant.password.require}", groups = AddGroup.class) + private String password; + + @Schema(description = "姓名", required = true) + @NotBlank(message = "{sysuser.realname.require}", groups = DefaultGroup.class) + private String realName; + + @Schema(description = "邮箱", required = true) + //@NotBlank(message="{sysuser.email.require}", groups = DefaultGroup.class) + //@Email(message="{sysuser.email.error}", groups = DefaultGroup.class) + private String email; + + @Schema(description = "手机号", required = true) + //@NotBlank(message="{sysuser.mobile.require}", groups = DefaultGroup.class) + private String mobile; + + @Schema(description = "角色ID列表") + private List roleIdList; + + @Schema(description = "角色名") + private String roleName; + + @Schema(description = "是否租户") + private Integer tenantType; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "企业详情id(仅查询使用)") + private Long companyId; + + @Schema(description = "状态 0:停用 1:正常", required = true) + @Range(min = 0, max = 1, message = "{tenant.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_PATTERN_STR) + private Date createDate; + + @Schema(description = "物编码") + private String thingCode; + + @Schema(description = "行业类型") + private String industryType; + + @Schema(description = "地区") + private String regionCode; + + @Schema(description = "经纬度") + private String lngLat; + + @Schema(description = "详细地址") + private String address; + + @Schema(description = "企业数量") + private Integer enterpriseQuantity; + + @Schema(description = "企业用户数量") + private Integer enterpriseUsers; + + @Schema(description = "企业物数量") + private Integer enterpriseThings; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailDTO.java new file mode 100644 index 0000000..7756b50 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailDTO.java @@ -0,0 +1,74 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serial; +import java.io.Serializable; + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@Data +@Schema( name= "企业详情") +public class SysTenantDetailDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema( description= "主键") + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema( description= "企业名称") + @NotBlank(message = "企业名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema( description= "统一社会信用识别码") + @Pattern(regexp = "^([159Y]{1})([1239]{1})([0-9ABCDEFGHJKLMNPQRTUWXY]{6})([0-9ABCDEFGHJKLMNPQRTUWXY]{9})([0-90-9ABCDEFGHJKLMNPQRTUWXY])$", message = "请输入正确社会信用代码", groups = UpdateGroup.class) + private String corporationCode; + @Schema( description= "行业门类编码") + private String industryCategory; + @Schema( description= "行业门类名称") + private String industryCategoryName; + @Schema( description= "行业大类编码") + private String industryType; + @Schema( description= "行业大类名称") + private String industryTypeName; + @Schema( description= "地区") + @NotBlank(message = "地区不能为空", groups = DefaultGroup.class) + private String regionCode; + @Schema( description= "地址") + private String address; + @Schema( description= "联系人") + private String contact; + @Schema( description= "联系电话") + @Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[6|7|8])|(18[0-9])|166|198|199)\\d{8}$", message = "请输入正确的电话号码", groups = UpdateGroup.class) + private String contactNumber; + @Schema( description= "邮箱") + @Email(message = "请输入正确的邮箱", groups = UpdateGroup.class) + private String email; + @Schema( description= "经纬度") + private String lngLat; + @Schema( description= "logo") + private String logoUrl; + @Schema( description= "企业图片") + private String imageUrl; + @Schema( description= "企业描述") + private String remark; + @Schema( description= "企业类型") + @NotNull(message = "企业类型不能为空", groups = DefaultGroup.class) + private Integer tenantType; + @Schema( description= "物code") + private String thingCode; + @Schema( description= "企业英文名称") + private String englishName; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailMenuDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailMenuDTO.java new file mode 100644 index 0000000..798c0df --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantDetailMenuDTO.java @@ -0,0 +1,89 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@Data +@Schema( name= "企业详情") +public class SysTenantDetailMenuDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema( description= "主键") + @NotNull(message = "{id.require}", groups = UpdateGroup.class) + private Long id; + @Schema( description= "企业名称") + @NotBlank(message = "企业名称不能为空", groups = DefaultGroup.class) + private String name; + @Schema( description= "统一社会信用识别码") + @Pattern(regexp = "^([159Y]{1})([1239]{1})([0-9ABCDEFGHJKLMNPQRTUWXY]{6})([0-9ABCDEFGHJKLMNPQRTUWXY]{9})([0-90-9ABCDEFGHJKLMNPQRTUWXY])$", message = "请输入正确社会信用代码", groups = UpdateGroup.class) + private String corporationCode; + @Schema( description= "行业门类编码") + private String industryCategory; + @Schema( description= "行业门类名称") + private String industryCategoryName; + @Schema( description= "行业大类编码") + private String industryType; + @Schema( description= "行业大类名称") + private String industryTypeName; + @Schema( description= "地区") + @NotBlank(message = "地区不能为空", groups = DefaultGroup.class) + private String regionCode; + @Schema( description= "地址") + private String address; + @Schema( description= "联系人") + private String contact; + @Schema( description= "联系电话") + @Pattern(regexp = "^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(17[6|7|8])|(18[0-9])|166|198|199)\\d{8}$", message = "请输入正确的电话号码", groups = UpdateGroup.class) + private String contactNumber; + @Schema( description= "邮箱") + @Email(message = "请输入正确的邮箱", groups = UpdateGroup.class) + private String email; + @Schema( description= "经纬度") + private String lngLat; + @Schema( description= "logo") + private String logoUrl; + @Schema( description= "企业图片") + private String imageUrl; + @Schema( description= "企业描述") + private String remark; + @Schema( description= "企业类型") + @NotNull(message = "企业类型不能为空", groups = DefaultGroup.class) + private Integer tenantType; + @Schema( description= "物code") + private String thingCode; + @Schema( description= "企业英文名称") + private String englishName; + + @Schema(description = "id") + private Long userMenuId; + @Schema(description = "用户id") + private Long userId; + @Schema(description = "菜单id") + private Long menuId; + @Schema(description = "菜单URL") + private String url; + @Schema(description = "平台名称") + private String logoName; + @Schema(description = "菜单URL") + private String logo; + @Schema(description = "是否缩进展示logo") + private Boolean showLogo; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupDTO.java new file mode 100644 index 0000000..3155aa4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupDTO.java @@ -0,0 +1,30 @@ +package com.thing.sys.tenant.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2021-12-27 + */ +@Data +@Schema( name= "租户组关系表") +public class SysTenantGroupDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + private Long id; + @Schema(description = "父租户code") + private Long parentCode; + @Schema(description = "子租户code") + private Long code; + @Schema(description = "父级可修改节点code") + private Long parentModifyCode; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupForm.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupForm.java new file mode 100644 index 0000000..d73b25a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupForm.java @@ -0,0 +1,29 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-03-23 + **/ +@Data +@Schema( name= "选择租户") +public class SysTenantGroupForm { + + + @Schema(description = "父租户编码") + @NotNull(message="父租户编码不能为空", groups = DefaultGroup.class) + private Long tenantCode; + + @Schema(description = "子租户编码") + @NotNull(message="子租户编码不能为空", groups = DefaultGroup.class) + private List children; + + @Schema(description = "是否是企业分组中的入参") + private Boolean group = true; +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupTreeDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupTreeDTO.java new file mode 100644 index 0000000..9f08b68 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantGroupTreeDTO.java @@ -0,0 +1,41 @@ +package com.thing.sys.tenant.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.params.TreeNode; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2022-3-23 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema( name= "租户组关系表") +public class SysTenantGroupTreeDTO extends TreeNode implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "子租户code") + private Long id; + @Schema(description = "父租户code") + private Long pid; + @Schema(description = "名称") + private String tenantName; + @Schema(description = "状态") + private Integer status; + @Schema(description = "创建时间") + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + @JsonFormat(pattern = DateTimeUtils.DATE_PATTERN_STR) + private Date createDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantUserDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantUserDTO.java new file mode 100644 index 0000000..c434ae9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/SysTenantUserDTO.java @@ -0,0 +1,58 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotNull; + +/** + * @author zhenghh. 2022-03-22 + **/ +@Data +@Schema( name= "设置账户") +public class SysTenantUserDTO { + + @Schema(description = "租户表主键") + @NotNull(message = "租户表主键不能为空", groups = DefaultGroup.class) + private Long tenantId; + + @Schema(description = "登录账号") + @NotBlank(message = "登录账号不能为空", groups = DefaultGroup.class) + private String username; + + @Schema(description = "密码") + @NotBlank(message = "密码不能为空", groups = AddGroup.class) + private String password; + + @Schema(description = "确认密码") + @NotBlank(message = "确认密码不能为空", groups = AddGroup.class) + private String confirmPassword; + + @Schema(description = "邮箱") + private String email; + + @Schema(description = "手机号") + private String mobile; + + @Schema(description = "角色ID") + private Long roleId; + + @Schema(description = "是否租户(不可修改)") + private Integer tenantType; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "状态 0:停用 1:正常") + @Range(min = 0, max = 1, message = "{tenant.status.range}", groups = DefaultGroup.class) + private Integer status; + + @Schema(description = "默认用户id") + private Long userId; + +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/TenantDetailForm.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/TenantDetailForm.java new file mode 100644 index 0000000..9c5cc26 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/TenantDetailForm.java @@ -0,0 +1,85 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-01-13 + **/ +@Data +@Schema( description= "企业管理表单提交") +public class TenantDetailForm { + + @Schema( description= "基础信息") + private SysTenantDetailDTO tenantDetail; + + @Schema( description= "账号设置") + private TenantUser tenantUser; + + @Schema( description= "菜单信息") + private TenantRole tenantRole; + + @Schema( description= "分配物列表") + private List thingList; + + @Schema( description= "分配企业列表") + private List tenantCodeList; + + @Data + @Schema( description="设置账号") + public static class TenantUser { + + @Schema( description= "登录账号") + @NotBlank(message = "登录账号不能为空", groups = DefaultGroup.class) + private String username; + + @Schema( description= "密码") + @NotBlank(message = "密码不能为空", groups = DefaultGroup.class) + private String password; + + @Schema( description= "确认密码") + @NotBlank(message = "确认密码不能为空", groups = DefaultGroup.class) + private String confirmPassword; + + @Schema( description= "手机号") + private String phone; + + @Schema( description= "邮箱") + private String email; + } + + @Data + @Schema( description="菜单配置") + public static class TenantRole { + + @Schema( description= "是否为默认角色") + @NotNull(message = "默认角色不能为空", groups = DefaultGroup.class) + private Boolean defaultRole; + + @Schema( description= "默认角色主键(当为默认角色时,不为空)") + //@NotNull(message="默认角色主键不能为空", groups = UpdateGroup.class) + private Long roleId; + + @Schema( description= "菜单主键集合(当不为默认角色时,不为空)") + //@NotNull(message="菜单主键集合不能为空", groups = UpdateGroup.class) + private List menuIdList; + } + + @Data + @Schema( description="分配物") + public static class TenantThing { + + @Schema( description= "物编码") + private String code; + @Schema( description= "物名称") + private String name; + @Schema( description= "启用停用,1启用,0停用") + private String status; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/dto/UpdateStatusDTO.java b/modules/thing/src/main/java/com/thing/sys/tenant/dto/UpdateStatusDTO.java new file mode 100644 index 0000000..ec43635 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/dto/UpdateStatusDTO.java @@ -0,0 +1,26 @@ +package com.thing.sys.tenant.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import org.hibernate.validator.constraints.Range; + +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author zhenghh. 2022-01-13 + **/ +@Data +@Schema( name= "修改状态") +public class UpdateStatusDTO { + + @Schema(description = "主键集合", required = true) + @NotNull(message = "主键集合不能为空", groups = DefaultGroup.class) + private List ids; + + @Schema(description = "状态 0:停用 1:正常", required = true) + @Range(min=0, max=1, message = "{tenant.status.range}", groups = DefaultGroup.class) + private Integer status; +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantDetailEntity.java b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantDetailEntity.java new file mode 100644 index 0000000..262dfdf --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantDetailEntity.java @@ -0,0 +1,92 @@ +package com.thing.sys.tenant.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("sys_tenant_detail") +public class SysTenantDetailEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 企业名称 + */ + private String name; + /** + * 统一社会信用识别码 + */ + private String corporationCode; + /** + * 行业门类编码(SysIndustryType.parentCode=0) + */ + private String industryCategory; + /** + * 行业大类编码(SysIndustryType.parentCode=industryCategory) + */ + private String industryType; + /** + * 地区 + */ + private String regionCode; + /** + * 地址 + */ + private String address; + /** + * 联系人 + */ + private String contact; + /** + * 联系电话 + */ + private String contactNumber; + /** + * 邮箱 + */ + private String email; + /** + * 经纬度 + */ + private String lngLat; + /** + * logo + */ + private String logoUrl; + /** + * 企业图片 + */ + private String imageUrl; + /** + * 企业描述 + */ + private String remark; + /** + * 企业类型 + */ + @Column(ignore = true) + private Integer tenantType; + /** + * 物code + */ + @Column(ignore = true) + private String thingCode; + /** + * 企業英文名 + */ + private String englishName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantEntity.java b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantEntity.java new file mode 100644 index 0000000..e978afa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantEntity.java @@ -0,0 +1,99 @@ +package com.thing.sys.tenant.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("sys_tenant") +public class SysTenantEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 租户编码 + */ + private Long tenantCode; + /** + * 租户名称 + */ + private String tenantName; + /** + * 状态 0:停用 1:正常 + */ + private Integer status; + /** + * 备注 + */ + private String remark; + /** + * 登录账号ID + */ + private Long userId; + /** + * 登录账号 + */ + private String username; + /** + * 删除标识 0:未删除 1:删除 + */ + private Integer delFlag; + /** + * 系统租户 0:否 1:是 + */ + private Integer sysTenant; + /** + * 企业类型 + */ + private Integer tenantType; + /** + * 物code + */ + private String thingCode; + + /** + * 邮箱 + */ + + @Column(ignore = true) + private String email; + /** + * 手机号 + */ + @Column(ignore = true) + private String mobile; + /** + * 真实姓名 + */ + @Column(ignore = true) + private String realName; + /** + * 企业详情ID + */ + @Column(ignore = true) + private Long companyId; + /** + * 企业数量 + */ + private Integer enterpriseQuantity; + /** + * 企业用户数量 + */ + private Integer enterpriseUsers; + /** + * 企业物数量 + */ + private Integer enterpriseThings; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantGroupEntity.java b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantGroupEntity.java new file mode 100644 index 0000000..32147a5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/entity/SysTenantGroupEntity.java @@ -0,0 +1,41 @@ +package com.thing.sys.tenant.entity; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseEntity; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2021-12-27 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@AllArgsConstructor +@NoArgsConstructor +@Table("sys_tenant_group") +public class SysTenantGroupEntity extends BaseEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 父租户code + */ + private Long parentCode; + /** + * 子租户code + */ + private Long code; + + /** + * 父级可修改节点code + */ + private Long parentModifyCode; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/excel/SysTenantDetailExcel.java b/modules/thing/src/main/java/com/thing/sys/tenant/excel/SysTenantDetailExcel.java new file mode 100644 index 0000000..ed5feb8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/excel/SysTenantDetailExcel.java @@ -0,0 +1,43 @@ +package com.thing.sys.tenant.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import cn.afterturn.easypoi.excel.annotation.ExcelIgnore; +import lombok.Data; + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@Data +public class SysTenantDetailExcel { + @ExcelIgnore + private Integer tenantType; + @Excel(name = "企业类型") + private String tenantTypeName; + @Excel(name = "企业名称") + private String name; + @Excel(name = "统一社会信用识别码") + private String corporationCode; + @ExcelIgnore + private String industryType; + @Excel(name = "行业类型") + private String industryTypeName; + @ExcelIgnore + private String regionCode; + @Excel(name = "地区") + private String regionCodeName; + @Excel(name = "地址") + private String address; + @Excel(name = "联系人") + private String contact; + @Excel(name = "联系电话") + private String contactNumber; + @Excel(name = "邮箱") + private String email; + @Excel(name = "经纬度") + private String lngLat; + @Excel(name = "企业描述") + private String remark; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantDetailMapper.java b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantDetailMapper.java new file mode 100644 index 0000000..fbe6224 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantDetailMapper.java @@ -0,0 +1,20 @@ +package com.thing.sys.tenant.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 企业详情 +* +* @author zhenghh zhenghh +* @since 3.0 2022-01-13 +*/ +@Mapper +public interface SysTenantDetailMapper extends PowerBaseMapper { + + List getList(Map params); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantGroupMapper.java b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantGroupMapper.java new file mode 100644 index 0000000..b066c3d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantGroupMapper.java @@ -0,0 +1,16 @@ +package com.thing.sys.tenant.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 租户组关系表 +* +* @author zhenghh +* @since 3.0 2021-12-27 +*/ +@Mapper +public interface SysTenantGroupMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantMapper.java b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantMapper.java new file mode 100644 index 0000000..f3adf31 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/mapper/SysTenantMapper.java @@ -0,0 +1,58 @@ +package com.thing.sys.tenant.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.sys.tenant.dto.RegionIndustryTenantInfoDTO; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupTreeDTO; +import com.thing.sys.tenant.entity.SysTenantEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Mapper +public interface SysTenantMapper extends PowerBaseMapper { + + List getList(Map params); + + SysTenantEntity getById(Long id); + + SysTenantEntity getTenantCode(Long tenantCode); + + void deleteBatch(Long[] ids); + + /** + * 获取子租户列表 + * @param parentCodeList 父编码集合 + * @return list + */ + List getChildren(List parentCodeList); + + /** + * 分配物企业下拉列表 + * @param params 参数 + * @return list + */ + List getSelectList(Map params); + + List queryList(Map params); + + + void insertList(@Param("id") Long id, @Param("orgName") String orgName, @Param("deptId") Long deptId, @Param("tenantCode") Long tenantCode, @Param("updater") Long updater, @Param("updateDate") Long updateDate); + + void updateStatusByIds(@Param("ids") List ids, @Param("status") Integer status); + + int selectOrgById(@Param("id") Long id); + + List getThingCodesByTenantCode(@Param("tenantCode") Long tenantCode); + + List getByGroupTenantCode(@Param("tenantCode")Long tenantCode); + + List getTenantListByParams(Map params); +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantDetailService.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantDetailService.java new file mode 100644 index 0000000..c683abb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantDetailService.java @@ -0,0 +1,102 @@ +package com.thing.sys.tenant.service; + + +import com.thing.common.core.enumeration.TenantSaveType; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import com.thing.sys.tenant.dto.SysTenantDetailMenuDTO; +import com.thing.sys.tenant.dto.SysTenantUserDTO; +import com.thing.sys.tenant.dto.TenantDetailForm; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.excel.SysTenantDetailExcel; + +import java.util.List; +import java.util.Map; + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +public interface SysTenantDetailService extends IBaseService { + + /** + * 分页 + * + * @param params 查询条件 + * @return page + */ + PageData page(Map params); + + /** + * 列表 + * + * @param params 查询条件 + * @return list + */ + List selectList(Map params); + + /** + * 获取导出数据 + * + * @param params 查询条件 + * @return list + */ + List getExportList(List idList, Map params); + + /** + * 修改 + * + * @param dto 企业详情 + */ + void updateDetail(SysTenantDetailDTO dto); + + /** + * 数据校验 + * + * @param params 参数 + * @return 是否存在 + */ + Boolean validate(Map params); + + /** + * 详情 + * + * @param id 主键 + * @return dto + */ + SysTenantDetailDTO getDetail(Long id); + +// SysTenantDetailMenuDTO getDetail(Long id); + + /** + * 当前用户企业详情 + * + * @return dto + */ + SysTenantDetailDTO getDetail(); + + /** + * 保存 + * + * @param tenantDetail 基础信息 + * @param typeList 保存类型集合 + */ + void saveDetail(TenantDetailForm tenantDetail, List typeList); + + /** + * 租户账号保存 + * + * @param dto dto + */ + void compSave(SysTenantUserDTO dto); + + /** + * 租户账号修改 + * + * @param dto dto + */ + void compUpdate(SysTenantUserDTO dto); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantGroupService.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantGroupService.java new file mode 100644 index 0000000..640b219 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantGroupService.java @@ -0,0 +1,65 @@ +package com.thing.sys.tenant.service; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupForm; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; + +import java.util.List; +import java.util.Map; + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2021-12-27 + */ +public interface SysTenantGroupService extends IBaseService { + + /** + * 父租户code获取子列表 + * @param parentCode 父租户code + * @return 子租户列表 + */ + List getChildren(Long parentCode); + + /** + * 删除 + * @param parentCode 父租户code + */ + void deleteByParentCode(Long parentCode); + + /** + * 已选企业列表 + * @param tenantCode 租户编码 + * @return list + */ + List groupChildren(Long tenantCode); + + /** + * 保存 + * @param form 表单 + */ + void save(SysTenantGroupForm form); + + /** + * 删除 + * @param tenantCodes + */ + void deleteBatchCodes(List tenantCodes); + + /** + * 不为超管时,查询自己下属 + * @param params 参数 + * @param includeTenant 是否包含租户 + */ + void paramsAddTenantCodeList(Map params, Boolean includeTenant); + + /** + * 不为超管时,查询自己下属 + * @param wrapper 参数 + * @param includeTenant 是否包含租户 + */ + void wrapperAddTenantCodeList(QueryWrapper wrapper, Boolean includeTenant); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantRoleService.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantRoleService.java new file mode 100644 index 0000000..df64c90 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantRoleService.java @@ -0,0 +1,44 @@ +package com.thing.sys.tenant.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysRoleEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 租户角色 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysTenantRoleService extends IBaseService { + + PageData page(Map params); + + List list(Map params); + + SysRoleDTO get(Long id); + + void save(SysRoleDTO dto); + + void update(SysRoleDTO dto); + + void delete(Long[] ids); + + /** + * 企业角色 + * @param params 查询条件 + * @return page + */ + PageData compPage(Map params); + + /** + * 企业角色 + * @param params 查询条件 + * @return list + */ + List compList(Map params); +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantService.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantService.java new file mode 100644 index 0000000..792fc24 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/SysTenantService.java @@ -0,0 +1,114 @@ +package com.thing.sys.tenant.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.CtlUserDTO; +import com.thing.sys.tenant.dto.RegionIndustryTenantInfoDTO; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupTreeDTO; +import com.thing.sys.tenant.dto.UpdateStatusDTO; +import com.thing.sys.tenant.entity.SysTenantEntity; + +import java.util.List; +import java.util.Map; + + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +public interface SysTenantService extends IBaseService { + + PageData page(Map params); + + SysTenantDTO get(Long id); + + void save(SysTenantDTO dto); + + void update(SysTenantDTO dto); + + void delete(Long[] ids); + + SysTenantDTO getTenantCode(Long tenantCode); + + /** + * 租户列表 + * @return list + */ + List listDTO(); + + /** + * 状态更新 + * @param updateStatusDTO 参数 + */ + void updateStatus(UpdateStatusDTO updateStatusDTO); + + /** + * 租户、企业账号分页 + * @param params 查询条件 + * @return page + */ + PageData groupPage(Map params); + + /** + * 租户分组分页 + * @param params 参数 + * @return page + */ + PageData groupTreePage(Map params); + + /** + * 无账号企业列表 + * @param id 租户主键 + * @return list + */ + List noneList(Long id); + + /** + * 登录名是否存在 + * @param params 参数 + * @return 是否存在 + */ + Boolean validateUser(Map params); + + /** + * 排除当前企业后所有企业列表 + * @param params 参数 + * @return page + */ + PageData excludePage(Map params); + + /** + * 租户企业列表 + * @return list + */ + List tenantList(); + + /** + * 分配物企业下拉列表 + * @return list + */ + List selectList(); + + /** + * 租户编码获取列表 + * @param tenantCodeList 租户编码 + * @return list + */ + List selectListByTenantCodeList(List tenantCodeList); + + /** + * 看板控制下拉选择 + * @return list + */ + List ctlList(); + + List queryList(Map params); + + List getThingCodesByTenantCode(Long tenantCode); + + List getByGroupTenantCode(Long tenantCode); + + List getTenantListByParams(Map params); +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantDetailServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantDetailServiceImpl.java new file mode 100644 index 0000000..eb176b0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantDetailServiceImpl.java @@ -0,0 +1,715 @@ +package com.thing.sys.tenant.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.orm.utils.IdGenerator; +import com.thing.event.TenantDetailSavedEvent; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.*; +import com.thing.sys.biz.mapper.*; +import com.thing.sys.biz.service.SysDictTypeService; +import com.thing.sys.biz.service.SysIndustryTypeService; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysRoleService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.password.PasswordUtils; +import com.thing.sys.tenant.dto.SysTenantDetailDTO; +import com.thing.sys.tenant.dto.SysTenantGroupForm; +import com.thing.sys.tenant.dto.SysTenantUserDTO; +import com.thing.sys.tenant.dto.TenantDetailForm; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.entity.SysTenantEntity; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; +import com.thing.sys.tenant.excel.SysTenantDetailExcel; +import com.thing.sys.tenant.mapper.SysTenantDetailMapper; +import com.thing.sys.tenant.mapper.SysTenantGroupMapper; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantDetailService; +import com.thing.sys.tenant.service.SysTenantGroupService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.entity.entity.IotThingEntity; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.util.*; +import java.util.stream.Collectors; + +import static com.thing.sys.tenant.entity.table.SysTenantDetailEntityTableDef.SYS_TENANT_DETAIL_ENTITY; +import static com.thing.sys.tenant.entity.table.SysTenantEntityTableDef.SYS_TENANT_ENTITY; + + +/** + * 企业详情 + * + * @author zhenghh zhenghh + * @since 3.0 2022-01-13 + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysTenantDetailServiceImpl extends BaseServiceImpl implements SysTenantDetailService { + + private final SysTenantMapper tenantDao; + private final SysUserMapper sysUserMapper; + private final SysDeptMapper sysDeptMapper; + private final SysRoleMapper sysRoleMapper; + private final SysRoleUserMapper sysRoleUserMapper; + private final SysRoleMenuService sysRoleMenuService; + private final SysTenantGroupService sysTenantGroupService; + private final SysDictTypeService dictTypeService; + private final SysRegionMapper regionDao; + private final MsgUserService msgUserService; + private final SysRoleService sysRoleService; + private final SysTenantGroupMapper sysTenantGroupMapper; + private final SysIndustryTypeService sysIndustryTypeService; + + private final ThingManageContextService thingManageContextService; + private final ApplicationEventPublisher applicationEventPublisher; + + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + String regionCode = (String) params.get("regionCode"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("name", name, StringUtils.isNotBlank(name)); + wrapper.eq("region_code", regionCode, StringUtils.isNotBlank(regionCode)); + return wrapper; + } + + @Override + public PageData page(Map params) { + //转换成like + params.put("tenantType", StringUtils.isNotBlank((String) params.get("tenantType")) + ? Integer.valueOf((String) params.get("tenantType")) : null); + Page page = getPage(params, SysTenantDetailDTO.class); + //不为超管时,查询自己下属 + sysTenantGroupService.paramsAddTenantCodeList(params, true); + PageData pageData = selectPage(page, params); + List list = pageData.getList(); + Set industryCodes = list.stream().map(SysTenantDetailDTO::getIndustryCategory).collect(Collectors.toSet()); + industryCodes.addAll(list.stream().map(SysTenantDetailDTO::getIndustryType).collect(Collectors.toSet())); + List industryTypeEntities = sysIndustryTypeService.getByCodes(industryCodes); + Map industryTypeMap = + industryTypeEntities.stream() + .collect( + Collectors.toMap( + SysIndustryTypeEntity::getCode, + SysIndustryTypeEntity::getName)); + + list.forEach(item -> { + item.setImageUrl(OSSFactory.splice(item.getImageUrl())); + item.setLogoUrl(OSSFactory.splice(item.getLogoUrl())); + item.setIndustryTypeName(industryTypeMap.get(item.getIndustryType())); + item.setIndustryCategoryName(industryTypeMap.get(item.getIndustryCategory())); + }); + + return pageData; + } + + private PageData selectPage(Page page, Map params){ + String name = MapUtil.getStr(params, "name"); + String corporationCode = MapUtil.getStr(params, "corporationCode"); + String industryCategory = MapUtil.getStr(params, "industryCategory"); + String industryType = MapUtil.getStr(params, "industryType"); + String regionCode = MapUtil.getStr(params, "regionCode"); + Integer tenantType = MapUtil.getInt(params, "tenantType"); + String tenantName = MapUtil.getStr(params, "tenantName"); + List tenantCodeList = (List)params.get("tenantCodeList"); + + QueryWrapper queryWrapper = QueryWrapper.create() + .select( + SYS_TENANT_ENTITY.TENANT_TYPE, + SYS_TENANT_ENTITY.THING_CODE, + SYS_TENANT_DETAIL_ENTITY.ALL_COLUMNS) + .from(SYS_TENANT_ENTITY) + .innerJoin(SYS_TENANT_DETAIL_ENTITY) + .on(SYS_TENANT_ENTITY.TENANT_CODE.eq(SYS_TENANT_DETAIL_ENTITY.ID)) + .like(SysTenantDetailEntity::getName, name, StringUtils.isNotEmpty(name)) + .eq(SysTenantDetailEntity::getCorporationCode, corporationCode, StringUtils.isNotEmpty(corporationCode)) + .eq(SysTenantDetailEntity::getIndustryCategory, industryCategory, StringUtils.isNotEmpty(industryCategory)) + .eq(SysTenantDetailEntity::getIndustryType, industryType, StringUtils.isNotEmpty(industryType)) + .eq(SysTenantDetailEntity::getRegionCode, regionCode, StringUtils.isNotEmpty(regionCode)) + .like(SysTenantEntity::getTenantType, tenantType, Objects.nonNull(tenantType)) + .like(SysTenantEntity::getTenantName, tenantName, StringUtils.isNotEmpty(tenantName)) + .in(SysTenantEntity::getTenantCode, tenantCodeList, !CollectionUtils.isEmpty(tenantCodeList)); + + Page paginate = mapper.paginateAs(page, queryWrapper, SysTenantDetailDTO.class); + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + /** + * 列表 + * + * @param params 查询条件 + * @return list + */ + @Override + public List selectList(Map params) { + params.put("tenantType", StringUtils.isNotBlank((String) params.get("tenantType")) + ? Integer.valueOf((String) params.get("tenantType")) : null); + return ConvertUtils.sourceToTarget(mapper.getList(params), SysTenantDetailDTO.class); + } + + /** + * 获取导出数据 + * + * @param params 查询条件 + * @return list + */ + @Override + public List getExportList(List idList, Map params) { + //转换成like + paramsToLike(params, "tenantName", "corporationCode"); + //查询 + params.put("tenantType", StringUtils.isNotBlank((String) params.get("tenantType")) + ? Integer.valueOf((String) params.get("tenantType")) : null); + //不为超管时,查询自己下属 + sysTenantGroupService.paramsAddTenantCodeList(params, true); + List list = mapper.getList(params); + if (CollectionUtil.isEmpty(list)) { + return new ArrayList<>(); + } + if (CollectionUtil.isNotEmpty(idList)) { + list = list.stream().filter(item -> idList.contains(item.getId())).collect(Collectors.toList()); + } + //字典 + List tenantTypeList = dictTypeService.getDictDataByType("tenant_type"); + List industryTypeList = dictTypeService.getDictDataByType("industry_type"); + List regionCodeList = list.parallelStream().filter(item -> StringUtils.isNotBlank(item.getRegionCode())) + .map(item -> Long.parseLong(item.getRegionCode())).distinct().collect(Collectors.toList()); + List regionList = CollectionUtil.isNotEmpty(regionCodeList) ? regionDao.selectListByIds(regionCodeList) : new ArrayList<>(); + return list.stream().map(item -> { + SysTenantDetailExcel excel = ConvertUtils.sourceToTarget(item, SysTenantDetailExcel.class); + excel.setTenantTypeName(tenantTypeList.parallelStream() + .filter(type -> StringUtils.equals(type.getDictValue(), String.valueOf(excel.getTenantType()))) + .findFirst().map(DictData::getDictLabel).orElse("--")); + excel.setIndustryTypeName(industryTypeList.parallelStream() + .filter(type -> StringUtils.equals(type.getDictValue(), String.valueOf(excel.getIndustryType()))) + .findFirst().map(DictData::getDictLabel).orElse("--")); + excel.setRegionCodeName(regionList.parallelStream() + .filter(region -> StringUtils.equals(String.valueOf(region.getId()), excel.getRegionCode())) + .findFirst().map(SysRegionEntity::getName).orElse("--")); + return excel; + }).collect(Collectors.toList()); + } + + /** + * 修改 + * + * @param dto 企业详情 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void updateDetail(SysTenantDetailDTO dto) { + SysTenantDetailEntity tenantDetail = ConvertUtils.sourceToTarget(dto, SysTenantDetailEntity.class); + //租户信息 + SysTenantEntity sysTenantEntity = tenantDao.selectOneByQuery(new QueryWrapper() + .eq(SysTenantEntity::getTenantCode, tenantDetail.getId())); + sysTenantEntity.setTenantName(tenantDetail.getName()); + sysTenantEntity.setRemark(tenantDetail.getRemark()); + tenantDao.update(sysTenantEntity); + //租户详情 + SysTenantDetailEntity detailEntity = mapper.selectOneById(dto.getId()); + tenantDetail.setImageUrl(OSSFactory.cutOut(tenantDetail.getImageUrl())); + tenantDetail.setLogoUrl(OSSFactory.cutOut(tenantDetail.getLogoUrl())); + if (detailEntity == null) { + mapper.insertSelective(tenantDetail); + } else { + mapper.update(tenantDetail); + } + } + + /** + * 数据校验 + * + * @param params 参数 + * @return 是否存在 + */ + @Override + public Boolean validate(Map params) { + String code = (String) params.get("code"); + String id = (String) params.get("id"); + if (StringUtils.isNotBlank(code)) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("corporation_code", code); + wrapper.ne("id", StringUtils.isNotBlank(id) ? Long.parseLong(id) : -1L); + return mapper.selectCountByQuery(wrapper) > 0; + } + return false; + } + + /** + * 详情 + * + * @param id 主键 + * @return dto + */ + @Override + public SysTenantDetailDTO getDetail(Long id) { + SysTenantDetailDTO dto = ConvertUtils.sourceToTarget(mapper.selectOneById(id), SysTenantDetailDTO.class); + if (dto == null) { + dto = new SysTenantDetailDTO(); + dto.setId(id); + } + dto.setImageUrl(OSSFactory.splice(dto.getImageUrl())); + dto.setLogoUrl(OSSFactory.splice(dto.getLogoUrl())); + SysTenantEntity sysTenantEntity = tenantDao.selectOneByQuery(new QueryWrapper().eq(SysTenantEntity::getTenantCode, id)); + if (sysTenantEntity != null) { + dto.setTenantType(sysTenantEntity.getTenantType()); + dto.setThingCode(sysTenantEntity.getThingCode()); + } + List industryCodes = new ArrayList<>(2); + industryCodes.add(dto.getIndustryCategory()); + industryCodes.add(dto.getIndustryType()); + List industryTypeEntities = sysIndustryTypeService.getByCodes(industryCodes); + Map industryTypeMap = + industryTypeEntities.stream() + .collect( + Collectors.toMap( + SysIndustryTypeEntity::getCode, + SysIndustryTypeEntity::getName)); + dto.setIndustryTypeName(industryTypeMap.get(dto.getIndustryType())); + dto.setIndustryCategoryName(industryTypeMap.get(dto.getIndustryCategory())); + + return dto; + } + + /** + * 当前用户企业详情 + * + * @return dto + */ + @Override + public SysTenantDetailDTO getDetail() { + Long tenantCode = TenantContext.getTenantCode(SecurityUser.getUser()); + return getDetail(tenantCode); + } + + /** + * 保存基础信息 + * + * @param detailForm 基础信息 + * @param typeList 保存步骤 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void saveDetail(TenantDetailForm detailForm, List typeList) { + + long id = IdGenerator.nextId(); + SysTenantDetailEntity tenantDetail = ConvertUtils.sourceToTarget(detailForm.getTenantDetail(), SysTenantDetailEntity.class); + tenantDetail.setId(id); + //企业物编码 统一信用编码存在使用编码不存在使用id + String thingCode = "CO_".concat(StringUtils.isNotBlank(tenantDetail.getCorporationCode()) ? tenantDetail.getCorporationCode() : String.valueOf(id)); + //部门信息 部门主键与租户详情主键与tenantCode相同 + SysDeptEntity deptEntity = setDeptEntity(tenantDetail.getName(), id); + sysDeptMapper.insertSelective(deptEntity); + + //2023-01-09创建企业新增默认角色 + SysRoleEntity defaultRoleEntity = setDefaultRoleEntity(tenantDetail.getName(), id); + sysRoleMapper.insertSelective(defaultRoleEntity); + + //账号信息 + SysUserEntity sysUserEntity = null; + if (typeList.contains(TenantSaveType.user)) { + sysUserEntity = setUserEntity(tenantDetail, detailForm.getTenantUser(), id); + sysUserMapper.insertSelective(sysUserEntity); + MsgUserDTO msgUserDTO = new MsgUserDTO(); + msgUserDTO.setUserId(sysUserEntity.getId()); + msgUserDTO.setEmailUsername(sysUserEntity.getEmail()); + msgUserDTO.setDingdingPhone(sysUserEntity.getMobile()); + msgUserDTO.setWxPhone(sysUserEntity.getMobile()); + msgUserService.saveDto(msgUserDTO); + + //绑定默认角色到账号中 + SysRoleUserEntity defaultRoleUserEntity = new SysRoleUserEntity(); + defaultRoleUserEntity.setUserId(sysUserEntity.getId()); + defaultRoleUserEntity.setRoleId(defaultRoleEntity.getId()); + sysRoleUserMapper.insertSelective(defaultRoleUserEntity); + } + //菜单权限信息 + if (typeList.contains(TenantSaveType.menu)) { + if (sysUserEntity == null) { + throw new SysException("账号信息未找到"); + } + TenantDetailForm.TenantRole tenantRole = detailForm.getTenantRole(); + //默认权限直接插入角色用户 + //非默认权限 先创建角色 角色菜单 再插入角色用户 + if (!tenantRole.getDefaultRole()) { + List menuIdList = tenantRole.getMenuIdList(); + if (CollectionUtil.isEmpty(menuIdList)) { + throw new SysException("请选择菜单"); + } + //角色 + SysRoleEntity roleEntity = setRoleEntity(tenantDetail.getName()); + sysRoleMapper.insertSelective(roleEntity); + //角色菜单 + sysRoleMenuService.saveBatch(menuIdList.parallelStream().map(menuId -> { + SysRoleMenuEntity entity = new SysRoleMenuEntity(); + entity.setRoleId(roleEntity.getId()); + entity.setMenuId(menuId); + return entity; + }).collect(Collectors.toList())); + tenantRole.setRoleId(roleEntity.getId()); + } + if (tenantRole.getRoleId() == null) { + throw new SysException("请选择企业角色"); + } + SysRoleUserEntity roleUserEntity = new SysRoleUserEntity(); + roleUserEntity.setUserId(sysUserEntity.getId()); + roleUserEntity.setRoleId(tenantRole.getRoleId()); + sysRoleUserMapper.insertSelective(roleUserEntity); + } + //分配物 + List thingTenantList = new ArrayList<>(); + if (typeList.contains(TenantSaveType.thing)) { + List thingList = detailForm.getThingList(); + if (CollectionUtil.isNotEmpty(thingList)) { + List codes = thingList.stream().map(TenantDetailForm.TenantThing::getCode).filter(StringUtils::isNotBlank).toList(); + thingTenantList = thingList.parallelStream() + .map(item -> { + IotThingEntity iotThingEntity = new IotThingEntity() + .setCode(item.getCode()) + .setName(item.getCode()) + .setRealType("1") + .setEnableStatus("1") + .setTemplateMark(TemplateMark.NO.getValue()) + .setType("默认物类型"); + iotThingEntity.setId(id); + return iotThingEntity; + } + ).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(codes)){ + //修改分配企业数 + thingManageContextService.updateModelAuthNumByCodes(codes); + + } + } + } + try { + // 保存企业物至物管理表 + thingManageContextService.saveModel(Collections.singletonList(thingCode)); + } catch (Exception e) { + e.printStackTrace(); + throw new SysException("保存失败,请检查数据源配置"); + } + //保存分配物及企业物至物实体表 + thingTenantList.add( + new IotThingEntity() + .setCode(thingCode) + .setName(StringUtils.isBlank(tenantDetail.getName()) ? thingCode : tenantDetail.getName()) + .setRealType("0") + .setEnableStatus("1") + .setTemplateMark(TemplateMark.NO.getValue()) + .setType("企业物") + ); + thingManageContextService.saveEntity(thingTenantList); + //分配企业 + if (typeList.contains(TenantSaveType.comp)) { + List tenantCodeList = detailForm.getTenantCodeList(); + if (CollectionUtil.isNotEmpty(tenantCodeList)) { + //获取这些子节点的绑定情况 + List existNode = sysTenantGroupMapper.selectListByQuery(QueryWrapper.create().in(SysTenantGroupEntity::getCode, tenantCodeList)); + Map> existMap = CollectionUtil.isNotEmpty(existNode) ? existNode.stream().collect(Collectors.groupingBy(SysTenantGroupEntity::getCode)) : Maps.newHashMap(); + sysTenantGroupService.saveBatch(tenantCodeList.parallelStream() + .map(code -> { + Long parentModifyCode = null; + if (!existMap.containsKey(code) || CollectionUtil.isEmpty(existMap.get(code).stream().map(SysTenantGroupEntity::getParentModifyCode).filter(Objects::nonNull).collect(Collectors.toList()))) { + parentModifyCode = tenantDetail.getId(); + } + return new SysTenantGroupEntity(id, code, parentModifyCode); + }).collect(Collectors.toList())); + } + } + //租户信息 + tenantDao.insert(setTenantEntity(tenantDetail, sysUserEntity, thingCode)); + //租户详情 + tenantDetail.setImageUrl(OSSFactory.cutOut(tenantDetail.getImageUrl())); + tenantDetail.setLogoUrl(OSSFactory.cutOut(tenantDetail.getLogoUrl())); + save(tenantDetail); + + // 租户创建成功后,push租户存储完成事件 + applicationEventPublisher.publishEvent(new TenantDetailSavedEvent(this, tenantDetail)); + + +// 新增租户保存公共机构 +// sysTenantMapper.insertList(deptEntity.getId(), tenantDetail.getName(), deptEntity.getId(), tenantDetail.getId(), tenantDetail.getUpdater(), tenantDetail.getUpdateDate()); + + //租户创建企业自动分组 + UserDetail userDetail = SecurityUser.getUser(); + if (((!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(TenantContext.getTenantCode(userDetail), userDetail.getTenantCode()))) + && StringUtils.equals(String.valueOf(tenantDetail.getTenantType()), CompanyType.COMPANY.getValue())) { + //将企业分配到该租户下 + SysTenantGroupForm form = new SysTenantGroupForm(); + form.setTenantCode(TenantContext.getTenantCode(userDetail)); + form.setChildren(Lists.newArrayList(tenantDetail.getId())); + form.setGroup(false); + sysTenantGroupService.save(form); + } + } + + /** + * 角色保存 + * + * @param tenantName 租户名称 + * @return 角色实体 + */ + private SysRoleEntity setRoleEntity(String tenantName) { + Long finalRoleId = new SnowFlakeIDKeyGenerator().nextId(); + //角色 + SysRoleEntity roleEntity = new SysRoleEntity(); + roleEntity.setId(finalRoleId); + roleEntity.setName("企业角色_" + tenantName); + //sa切换租户 租户的tenantCode、companyId + //sa tenantCode companyId + UserDetail userDetail = SecurityUser.getUser(); + roleEntity.setDeptId(TenantContext.getTenantCode(userDetail)); + roleEntity.setTenantCode(TenantContext.getTenantCode(userDetail)); + roleEntity.setCompanyId(TenantContext.getCompanyId(userDetail)); + roleEntity.setDefaultRole(false); + roleEntity.setType(RoleType.COMPANY.getValue()); + return roleEntity; + } + + /** + * 默认角色保存 + * + * @param tenantName 租户名称 + * @param tenantCode 租户编码 + * @return 角色实体 + */ + private SysRoleEntity setDefaultRoleEntity(String tenantName, Long tenantCode) { + Long finalRoleId = new SnowFlakeIDKeyGenerator().nextId(); + //角色 + SysRoleEntity roleEntity = new SysRoleEntity(); + roleEntity.setId(finalRoleId); + roleEntity.setName("默认角色_" + tenantName + "_" + System.currentTimeMillis()); + roleEntity.setDeptId(tenantCode); + roleEntity.setTenantCode(tenantCode); + roleEntity.setCompanyId(tenantCode); + roleEntity.setDefaultRole(false); + roleEntity.setType(RoleType.DEFAULT.getValue()); + return roleEntity; + } + + + /** + * 租户账号保存 + * + * @param dto dto + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void compSave(SysTenantUserDTO dto) { + if (!StringUtils.equals(dto.getPassword(), dto.getConfirmPassword())) { + throw new SysException("确认密码与密码不一致"); + } + SysTenantEntity sysTenantEntity = tenantDao.selectOneById(dto.getTenantId()); + SysUserEntity user = sysUserMapper.getByUsername(dto.getUsername()); + if (user != null) { + throw new SysException(ErrorCode.ACCOUNT_EXIST); + } + SysUserEntity userEntity = new SysUserEntity(); + userEntity.setUsername(dto.getUsername()); + userEntity.setRealName(sysTenantEntity.getTenantName()); + userEntity.setPassword(PasswordUtils.encode(dto.getPassword())); + userEntity.setSuperTenant(sysTenantEntity.getTenantType()); + userEntity.setSuperAdmin(SuperAdminEnum.NO.value()); + userEntity.setDeptId(sysTenantEntity.getTenantCode()); + userEntity.setStatus(dto.getStatus()); + userEntity.setGender(2); + userEntity.setTenantGroup(sysTenantEntity.getTenantType()); + userEntity.setEmail(dto.getEmail()); + userEntity.setMobile(dto.getMobile()); + userEntity.setTenantCode(sysTenantEntity.getTenantCode()); + sysUserMapper.insertSelective(userEntity); + + //绑定默认角色到账号中 + Map param = Maps.newHashMap(); + param.put("tenantCode", sysTenantEntity.getTenantCode()); + SysRoleDTO sysRoleDTO = sysRoleService.getDefaultRole(param); + if (ObjectUtil.isNotNull(sysRoleDTO)) { + SysRoleUserEntity defaultRoleUserEntity = new SysRoleUserEntity(); + defaultRoleUserEntity.setUserId(userEntity.getId()); + defaultRoleUserEntity.setRoleId(sysRoleDTO.getId()); + sysRoleUserMapper.insertSelective(defaultRoleUserEntity); + } + + //保存用户配置信息 + MsgUserDTO msgUserDTO = new MsgUserDTO(); + msgUserDTO.setUserId(userEntity.getId()); + msgUserDTO.setEmailUsername(userEntity.getEmail()); + msgUserDTO.setDingdingPhone(userEntity.getMobile()); + msgUserDTO.setWxPhone(userEntity.getMobile()); + msgUserService.saveDto(msgUserDTO); + + sysTenantEntity.setStatus(dto.getStatus()); + sysTenantEntity.setUserId(userEntity.getId()); + sysTenantEntity.setUsername(dto.getUsername()); + sysTenantEntity.setRemark(dto.getRemark()); + tenantDao.update(sysTenantEntity); + + if (dto.getRoleId() != null) { + SysRoleUserEntity roleUserEntity = new SysRoleUserEntity(); + roleUserEntity.setUserId(userEntity.getId()); + roleUserEntity.setRoleId(dto.getRoleId()); + sysRoleUserMapper.insertSelective(roleUserEntity); + } + } + + /** + * 租户账号修改 + * + * @param dto dto + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void compUpdate(SysTenantUserDTO dto) { + if (StringUtils.isNotBlank(dto.getPassword()) && !StringUtils.equals(dto.getPassword(), dto.getConfirmPassword())) { + throw new SysException("确认密码与密码不一致"); + } + SysTenantEntity sysTenantEntity = tenantDao.selectOneById(dto.getTenantId()); + + SysUserEntity userEntity = sysUserMapper.selectOneById(dto.getUserId()); + SysUserEntity user = sysUserMapper.getByUsername(dto.getUsername()); + if (user != null && !Objects.equals(userEntity.getId(), user.getId())) { + throw new SysException(ErrorCode.ACCOUNT_EXIST); + } + userEntity.setUsername(dto.getUsername()); + userEntity.setRealName(sysTenantEntity.getTenantName()); + if (StringUtils.isNotBlank(dto.getPassword())) { + userEntity.setPassword(PasswordUtils.encode(dto.getPassword())); + } + userEntity.setStatus(dto.getStatus()); + userEntity.setEmail(dto.getEmail()); + userEntity.setMobile(dto.getMobile()); + sysUserMapper.update(userEntity); + + //保存用户配置信息 + MsgUserDTO msgUserDTO = msgUserService.getInfoByUserId(userEntity.getId()); + if (ObjectUtil.isNotNull(msgUserDTO)) { + msgUserDTO.setEmailUsername(userEntity.getEmail()); + msgUserDTO.setDingdingPhone(userEntity.getMobile()); + msgUserDTO.setWxPhone(userEntity.getMobile()); + msgUserService.updateDto(msgUserDTO); + } + + sysTenantEntity.setStatus(dto.getStatus()); + sysTenantEntity.setUsername(dto.getUsername()); + sysTenantEntity.setRemark(dto.getRemark()); + tenantDao.update(sysTenantEntity); + + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + + //先删除 后插入 + List roleIdList = sysRoleUserMapper.getRoleIdLists(dto.getUserId(), tenantCode); + if (ObjectUtil.isNotNull(roleIdList) && !roleIdList.isEmpty()) { + sysRoleUserMapper.deleteByRoleIdsAndUserId(roleIdList, dto.getUserId()); + } + SysRoleUserEntity roleUserEntity = new SysRoleUserEntity(); + roleUserEntity.setUserId(userEntity.getId()); + roleUserEntity.setRoleId(dto.getRoleId()); + sysRoleUserMapper.insertSelective(roleUserEntity); + + } + + /** + * 设置用户信息 + * + * @param tenantDetail 租户 + * @param tenantUser 用户 + * @param tenantCode 租户编码 + * @return 用户信息 + */ + private SysUserEntity setUserEntity(SysTenantDetailEntity tenantDetail, TenantDetailForm.TenantUser tenantUser, Long tenantCode) { + if (!StringUtils.equals(tenantUser.getPassword(), tenantUser.getConfirmPassword())) { + throw new SysException("确认密码与密码不一致"); + } + //账号已存在 + SysUserEntity user = sysUserMapper.getByUsername(tenantUser.getUsername()); + if (user != null) { + throw new SysException(ErrorCode.ACCOUNT_EXIST); + } + SysUserEntity userEntity = new SysUserEntity(); + userEntity.setUsername(tenantUser.getUsername()); + userEntity.setRealName(tenantDetail.getName()); + userEntity.setPassword(PasswordUtils.encode(tenantUser.getPassword())); + userEntity.setSuperTenant(SuperTenantEnum.YES.value()); + userEntity.setSuperAdmin(SuperAdminEnum.NO.value()); + userEntity.setStatus(1); + userEntity.setGender(2); + userEntity.setTenantGroup(tenantDetail.getTenantType()); + userEntity.setEmail(tenantUser.getEmail()); + userEntity.setMobile(tenantUser.getPhone()); + userEntity.setTenantCode(tenantCode); + userEntity.setDeptId(tenantCode); + return userEntity; + } + + /** + * 设置租户信息 + * + * @param detail 基础信息 + * @param user 用户信息 + * @param thingCode 企业物编码 + * @return 租户信息 + */ + private SysTenantEntity setTenantEntity(SysTenantDetailEntity detail, SysUserEntity user, String thingCode) { + SysTenantEntity tenantEntity = new SysTenantEntity(); + tenantEntity.setTenantCode(detail.getId()); + tenantEntity.setTenantName(detail.getName()); + tenantEntity.setTenantType(detail.getTenantType()); + tenantEntity.setThingCode(thingCode); + tenantEntity.setDelFlag(DeleteEnum.NO.value()); + tenantEntity.setSysTenant(SysTenantEnum.NO.value()); + tenantEntity.setStatus(1); + tenantEntity.setRemark(detail.getRemark()); + if (user != null) { + tenantEntity.setUserId(user.getId()); + tenantEntity.setUsername(user.getUsername()); + } + return tenantEntity; + } + + /** + * 设置部门信息 + * + * @param tenantName 名称 + * @param id 租户编码作为部门主键 + * @return 部门信息 + */ + private SysDeptEntity setDeptEntity(String tenantName, Long id) { + SysDeptEntity deptEntity = new SysDeptEntity(); + deptEntity.setPid(Constant.DEPT_ROOT); + deptEntity.setName(tenantName); + deptEntity.setSort(Constant.DEPT_SORT_ROOT); + deptEntity.setTenantCode(id); + deptEntity.setId(id); + return deptEntity; + } +} diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantGroupServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantGroupServiceImpl.java new file mode 100644 index 0000000..e68615a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantGroupServiceImpl.java @@ -0,0 +1,193 @@ +package com.thing.sys.tenant.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupForm; +import com.thing.sys.tenant.entity.SysTenantEntity; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; +import com.thing.sys.tenant.mapper.SysTenantGroupMapper; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantGroupService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 租户组关系表 + * + * @author zhenghh + * @since 3.0 2021-12-27 + */ +@Service +@Primary +public class SysTenantGroupServiceImpl extends BaseServiceImpl implements SysTenantGroupService { + + @Autowired + private SysTenantMapper tenantDao; + + @Override + public QueryWrapper getWrapper(Map params){ + return new QueryWrapper(); + } + + /** + * 父租户code获取子列表 + * + * @param parentCode 父租户code + * @return 子租户列表 + */ + @Override + public List getChildren(Long parentCode) { + if (parentCode == null) { + return new ArrayList<>(); + } + QueryWrapper queryWrapper = new QueryWrapper() + .eq(SysTenantGroupEntity::getParentCode, parentCode); + List list = mapper.selectListByQuery(queryWrapper); + if (CollectionUtil.isEmpty(list)) { + return new ArrayList<>(); + } + return list.parallelStream().map(SysTenantGroupEntity::getCode).distinct().collect(Collectors.toList()); + } + + /** + * 删除 + * + * @param parentCode 父租户code + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteByParentCode(Long parentCode) { + if(parentCode == null) { + return; + } + QueryWrapper wrapper = new QueryWrapper() + .eq(SysTenantGroupEntity::getParentCode, parentCode); + mapper.deleteByQuery(wrapper); + } + + /** + * 已选企业列表 + * + * @param tenantCode 租户编码 + * @return list + */ + @Override + public List groupChildren(Long tenantCode) { + List childrenCode = getChildren(tenantCode); + if(CollectionUtil.isEmpty(childrenCode)) { + return new ArrayList<>(); + } + List list = tenantDao.selectListByQuery(new QueryWrapper() + .in(SysTenantEntity::getTenantCode, childrenCode) + .select(SysTenantEntity::getTenantCode, SysTenantEntity::getTenantName)); + return ConvertUtils.sourceToTarget(list, SysTenantDTO.class); + } + + /** + * 保存 + * + * @param form 表单 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysTenantGroupForm form) { + Long tenantCode = form.getTenantCode(); + List childrenTenantCodeList = form.getChildren(); + //先删除 + if (form.getGroup()) deleteByParentCode(tenantCode); + if (CollectionUtil.isNotEmpty(childrenTenantCodeList)) { + List collect = childrenTenantCodeList.stream().map(code -> { + SysTenantGroupEntity entity = new SysTenantGroupEntity(); + entity.setCode(code); + entity.setParentCode(tenantCode); + return entity; + }).collect(Collectors.toList()); + + //获取这些子节点的绑定情况 + List existNode = mapper.selectListByQuery(QueryWrapper.create().in(SysTenantGroupEntity::getCode, childrenTenantCodeList)); + Map> existMap = CollectionUtil.isNotEmpty(existNode) ? existNode.stream().collect(Collectors.groupingBy(SysTenantGroupEntity::getCode)) : Maps.newHashMap(); + collect = collect.stream().peek(item -> { + if (!existMap.containsKey(item.getCode()) || CollectionUtil.isEmpty(existMap.get(item.getCode()).stream().map(SysTenantGroupEntity::getParentModifyCode).filter(Objects::nonNull).collect(Collectors.toList()))) { + item.setParentModifyCode(tenantCode); + } + }).collect(Collectors.toList()); + saveBatch(collect); + } + } + + /** + * 删除 + * + * @param tenantCodes 父编码集合 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteBatchCodes(List tenantCodes) { + QueryWrapper wrapper = new QueryWrapper() + .in(SysTenantGroupEntity::getCode, tenantCodes); + mapper.deleteByQuery(wrapper); + } + + /** + * 不为超管 或 超管切换租户时,查询自己及下属 + * @param params 参数 + * @param includeTenant 是否包含当前租户 + */ + @Override + public void paramsAddTenantCodeList(Map params, Boolean includeTenant) { + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + List tenantCodeList = getChildren(tenantCode); + if(includeTenant) { + tenantCodeList.add(tenantCode); + } + //保证查不到数据 + if(CollectionUtil.isEmpty(tenantCodeList)) { + tenantCodeList.add(-1L); + } + params.put("tenantCodeList", tenantCodeList); + } + } + + /** + * 不为超管时,查询自己下属 + * + * @param wrapper 参数 + * @param includeTenant 是否包含当前租户 + */ + @Override + public void wrapperAddTenantCodeList(QueryWrapper wrapper, Boolean includeTenant) { + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + List tenantCodeList = getChildren(tenantCode); + if(includeTenant) { + tenantCodeList.add(tenantCode); + } + //保证查不到数据 + if(CollectionUtil.isEmpty(tenantCodeList)) { + tenantCodeList.add(-1L); + } + wrapper.in(SysTenantEntity::getTenantCode, tenantCodeList); + } + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantRoleServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantRoleServiceImpl.java new file mode 100644 index 0000000..723b4b3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantRoleServiceImpl.java @@ -0,0 +1,151 @@ +package com.thing.sys.tenant.service.impl; + +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.RoleType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysRoleDTO; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.mapper.SysRoleMapper; +import com.thing.sys.biz.service.SysRoleMenuService; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.tenant.service.SysTenantRoleService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 租户角色 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysTenantRoleServiceImpl extends BaseServiceImpl implements SysTenantRoleService { + private final SysRoleMenuService sysRoleMenuService; + private final SysRoleUserService sysRoleUserService; + + @Override + public PageData page(Map params) { + paramsToCompany(params); + Page page = mapper.selectPageExt( + getPage(params), + buildOrderWrapper(params, Constant.CREATE_DATE, false) + ); + + return getPageData(page, SysRoleDTO.class); + } + + @Override + public List list(Map params) { + paramsToCompany(params); + List entityList = mapper.selectListExt(getWrapper(params)); + + return ConvertUtils.sourceToTarget(entityList, SysRoleDTO.class); + } + + @Override + public QueryWrapper getWrapper(Map params) { + String name = (String) params.get("name"); + String type = (String) params.get("type"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("name", name, StringUtils.isNotBlank(name)).eq("type", type, StringUtils.isNotBlank(type)); + return wrapper; + } + + /** + * 企业角色 + * + * @param params 查询条件 + * @return page + */ + @Override + public PageData compPage(Map params) { + paramsToCompany(params); + QueryWrapper eq = buildOrderWrapper(params, Constant.CREATE_DATE, false).eq(SysRoleEntity::getTenantCode, UserContext.getRealTenantCode()); + Page page = mapper.selectPageExt( + getPage(params),eq + ); + return getPageData(page, SysRoleDTO.class); + } + + /** + * 企业角色 + * + * @param params 查询条件 + * @return list + */ + @Override + public List compList(Map params) { + paramsToCompany(params); + List entityList = mapper.selectListExt(getWrapper(params)); + return ConvertUtils.sourceToTarget(entityList, SysRoleDTO.class); + } + + @Override + public SysRoleDTO get(Long id) { + SysRoleEntity entity = mapper.selectOneById(id); + + return ConvertUtils.sourceToTarget(entity, SysRoleDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + //保存角色 + entity.setDefaultRole(false); + entity.setType(RoleType.COMPANY.getValue()); + save(entity); + + //保存角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysRoleDTO dto) { + SysRoleEntity entity = ConvertUtils.sourceToTarget(dto, SysRoleEntity.class); + + //更新角色 + updateById(entity); + + //更新角色菜单关系 + sysRoleMenuService.saveOrUpdate(entity.getId(), dto.getMenuIdList()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(Long[] ids) { + //使用中的角色不能删除 + Long count = sysRoleUserService.selectCountByRoleIds(Arrays.asList(ids)); + if (count > 0) { + throw new SysException("存在已分配的角色,无法删除"); + } + //删除角色 + mapper.deleteBatchByIds(Arrays.asList(ids)); + + //删除角色用户关系 + sysRoleUserService.deleteByRoleIds(ids); + + //删除角色菜单关系 + sysRoleMenuService.deleteByRoleIds(ids); + } + + private void paramsToCompany(Map params) { + params.put("type", RoleType.COMPANY.getValue()); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantServiceImpl.java b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantServiceImpl.java new file mode 100644 index 0000000..3676ff6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/sys/tenant/service/impl/SysTenantServiceImpl.java @@ -0,0 +1,448 @@ +package com.thing.sys.tenant.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Maps; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.DeleteEnum; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.enumeration.SuperTenantEnum; +import com.thing.common.core.enumeration.SysTenantEnum; +import com.thing.common.core.exception.ErrorCode; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.TreeUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.msg.userinfo.dto.MsgUserDTO; +import com.thing.msg.userinfo.service.MsgUserService; +import com.thing.password.PasswordUtils; +import com.thing.sys.biz.dto.CtlUserDTO; +import com.thing.sys.biz.entity.SysRoleEntity; +import com.thing.sys.biz.entity.SysRoleUserEntity; +import com.thing.sys.biz.entity.SysUserEntity; +import com.thing.sys.biz.mapper.SysRoleUserMapper; +import com.thing.sys.biz.mapper.SysUserMapper; +import com.thing.sys.biz.service.SysRoleUserService; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.dto.RegionIndustryTenantInfoDTO; +import com.thing.sys.tenant.dto.SysTenantDTO; +import com.thing.sys.tenant.dto.SysTenantGroupTreeDTO; +import com.thing.sys.tenant.dto.UpdateStatusDTO; +import com.thing.sys.tenant.entity.SysTenantDetailEntity; +import com.thing.sys.tenant.entity.SysTenantEntity; +import com.thing.sys.tenant.mapper.SysTenantDetailMapper; +import com.thing.sys.tenant.mapper.SysTenantMapper; +import com.thing.sys.tenant.service.SysTenantGroupService; +import com.thing.sys.tenant.service.SysTenantService; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Primary; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 租户管理 + * + * @author Mark sunlightcs@gmail.com + */ +@Service +@RequiredArgsConstructor +@Primary +public class SysTenantServiceImpl extends BaseServiceImpl implements SysTenantService { + private final SysRoleUserService sysRoleUserService; + private final SysUserMapper sysUserMapper; + private final SysRoleUserMapper roleUserDao; + private final SysTenantDetailMapper tenantDetailDao; + private final SysTenantGroupService tenantGroupService; + private final MsgUserService msgUserService; + private final SysTenantMapper sysTenantMapper; + @Override + public PageData page(Map params) { + //查询 + params.put("sysTenant", StringUtils.isNotBlank((String)params.get("sysTenant")) + ? Integer.valueOf((String) params.get("sysTenant")) : null); + params.put("status", StringUtils.isNotBlank((String)params.get("status")) + ? Integer.valueOf((String) params.get("status")) : null); + params.put("tenantCode", StringUtils.isNotBlank((String)params.get("tenantCode")) + ? Long.parseLong((String) params.get("tenantCode")) : null); + params.put("tenantGroup", StringUtils.isNotBlank((String)params.get("tenantGroup")) + ? Integer.valueOf((String) params.get("tenantGroup")) : null); + + UserDetail userDetail = SecurityUser.getUser(); + if(!"admin".equals(userDetail.getUsername())) { + List tenantCodeList = userDetail.getTenantCodeList() == null ? new ArrayList<>() : userDetail.getTenantCodeList(); + tenantCodeList.add(userDetail.getTenantCode()); + params.put("tenantCodeList", tenantCodeList); + } + Page page = mapper.paginate(getPage(params), getWrapper(params)); + return getPageData(page.getRecords(), page.getTotalRow(), SysTenantDTO.class); + } + + /** + * 排除当前企业后所有企业列表 + * + * @param params 参数 + * @return page + */ + @Override + public PageData excludePage(Map params) { + String tenantCode = (String) params.get("tenantCode"); + String tenantName = (String) params.get("tenantName"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.ne("tenant_code", StringUtils.isNotBlank(tenantCode) ? Long.parseLong(tenantCode) : -1); + wrapper.eq("tenant_type", 0); + wrapper.like("tenant_name", tenantName, StringUtils.isNotBlank(tenantName)); + wrapper.isNotNull(SysTenantEntity::getUserId); + Page page = mapper.paginate(getPage(params),wrapper); + return new PageData<>(ConvertUtils.sourceToTarget(page.getRecords(), SysTenantDTO.class), page.getTotalRow()); + } + + /** + * 租户企业列表 + * + * @return list + */ + @Override + public List tenantList() { + QueryWrapper queryWrapper = new QueryWrapper() + .eq(SysTenantEntity::getTenantType, 1).select(SysTenantEntity::getTenantCode, SysTenantEntity::getTenantName); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + queryWrapper.eq(SysTenantEntity::getTenantCode,tenantCode); + } + return ConvertUtils.sourceToTarget(mapper.selectListByQuery(queryWrapper), SysTenantDTO.class); + } + + /** + * 分配物企业下拉列表 + * + * @return list + */ + @Override + public List selectList() { + Map params = Maps.newHashMapWithExpectedSize(1); + //不为超管时,查询自己下属 + UserDetail userDetail = SecurityUser.getUser(); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value())) { + List tenantCodeList = userDetail.getTenantCodeList() == null ? new ArrayList<>() : userDetail.getTenantCodeList(); + tenantCodeList.add(userDetail.getTenantCode()); + params.put("tenantCodeList", tenantCodeList); + } + return ConvertUtils.sourceToTarget(mapper.getSelectList(params), SysTenantDTO.class); + } + + /** + * 租户编码获取列表 + * + * @param tenantCodeList 租户编码 + * @return list + */ + @Override + public List selectListByTenantCodeList(List tenantCodeList) { + if(CollectionUtil.isEmpty(tenantCodeList)) { + return new ArrayList<>(); + } + return mapper.selectListByQuery(QueryWrapper.create() + .in(SysTenantEntity::getTenantCode, tenantCodeList)); + } + + /** + * 租户、企业账号分页 + * + * @param params 查询条件 + * @return page + */ + @Override + public PageData groupPage(Map params) { + String tenantType = (String)params.get("tenantType"); + params.put("tenantType", StringUtils.isNotBlank(tenantType) ? Integer.valueOf(tenantType) : null); + //不为超管时,查询自己下属 + tenantGroupService.paramsAddTenantCodeList(params, false); + List tenantCodeList = MapUtil.get(params, "tenantCodeList", List.class); + QueryWrapper eq = new QueryWrapper().like(SysTenantEntity::getTenantName, params.get("tenantName")) + .eq(SysTenantEntity::getTenantType, MapUtil.getInt(params,"tenantType"),StringUtils.isNotBlank(tenantType)) + .in(SysTenantEntity::getTenantCode, tenantCodeList,CollectionUtils.isNotEmpty(tenantCodeList)); + Page list = mapper.paginate(new Page<>(MapUtil.getInt(params,"page"),MapUtil.getInt(params,"limit")),eq); + if(CollectionUtil.isEmpty(list.getRecords())) { + return getPageData(list.getRecords(), list.getTotalRow(), SysTenantDTO.class); + } + //获取角色 + List userIds = list.getRecords().parallelStream().map(SysTenantEntity::getUserId).collect(Collectors.toList()); + List roleList = getRoleList(userIds); + return new PageData<>(list.getRecords().stream().map(item -> { + SysTenantDTO dto = ConvertUtils.sourceToTarget(item, SysTenantDTO.class, ConvertUtils.TypeConvertEnum.Long_to_Date); + dto.setRoleName(roleList.parallelStream().filter(role -> Objects.equals(dto.getUserId(), role.getId())) + .map(SysRoleEntity::getName).collect(Collectors.joining(","))); + return dto; + }).collect(Collectors.toList()), list.getTotalRow()); + } + + private List getRoleList(List userIds) { + return roleUserDao.getRoleListByUserIds(userIds); + } + + + /** + * 租户分组分页 + * + * @param params 参数 + * @return page + */ + @Override + public PageData groupTreePage(Map params) { + Page page = getPage(params); + params.put("tenant_type","1"); + Page data = mapper.paginate(page,getWrapper(params)); + List records = data.getRecords(); + if(CollectionUtil.isEmpty(records)) { + return getPageData(new ArrayList<>(), 0l, SysTenantGroupTreeDTO.class); + } + List parentList = records.stream().map(item -> { + SysTenantGroupTreeDTO dto = ConvertUtils.sourceToTarget(item, SysTenantGroupTreeDTO.class); + dto.setId(item.getTenantCode()); + dto.setPid(0L); + return dto; + }).collect(Collectors.toList()); + List parentCodeList = records.stream().map(SysTenantEntity::getTenantCode).collect(Collectors.toList()); + parentList.addAll(mapper.getChildren(parentCodeList)); + return new PageData<>(TreeUtils.build(parentList, 0L), data.getTotalRow()); + } + + public QueryWrapper getWrapper(Map params){ + String tenantName = (String) params.get("tenantName"); + String status = params.get("status").toString(); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like("tenant_name", tenantName, StringUtils.isNotBlank(tenantName)); + wrapper.eq("status", StringUtils.isNotBlank(status) ? Integer.parseInt(status) : 1); + List tenantCodeList = (List)params.get("tenantCodeList"); + wrapper.in(SysTenantEntity::getTenantCode, tenantCodeList,CollectionUtils.isNotEmpty(tenantCodeList)); + String type = (String) params.get("tenant_type"); + if(StringUtils.isNotBlank(type)){ + wrapper.eq("tenant_type", Integer.parseInt(type), StringUtils.isNotBlank(type)); + } + return wrapper; + } + + /** + * 无账号企业列表 + * @param id 租户主键 + * @return list + */ + @Override + public List noneList(Long id) { + QueryWrapper queryWrapper = new QueryWrapper() + .isNull(SysTenantEntity::getUserId) + .or(a -> a.eq(SysTenantEntity::getId, id), id != null) + .select(SysTenantEntity::getId, SysTenantEntity::getTenantName, SysTenantEntity::getTenantType); + tenantGroupService.wrapperAddTenantCodeList(queryWrapper, false); + return ConvertUtils.sourceToTarget(mapper.selectListByQuery(queryWrapper), SysTenantDTO.class); + } + + /** + * 登录名是否存在 + * + * @param params 参数 + * @return 是否存在 + */ + @Override + public Boolean validateUser(Map params) { + String id = (String) params.get("id"); + String username = (String) params.get("username"); + QueryWrapper wrapper = new QueryWrapper(); + wrapper.ne("id", StringUtils.isNotBlank(id) ? Long.parseLong(id) : -1L); + wrapper.eq("username", username, StringUtils.isNotBlank(username)); + return mapper.selectCountByQuery(wrapper) > 0; + } + + /** + * 租户列表 + * + * @return list + */ + @Override + public List listDTO() { + List list = mapper.selectListByQuery(new QueryWrapper()); + if(CollectionUtil.isEmpty(list)) { + return new ArrayList<>(); + } + //获取租户详情 + List tenantCodeList = list.parallelStream().map(SysTenantEntity::getTenantCode).collect(Collectors.toList()); + List tenantDetailList = tenantDetailDao.selectListByIds(tenantCodeList); + //获取角色 + List userIds = list.parallelStream().map(SysTenantEntity::getUserId).collect(Collectors.toList()); + List roleList = roleUserDao.selectListByQuery(new QueryWrapper().in(SysRoleUserEntity::getUserId, userIds)); + return list.stream().map(item -> { + SysTenantDTO dto = ConvertUtils.sourceToTarget(item, SysTenantDTO.class); + dto.setRoleIdList(roleList.parallelStream().filter(role -> Objects.equals(role.getUserId(), item.getUserId())) + .map(SysRoleUserEntity::getRoleId).collect(Collectors.toList())); + tenantDetailList.parallelStream().filter(detail -> Objects.equals(detail.getId(), item.getTenantCode())) + .findFirst().ifPresent(detail -> { + dto.setEmail(detail.getEmail()); + //dto.setMobile(detail.getContactNumber()); + }); + return dto; + }).collect(Collectors.toList()); + } + + @Override + public SysTenantDTO get(Long id) { + SysTenantEntity entity = mapper.getById(id); + return ConvertUtils.sourceToTarget(entity, SysTenantDTO.class); + } + + @Override + public SysTenantDTO getTenantCode(Long tenantCode) { + SysTenantEntity entity = mapper.getTenantCode(tenantCode); + + return ConvertUtils.sourceToTarget(entity, SysTenantDTO.class); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void save(SysTenantDTO dto) { + SysTenantEntity entity = ConvertUtils.sourceToTarget(dto, SysTenantEntity.class); + + //账号已存在 + SysUserEntity userEntity = sysUserMapper.getByUsername(dto.getUsername()); + if(userEntity != null){ + throw new SysException(ErrorCode.ACCOUNT_EXIST); + } + //保存用户 + userEntity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + userEntity.setPassword(PasswordUtils.encode(dto.getPassword())); + userEntity.setSuperTenant(SuperTenantEnum.YES.value()); + userEntity.setSuperAdmin(SuperAdminEnum.NO.value()); + userEntity.setGender(2); + sysUserMapper.insertSelective(userEntity); + + MsgUserDTO msgUserDTO = new MsgUserDTO(); + msgUserDTO.setUserId(userEntity.getId()); + msgUserDTO.setEmailUsername(userEntity.getEmail()); + msgUserDTO.setDingdingPhone(userEntity.getMobile()); + msgUserDTO.setWxPhone(userEntity.getMobile()); + msgUserDTO.setWxOpenId(userEntity.getOpenId()); + msgUserService.saveDto(msgUserDTO); + + //保存角色用户关系 + sysRoleUserService.saveOrUpdate(userEntity.getId(), dto.getRoleIdList()); + + //保存租户 + entity.setUserId(userEntity.getId()); + entity.setDelFlag(DeleteEnum.NO.value()); + entity.setSysTenant(SysTenantEnum.NO.value()); + save(entity); + + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void update(SysTenantDTO dto) { + //更新租户 + SysTenantEntity entity = ConvertUtils.sourceToTarget(dto, SysTenantEntity.class); + updateById(entity); + + //查询租户用户ID + Long userId = mapper.selectOneById(entity.getId()).getUserId(); + + //更新用户 + SysUserEntity userEntity = ConvertUtils.sourceToTarget(dto, SysUserEntity.class); + userEntity.setId(userId); + //密码加密 + if(StringUtils.isBlank(dto.getPassword())){ + userEntity.setPassword(null); + }else{ + String password = PasswordUtils.encode(dto.getPassword()); + userEntity.setPassword(password); + } + sysUserMapper.update(userEntity); + + MsgUserDTO msgUserDTO = msgUserService.getInfoByUserId(userEntity.getId()); + if (ObjectUtil.isNotNull(msgUserDTO)) { + msgUserDTO.setEmailUsername(userEntity.getEmail()); + msgUserDTO.setDingdingPhone(userEntity.getMobile()); + msgUserDTO.setWxPhone(userEntity.getMobile()); + msgUserDTO.setWxOpenId(userEntity.getOpenId()); + msgUserService.updateDto(msgUserDTO); + } + + //更新角色用户关系 + sysRoleUserService.saveOrUpdate(userEntity.getId(), dto.getRoleIdList()); + } + + /** + * 状态更新 + * + * @param updateStatusDTO 参数 + */ + @Override + public void updateStatus(UpdateStatusDTO updateStatusDTO) { + if(CollectionUtil.isEmpty(updateStatusDTO.getIds())) { + throw new SysException("请至少选择一条数据"); + } + QueryWrapper tenantWrapper = new QueryWrapper() + .in(SysTenantEntity::getId, updateStatusDTO.getIds()) + .select(SysTenantEntity::getId, SysTenantEntity::getUserId); + List tenantList = mapper.selectListByQuery(tenantWrapper); + if(CollectionUtil.isNotEmpty(tenantList)) { + //修改租户状态 + mapper.updateStatusByIds(updateStatusDTO.getIds(), updateStatusDTO.getStatus()); + //修改用户状态 + List userIds = tenantList.parallelStream().map(SysTenantEntity::getUserId).collect(Collectors.toList()); + sysUserMapper.updateStatusByIds(userIds, updateStatusDTO.getStatus()); + } + } + + @Override + public void delete(Long[] ids) { + mapper.deleteBatch(ids); + } + + /** + * 看板控制下拉选择 + * + * @return list + */ + @Override + public List ctlList() { + Map params = Maps.newHashMap(); + //不为超管时,查询自己下属 + tenantGroupService.paramsAddTenantCodeList(params, true); + List list = mapper.getList(params); + return ConvertUtils.sourceToTarget(list, CtlUserDTO.class); + } + + @Override + public List queryList(Map params) { + tenantGroupService.paramsAddTenantCodeList(params, true); + return sysTenantMapper.queryList(params); + } + + @Override + public List getThingCodesByTenantCode(Long tenantCode) { + return sysTenantMapper.getThingCodesByTenantCode(tenantCode); + } + + @Override + public List getByGroupTenantCode(Long tenantCode) { + return sysTenantMapper.getByGroupTenantCode(tenantCode); + } + + @Override + public List getTenantListByParams(Map params) { + return sysTenantMapper.getTenantListByParams(params); + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiController.java b/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiController.java new file mode 100644 index 0000000..5ab29a9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiController.java @@ -0,0 +1,219 @@ +package com.thing.thing.api.controller; + +import cn.hutool.core.util.ObjectUtil; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.api.dto.IotThingApiDTO; +import com.thing.thing.api.dto.IotThingApiDebugDTO; +import com.thing.thing.api.excel.IotThingApiExcel; +import com.thing.thing.api.service.IotThingApiService; +import com.thing.thing.dict.dto.IotThingDictRelationListDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.compress.utils.Lists; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Map; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@RestController +@RequestMapping("v2/api") +@Tag(name = "超级api") +public class IotThingApiController { + + @Resource + private IotThingApiService service; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@RequestParam Map params) { + PageData page = service.getPageData(params, IotThingApiDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + IotThingApiDTO data = service.getByIdAs(id, IotThingApiDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingApiDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + dto.setId(new SnowFlakeIDKeyGenerator().nextId()); + service.saveDto(dto); + return new Result().ok(dto); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingApiDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.updateDto(dto); + return new Result().ok(dto); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDelete(ids); + return new Result<>(); + } + + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + public void export(@RequestParam Map params, HttpServletResponse response) throws Exception { + List list = service.listAs(params, IotThingApiDTO.class); + ExcelUtils.exportExcel(ConvertUtils.sourceToTarget(list, IotThingApiExcel.class), "超级API列表", "超级API", IotThingApiExcel.class, "超级API.xls", response); + } + + @PostMapping("import") + @Operation(summary = "导入excel") + @Parameter(name = "file", description = "文件") + public Result importExcel(@RequestParam("file") MultipartFile file) { + service.importExcel(file); + return new Result<>(); + } + + @GetMapping("/routeId") + @Operation(summary = "路由id") + public Result getRouteId() { + return new Result().ok(service.getRouteId()); + } + + @GetMapping("/routeUrl/{type}") + @Operation(summary = "路由Url") + public Result getRouteUrl(@PathVariable Integer type) { + return new Result().ok(service.getRouteUrl(type)); + } + + @GetMapping("/generatorToken") + @Operation(summary = "token生成") + public Result getToken() { + return new Result().ok(service.getToken()); + } + + @GetMapping("/conditionTypes") + @Operation(summary = "物条件类型") + public Result>> conditionTypes() { + return new Result>>().ok(service.conditionTypes()); + } + + @GetMapping("/groupFunc") + @Operation(summary = "聚合函数") + public Result>> groupFunc() { + return new Result>>().ok(service.groupFunc()); + } + + @GetMapping("callDict") + @Operation(summary = "超级API的调用") + public Result> callDict(String id, Long entityId) { + if (ObjectUtil.isNull(id) || ObjectUtil.isNull(entityId)) { + return new Result>().ok(Lists.newArrayList()); + } + try { + return new Result>().ok(service.callDict(Long.parseLong(id), entityId)); + } catch (Exception e) { + return new Result>().ok(Lists.newArrayList()); + } + } + + @GetMapping("telemetryById") + @Operation(summary = "超级API的远程调用") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = Constant.ID, description = "超级API的主键"), + @Parameter(name = "routeId", description = "路由id"), + @Parameter(name = "routeToken", description = "路由token:当开启token,即校验token和过期时间;没开启即不校验"), + @Parameter(name = "page", description = "开始页码"), + @Parameter(name = "limit", description = "记录数"), + @Parameter(name = "reqParams", description = "过滤条件(即也是超级API的参数通用解释):" + + "1.物条件格式<{\"entitys\":{\"list\":[id集合,逗号拼接],\"type\":[物类型集合,逗号拼接],\"search\":{\"name\":[物名称集合,逗号拼接],\"code\":[物编码集合,逗号拼接]},\"relation\":[{\"group\":\"关系组id\",\"entity\":\"关系物id\",\"to\":\"\",\"level\":\"层级:数字\"}],\"group\":[物组id集合]}}>;" + + "2.物属性格式<{\"attrs\":{\"type\":\"all-全部,split-分属性:\",\"keys\":[type为all属性id集合,type为split集合:<{\"entitys\":[单个物id],\"keys\":[属性集合]}>],\"keyType\":\"SQ\"}}> " + + "3.时间格式<{\"type\":\"last-最新数据,只有一条;nearest-最近,对应nearest节点,interval--区间,range-时间段\",\"func\":\"聚合函数-sum,avg,max,min,count,blank\",\"agg\":{\"time\":\"{前端使用}\",\"day\":\"天\",\"hour\":\"时\",\"minute\":\"分\"},\"max\":\"最大取值条数\",\"interval\":{\"duration\":\"区间数字\",\"type\":\"区间类型-day,hour,minute\"},\"nearest\":{\"day\":\"天\",\"hour\":\"小时\",\"minute\":\"分钟\"},\"startTime\":\"时间段的开始时间\",\"endTime\":\"时间段的结束时间\"}> " + + "4.注意点:时间格式,在API本身的参数中缺少times的key,reqParams中不缺少 times") + }) + public Result> telemetryById(@RequestParam Map params) { + return new Result>().ok(service.telemetryById(params)); + } + + + + @GetMapping("telemetry") + @Operation(summary = "超级API的远程调用") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = Constant.ID, description = "超级API的主键"), + @Parameter(name = "routeId", description = "路由id"), + @Parameter(name = "routeToken", description = "路由token:当开启token,即校验token和过期时间;没开启即不校验"), + @Parameter(name = "page", description = "开始页码"), + @Parameter(name = "limit", description = "记录数"), + @Parameter(name = "reqParams", description = "过滤条件(即也是超级API的参数通用解释):" + + "1.物条件格式<{\"entitys\":{\"list\":[id集合,逗号拼接],\"type\":[物类型集合,逗号拼接],\"search\":{\"name\":[物名称集合,逗号拼接],\"code\":[物编码集合,逗号拼接]},\"relation\":[{\"group\":\"关系组id\",\"entity\":\"关系物id\",\"to\":\"\",\"level\":\"层级:数字\"}],\"group\":[物组id集合]}}>;" + + "2.物属性格式<{\"attrs\":{\"type\":\"all-全部,split-分属性:\",\"keys\":[type为all属性id集合,type为split集合:<{\"entitys\":[单个物id],\"keys\":[属性集合]}>],\"keyType\":\"SQ\"}}> " + + "3.时间格式<{\"type\":\"last-最新数据,只有一条;nearest-最近,对应nearest节点,interval--区间,range-时间段\",\"func\":\"聚合函数-sum,avg,max,min,count,blank\",\"agg\":{\"time\":\"{前端使用}\",\"day\":\"天\",\"hour\":\"时\",\"minute\":\"分\"},\"max\":\"最大取值条数\",\"interval\":{\"duration\":\"区间数字\",\"type\":\"区间类型-day,hour,minute\"},\"nearest\":{\"day\":\"天\",\"hour\":\"小时\",\"minute\":\"分钟\"},\"startTime\":\"时间段的开始时间\",\"endTime\":\"时间段的结束时间\"}> " + + "4.注意点:时间格式,在API本身的参数中缺少times的key,reqParams中不缺少 times") + }) + public Result> telemetry(@RequestParam Map params) { + return new Result>().ok(service.telemetry(params)); + } + + + @PostMapping("debug") + @Operation(summary = "调试(附带js计算的功能)") + @LogOperation("调试(附带js计算的功能)") + public Result> debug(@RequestBody IotThingApiDebugDTO iotThingApiDebugDTO) { + return new Result>().ok(service.debug(iotThingApiDebugDTO)); + } + + @PostMapping("queryByCustomize") + @Operation(summary = "自定义条件") + public Result> queryByCustomize(@RequestBody IotThingApiDebugDTO iotThingApiDebugDTO) { + return new Result>().ok(service.queryByCustomize(iotThingApiDebugDTO)); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiLogController.java b/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiLogController.java new file mode 100644 index 0000000..56a9552 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/controller/IotThingApiLogController.java @@ -0,0 +1,92 @@ +package com.thing.thing.api.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.api.dto.IotThingApiLogDTO; +import com.thing.thing.api.service.IotThingApiLogService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import org.springframework.web.bind.annotation.*; + + + +import java.util.Map; + +import javax.annotation.Resource; + +/** +* 超级api +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@RestController +@RequestMapping("v2/api/log") +@Tag(name="超级api日志") +public class IotThingApiLogController { + + @Resource + private IotThingApiLogService service; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)") + }) + public Result> page( @RequestParam Map params){ + PageData page = service.getPageData(params, IotThingApiLogDTO.class); + return new Result>().ok(page); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingApiLogDTO data = service.getByIdAs(id, IotThingApiLogDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingApiLogDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.saveDto(dto); + return new Result().ok(dto); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingApiLogDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.updateDto(dto); + return new Result().ok(dto); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDelete(ids); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityAttrDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityAttrDTO.java new file mode 100644 index 0000000..bbdfa8f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityAttrDTO.java @@ -0,0 +1,103 @@ +package com.thing.thing.api.dto; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.thing.util.BeanUtil; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/* + * 数据集中属性相关参数对象 + * */ +@Data +public class ApiEntityAttrDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1870555093936521615L; + /** + * 设备分属性类型:all 全部 split 分属性 none + */ + private String type; + + /** + * 分属性条件 entitys 实体列表 keys : 实体属性 keyType TS时序属性暂时默认 + * 不分属性使用这个集合 + */ + private String keys; + + private String codes; + + /** + * TS时序属性暂时默认 + */ + private String keyType; + + + /** + * 设备分属性 + */ + private List attrIds; + + /** + * 设备不分属性 + */ + private List attrCodes; + + /** + * 设备分属性 + */ + private Map> entityAttrMap; + + public static ApiEntityAttrDTO createFromJson(String json) { + if(StringUtils.isNotBlank(json)){ + json = JSONObject.parseObject(json).getString("attrs"); + ApiEntityAttrDTO dto = BeanUtil.jsonConvertBean(json, ApiEntityAttrDTO.class); + dto.parseKeys(); + return dto; + } + return null; + } + + + public void parseKeys() { + //无属性 + if (StringUtils.equals("none",type) || (StringUtils.isBlank(keys))) { + return; + } + //不分属性 + if (StringUtils.equals("all",type)) { + JSONArray jsonArray = JSONArray.parseArray(codes); + if(null == jsonArray || jsonArray.isEmpty()){ + return; + } + attrCodes = jsonArray.stream().map(Object::toString).toList(); + } + //分属性 + if (StringUtils.equals("split",type)) { + JSONArray jsonArray = JSONArray.parseArray(keys); + if(null == jsonArray || jsonArray.isEmpty()){ + return; + } + entityAttrMap = new HashMap<>(); + for (int i = 0; i < jsonArray.size(); i++) { + JSONObject jsonObject = jsonArray.getJSONObject(i); + JSONArray keys1 = jsonObject.getJSONArray("keys"); + JSONArray entitys = jsonObject.getJSONArray("entitys"); + if(!entitys.isEmpty() && !keys1.isEmpty()){ + entityAttrMap.put(entitys.getLong(0),keys1.stream() + .map(Object::toString) + .map(Long::parseLong) + .toList()); + } + } + } + } + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityRelationDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityRelationDTO.java new file mode 100644 index 0000000..8745d3e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiEntityRelationDTO.java @@ -0,0 +1,49 @@ +package com.thing.thing.api.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/* + * 数据集中物关系相关参数对象 + * */ +@Data +public class ApiEntityRelationDTO implements Serializable { + + @Serial + private static final long serialVersionUID = -3720733280082423061L; + + @Schema(description = "是否检索关系中所有节点,0-全部,1-层级") + + @NotBlank(message = "是否检索关系中所有节点不允许为空") + private String all; + + @Schema(description = "关系树根节点") + @NotBlank(message = "关系树根节点不允许为空") + private String relationRoot; + + private String thingId; + + private String upOrDown; + + private String layers; + + @Schema(description = "是否包含当前选择节点,0-包含,1-不包含") + private String containCurrent; + + private String group; + + private Long entity; + + private String to; + + private String level= "0"; + + + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/ApiTimeDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiTimeDTO.java new file mode 100644 index 0000000..131ebc8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/ApiTimeDTO.java @@ -0,0 +1,187 @@ +package com.thing.thing.api.dto; + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.enumeration.DatasetAttrTimestampType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.util.BeanUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.time.LocalDateTime; +import java.util.Objects; + +/* + * 数据集中属性相关参数对象 + * */ +@Data +public class ApiTimeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1870555093936521615L; + + @Schema(description = "时间类型:last(最新)/nearest(最近)/range(时间段)/interval") + private String type; + + @Schema(description = "聚合函数 :max:最大值 min:最小值 avg:平均值 sum:求和 new:最新值(无)") + private String func; + + @Schema(description = "最大值") + private Integer max; + + @Schema(description = "开始时间") + private String startTime; + + @Schema(description = "结束时间") + private String endTime; + + @Schema(description = "最近时间参数") + private String nearest; + + @Schema(description = "时间区间") + private String interval; + + @Schema(description = "聚合间隔") + private String agg; + + /** + * 开始时间 + */ + private Long start; + + /** + * 结束时间 + */ + private Long end; + + /** + * 是否最新值查询 + */ + private Boolean isLast; + + /** + * 是否聚合 + */ + private Boolean isAgg; + + /** + * 间隔时间 + */ + private Integer intervalVal; + + /** + * 间隔时间类型 + */ + private TimeType timeType; + +// private ApiTimeDetailDTO aggTime; + + + +// private List> timeInterval; + + public void parse(){ + //最新的不用处理 + if(StringUtils.equals(type, DatasetAttrTimestampType.LASTEST.getType())){ + isLast = true; + } + //最近 + if(StringUtils.equals(type, DatasetAttrTimestampType.NEAREST.getType())){ + isLast = false; + if(StringUtils.isBlank(nearest)){ + return; + } + JSONObject jsonObject = JSONObject.parseObject(nearest); + long day = jsonObject.getLong("day"); + long hour = jsonObject.getLong("hour"); + long minute = jsonObject.getLong("minute"); + long dayInMillis = day * 24 * 60 * 60 * 1000; + long hourInMillis = hour * 60 * 60 * 1000; + long minuteInMillis = minute * 60 * 1000; + end = DateTimeUtils.getCurrentTime(); + start = end - (dayInMillis + hourInMillis + minuteInMillis); + } + + //时间段 + if(StringUtils.equals(type, DatasetAttrTimestampType.RANGE.getType())){ + isLast = false; + end = DateTimeUtils.convertTimeToLong(endTime); + start = DateTimeUtils.convertTimeToLong(startTime); + } + //区间 + if(StringUtils.equals(type, DatasetAttrTimestampType.INTERVAL.getType())){ + isLast = false; + JSONObject jsonObject = JSONObject.parseObject(interval); + String durationType = jsonObject.getString("type"); + int duration = jsonObject.getIntValue("duration"); + LocalDateTime now = LocalDateTime.now(); + if(StringUtils.equals(durationType, TimeType.DAY.getName())){ + LocalDateTime dayEnd = DateTimeUtils.getDayEnd(now); + LocalDateTime dayStart = dayEnd.minusDays(duration); + end = DateTimeUtils.parse(dayEnd); + start = DateTimeUtils.parse(dayStart)+1; + } + + if(StringUtils.equals(durationType, TimeType.WEEK.getName())){ + LocalDateTime weekEnd = DateTimeUtils.getWeekEnd(now); + LocalDateTime weekStart = weekEnd.minusWeeks(duration); + end = DateTimeUtils.parse(weekEnd); + start = DateTimeUtils.parse(weekStart)+1; + } + + if(StringUtils.equals(durationType, TimeType.MONTH.getName())){ + LocalDateTime monthEnd = DateTimeUtils.getMonthEnd(now); + LocalDateTime monthStart = monthEnd.minusMonths(duration); + end = DateTimeUtils.parse(monthEnd); + start = DateTimeUtils.parse(monthStart)+1; + } + + if(StringUtils.equals(durationType, TimeType.YEAR.getName())){ + LocalDateTime yearEnd = DateTimeUtils.getYearEnd(now); + LocalDateTime yearStart = yearEnd.minusYears(duration); + end = DateTimeUtils.parse(yearEnd); + start = DateTimeUtils.parse(yearStart)+1; + } + + } + + //聚合间隔 + if(StringUtils.isNotBlank(agg)){ + //先给个默认值 + isAgg = false; + JSONObject jsonObject = JSONObject.parseObject(agg); + Integer day = jsonObject.getInteger("day"); + Integer hour = jsonObject.getInteger("hour"); + Integer minute = jsonObject.getInteger("minute"); + if(Objects.nonNull(day) && !Objects.equals(day,0)){ + isAgg = true; + timeType = TimeType.DAY; + intervalVal = day; + } + if(Objects.nonNull(hour) && !Objects.equals(hour,0)){ + isAgg = true; + timeType = TimeType.HOUR; + intervalVal = hour; + } + if(Objects.nonNull(minute) && !Objects.equals(minute,0)){ + isAgg = true; + timeType = TimeType.MINUTE; + intervalVal = minute; + } + } + } + + public static ApiTimeDTO createFromJson(String json) { + if(StringUtils.isBlank(json)){ + return null; + } + ApiTimeDTO dto = BeanUtil.jsonConvertBean(json, ApiTimeDTO.class); + dto.parse(); + return dto; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDTO.java new file mode 100644 index 0000000..550a67c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDTO.java @@ -0,0 +1,89 @@ +package com.thing.thing.api.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 超级api +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Data +@Schema( name= "超级api") +public class IotThingApiDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + + @Schema(description = "API名称") + private String name; + + @Schema(description = "0 组态设计 1超级api") + //@NotBlank(message = "查询类型不能为空",groups = {AddGroup.class,UpdateGroup.class}) + private String type; + + @Schema(description = "路由id") + private Long routeId; + + @Schema(description = "路由地址") + private String routeUrl; + + @Schema(description = "排序:asc正序 desc 倒序") + private String sort; + + @Schema(description = "物的条件查询参数") + private String thingCondition; + + @Schema(description = "物的属性查询参数") + private String attrCondition; + + @Schema(description = "物的时间查询参数") + private String timeCondition; + + @Schema(description = "查询token") + private String token; + + @Schema(description = "启用:0未启用1启用") + private String enableStatus; + + @Schema(description = "过期时间") + private Long expire; + + @Schema(description = "调用次数") + private Long callsNum; + + @Schema(description = "备注") + private String remark; + + @Schema(description = "租户编码") + private Long tenantCode; + + @Schema(description = "公司id") + private Long companyId; + + @Schema(description = "部门id") + private Long deptId; + + @Schema(description = "创建人") + private Long creator; + + @Schema(description = "创建时间") + private Long createDate; + + @Schema(description = "修改人") + private Long updater; + + @Schema(description = "修改时间") + private Long updateDate; + + @Schema(description = "请求参数") + private String reqParams; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDebugDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDebugDTO.java new file mode 100644 index 0000000..055346f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiDebugDTO.java @@ -0,0 +1,45 @@ +package com.thing.thing.api.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +import javax.validation.constraints.NotNull; + +/** + * @author zzx + */ +@Data +public class IotThingApiDebugDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "函数") + @NotNull(message = "计算公式不能为空") + private String callBody; + + @Schema(description = "条件") + @NotNull + private String thingCondition; + + @Schema(description = "条件") + @NotNull + private String attrCondition; + + @Schema(description = "条件") + @NotNull + private String timeCondition; + + @Schema(description = "条件") + private String reqParams; + + private Integer page; + + private Integer limit; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiLogDTO.java b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiLogDTO.java new file mode 100644 index 0000000..095a9f3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/dto/IotThingApiLogDTO.java @@ -0,0 +1,56 @@ +package com.thing.thing.api.dto; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * ${comments} + * + * @author ls 825308804@qq.com + * @since 1.0.0 2022-08-26 + */ +@Data +@EqualsAndHashCode(callSuper = false) +public class IotThingApiLogDTO { + + /** + * 调用API的主键 + */ + private Long apiId; + + /** + * 调用API的名称 + */ + private String apiName; + + /** + * 调用方 + */ + private String caller; + /** + * 调用体 + */ + private String callEntity; + + private Long id; + /** + * 创建者 + */ + private Long creator; + /** + * 创建时间 + */ + private Long createDate; + + /** + * 修改人 + */ + private Long updater; + /** + * 修改时间 + */ + private Long updateDate; + + private String ip; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiEntity.java b/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiEntity.java new file mode 100644 index 0000000..30acbcf --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiEntity.java @@ -0,0 +1,90 @@ +package com.thing.thing.api.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_api") +public class IotThingApiEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * API名称 + */ + private String name; + /** + * 查询类型 + */ + private String type; + + /** + * 路由id + */ + private Long routeId; + /** + * 路由地址 + */ + private String routeUrl; + + /** + * 排序:asc正序 desc 倒序 + */ + private String sort; + + /** + * 物的条件查询参数 + */ + private String thingCondition; + /** + * 物的属性查询参数 + */ + private String attrCondition; + /** + * 物的时间查询参数 + */ + private String timeCondition; + /** + * 查询token + */ + private String token; + + /** + * 启用:0未启用1启用 + */ + private String enableStatus; + /** + * 过期时间 + */ + private Long expire; + /** + * 调用次数 + */ + private Long callsNum; + + /** + * 函数 + */ + private String callBody; + /** + * 备注 + */ + private String remark; + + + private String reqParams; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiLogEntity.java b/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiLogEntity.java new file mode 100644 index 0000000..7c682aa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/entity/IotThingApiLogEntity.java @@ -0,0 +1,60 @@ +package com.thing.thing.api.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.orm.entity.BaseDateEntity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import javax.validation.constraints.NotNull; +import java.io.Serial; + +/** + * ${comments} + * + * @author ls 825308804@qq.com + * @since 1.0.0 2022-08-26 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_api_log") +public class IotThingApiLogEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + + @Schema(description = "主键") + @NotNull(message = "API日志id不能为空",groups = UpdateGroup.class) + private Long id; + + /** + * 调用API的主键 + */ + @Schema(description = "调用API的主键") + private Long apiId; + + /** + * 调用API的名称 + */ + @Schema(description = "调用API的名称") + private String apiName; + + /** + * 调用方 + */ + @Schema(description = "调用方") + private String caller; + /** + * 调用体 + */ + @Schema(description = "调用体") + private String callEntity; + + @Schema(description = "调用IP") + private String ip; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/excel/IotThingApiExcel.java b/modules/thing/src/main/java/com/thing/thing/api/excel/IotThingApiExcel.java new file mode 100644 index 0000000..d68dddd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/excel/IotThingApiExcel.java @@ -0,0 +1,54 @@ +package com.thing.thing.api.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +public class IotThingApiExcel { + + @Excel(name = "API名称") + private String name; + + @Excel(name = "查询类型:0-组态设计 1-超级api",orderNum = "1") + private String type; + + @Excel(name = "路由id",orderNum = "2") + private Long routeId; + + @Excel(name = "路由地址",orderNum = "3") + private String routeUrl; + + @Excel(name = "排序",orderNum = "4") + private String sort; + + @Excel(name = "启用:0未启用1启用",orderNum = "5") + private String enableStatus; + + @Excel(name = "物的条件查询参数",orderNum = "6") + private String thingCondition; + + @Excel(name = "物的属性查询参数",orderNum = "7") + private String attrCondition; + + @Excel(name = "物的时间查询参数",orderNum = "8") + private String timeCondition; + + @Excel(name = "查询token",orderNum = "9") + private String token; + + @Excel(name = "过期时间",orderNum = "10") + private Long expire; + + @Excel(name = "备注",orderNum = "11") + private String remark; + + @Excel(name = "请求参数",orderNum = "12") + private String reqParams; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiLogMapper.java b/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiLogMapper.java new file mode 100644 index 0000000..b480916 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiLogMapper.java @@ -0,0 +1,16 @@ +package com.thing.thing.api.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.api.entity.IotThingApiLogEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * ${comments} + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-08-26 + */ +@Mapper +public interface IotThingApiLogMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiMapper.java b/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiMapper.java new file mode 100644 index 0000000..85b5123 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/mapper/IotThingApiMapper.java @@ -0,0 +1,19 @@ +package com.thing.thing.api.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.api.entity.IotThingApiEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** +* 超级api +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Mapper +public interface IotThingApiMapper extends PowerBaseMapper { + + void updateCallsNum(Long id, Long callsNum); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiLogService.java b/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiLogService.java new file mode 100644 index 0000000..90dd25d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiLogService.java @@ -0,0 +1,19 @@ +package com.thing.thing.api.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.api.entity.IotThingApiLogEntity; + +; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +public interface IotThingApiLogService extends IBaseService { + + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiService.java b/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiService.java new file mode 100644 index 0000000..2266d24 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/service/IotThingApiService.java @@ -0,0 +1,80 @@ +package com.thing.thing.api.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.api.dto.IotThingApiDebugDTO; +import com.thing.thing.api.entity.IotThingApiEntity; +import com.thing.thing.dict.dto.IotThingDictRelationListDTO; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +public interface IotThingApiService extends IBaseService { + + /** + * 获取路由接口 + */ + String getRouteUrl(Integer type); + + + /** + * 获取路由接口 + */ + String getToken(); + + /** + * 获取路由接口 + */ + Long getRouteId(); + + /** + * 聚合函数 + */ + List> groupFunc(); + + /** + * 物条件类型 + */ + List> conditionTypes(); + + + /** + * 数据集指定id查询(附带js计算的功能) + */ + List callDict(Long id, Long entityId); + + /** + * 单纯获取遥测数据 + */ + Map telemetryById( Map params); + + Map telemetry( Map params); + + /** + * 测试(附带js计算的功能) + * @param iotThingApiDebugDTO 入参 + * @return string + */ + Map debug(IotThingApiDebugDTO iotThingApiDebugDTO); + + /** + * 自定义条件查询 + * @return Map + */ + Map queryByCustomize(IotThingApiDebugDTO iotThingApiDebugDTO); + + /** + * websocket中的数据集操作 + * @return Map + */ + Map websocketApi(Long id); + + void importExcel(MultipartFile file); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiLogServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiLogServiceImpl.java new file mode 100644 index 0000000..ff9a892 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiLogServiceImpl.java @@ -0,0 +1,57 @@ +package com.thing.thing.api.service.impl; + +import com.alibaba.fastjson.JSON; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.api.mapper.IotThingApiLogMapper; +import com.thing.thing.api.dto.IotThingApiLogDTO; +import com.thing.thing.api.entity.IotThingApiLogEntity; +import com.thing.thing.api.service.IotThingApiLogService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * ${comments} + * + * @author chenyang 825308804@qq.com + * @since 1.0.0 2022-08-26 + */ +@Service +public class IotThingApiLogServiceImpl extends BaseServiceImpl implements IotThingApiLogService { + + + @Override + public QueryWrapper getWrapper(Map params){ + String apiId = (String) params.get("apiId"); + QueryWrapper wrapper = new QueryWrapper(); + if (StringUtils.isNotBlank(apiId)) wrapper.eq(IotThingApiLogEntity::getApiId, Long.parseLong(apiId)); + return wrapper; + } + + public PageData page(Map params) { + PageData page = getPageData(params, IotThingApiLogDTO.class); + if(page.getTotal()>0){ + List collect = page.getList().stream().peek(m -> { + m.setUpdateDate(m.getUpdateDate() * 1000); + m.setCreateDate(m.getCreateDate() * 1000); + String callEntity = m.getCallEntity(); + if(StringUtils.isNotBlank(callEntity)){ + m.setCallEntity(JSON.parseObject(callEntity).toJSONString()); + } + } + ).collect(Collectors.toList()); + page.setList(collect); + } + return page; + } + + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiServiceImpl.java new file mode 100644 index 0000000..666e486 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/api/service/impl/IotThingApiServiceImpl.java @@ -0,0 +1,873 @@ +package com.thing.thing.api.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.TypeReference; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.ScriptCreateService; +import com.thing.ScriptLanguage; +import com.thing.ScriptMsg; +import com.thing.api.ScriptEngine; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.*; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.annotation.DataFilter; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.api.controller.IotThingApiController; +import com.thing.thing.api.dto.*; +import com.thing.thing.api.entity.IotThingApiEntity; +import com.thing.thing.api.excel.IotThingApiExcel; +import com.thing.thing.api.mapper.IotThingApiMapper; +import com.thing.thing.api.service.IotThingApiLogService; +import com.thing.thing.api.service.IotThingApiService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictRelationListDTO; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDictDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.util.BeanUtil; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.servlet.mvc.method.RequestMappingInfo; +import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping; +import org.springframework.web.util.pattern.PathPattern; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * 超级api + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class IotThingApiServiceImpl extends BaseServiceImpl implements IotThingApiService { + + private final IotThingApiLogService apiLogService; + private final ScriptCreateService scriptCreateService; + private final ThingManageContextService thingManageContextService; + private final TsKvService tsKvService; + + + @Resource + @Qualifier("requestMappingHandlerMapping") + private RequestMappingHandlerMapping handlerMapping; + + @DataFilter + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + List idList = Lists.newArrayList(); + String ids = MapUtil.getStr(params, "ids"); + if (StringUtils.isNotBlank(ids)) { + idList.addAll(Arrays.stream(ids.split(",", -1)).map(Long::parseLong).toList()); + } + wrapper.like( + IotThingApiEntity::getName, + MapUtil.getStr(params, "name"), + StringUtils.isNotBlank(MapUtil.getStr(params, "name"))) + .eq( + IotThingApiEntity::getType, + MapUtil.getStr(params, "type"), + StringUtils.isNotBlank(MapUtil.getStr(params, "type"))) + .in(IotThingApiEntity::getId, idList, CollectionUtil.isNotEmpty(idList)) + .eq(IotThingApiEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(IotThingApiEntity::getCompanyId, UserContext.getRealCompanyId()); + return wrapper; + } + + public PageData page(Map params) { + paramsToLike(params, "name"); + return getPageData(params, IotThingApiDTO.class); + } + + public void save(IotThingApiDTO dto) { + String name = BizUtils.trimAll(dto.getName()); + IotThingApiEntity iotThingApiEntity = mapper.selectOneExt(QueryWrapper.create().eq(IotThingApiEntity::getName, name)); + if (ObjectUtil.isNotNull(iotThingApiEntity)) { + throw new SysException("当前API名称已经存在,请勿重复添加"); + } + Long routeId = getRouteId(); + dto.setId(routeId); + //组态设计 + if (StringUtils.equals(ApiTypes.CONFIG.getType(), dto.getType())) { + String token = getToken(); + dto.setRouteId(routeId); + dto.setToken(token); + dto.setEnableStatus(DatasetStatus.START.getStatus()); + }//超级API + else { + //若是启动,必须有有效期 + if (ObjectUtil.equals(dto.getEnableStatus(), DatasetStatus.START) && ObjectUtil.isNull(dto.getExpire())) { + throw new SysException("当前API有效时间不能为空"); + } else if (ObjectUtil.equals(dto.getEnableStatus(), DatasetStatus.STOP)) { + dto.setExpire(null); + } + if (ObjectUtil.isNull(dto.getRouteId())) { + dto.setRouteId(routeId); + } + if (ObjectUtil.isNull(dto.getToken())) { + dto.setToken(getToken()); + } + if (StringUtils.isBlank(dto.getRouteUrl())) { + dto.setRouteUrl(getRouteUrl(1)); + } + } + super.saveDto(dto); + } + + public void update(IotThingApiDTO dto) { + Long id = dto.getId(); + IotThingApiEntity iotThingApiEntity = getById(id); + if (ObjectUtil.isNull(iotThingApiEntity)) { + throw new SysException("当前API不存在"); + } + String name = BizUtils.trimAll(dto.getName()); + IotThingApiEntity entity = mapper.selectOneExt(QueryWrapper.create() + .eq(IotThingApiEntity::getName, name) + .ne(IotThingApiEntity::getId, id) + ); + if (ObjectUtil.isNotNull(entity)) { + throw new SysException("当前API名称已经存在,请勿重复添加"); + } + IotThingApiEntity insert = ConvertUtils.sourceToTarget(dto, IotThingApiEntity.class); + insert.setName(name); + super.updateById(insert); + } + + + @Override + public String getRouteUrl(Integer type) { + return getMethodUrl("telemetryById"); + } + + @Override + public String getToken() { + return TokenGenerator.generateValue(); + } + + @Override + public Long getRouteId() { + return new SnowFlakeIDKeyGenerator().nextId(); + } + + @Override + public List> groupFunc() { + ApiFuncEnum[] values = ApiFuncEnum.values(); + List> list = Lists.newArrayList(); + for (ApiFuncEnum value : values) { + Map param = new HashMap<>(); + if (value.getValue().equals("blank")) { + param.put("", ApiFuncEnum.getName(value.getValue())); + } else { + param.put(value.getValue(), ApiFuncEnum.getName(value.getValue())); + } + list.add(param); + } + return list; + } + + @Override + public List> conditionTypes() { + ApiTypeEnum[] values = ApiTypeEnum.values(); + List> list = Lists.newArrayList(); + + for (ApiTypeEnum value : values) { + Map param = new HashMap<>(); + param.put(value.getValue(), ApiTypeEnum.getName(value.getValue())); + list.add(param); + } + return list; + } + + + @Override + public List callDict(Long id, Long entityId) { + //获取对象 + IotThingApiEntity apiEntity = getById(id); + if (ObjectUtil.isNull(apiEntity)) { + return Lists.newArrayList(); + } + String attrCondition = apiEntity.getAttrCondition(); + if (StringUtils.isBlank(attrCondition)) { + return Lists.newArrayList(); + } + //这里为了处理分属性和不分属性的操作 + Map attrConditionMap = BeanUtil.jsonConvertMap(attrCondition); + String attrs = MapUtil.getStr(attrConditionMap, "attrs"); + if (StringUtils.isBlank(attrs)) { + return Lists.newArrayList(); + } + Map attrMap = BeanUtil.jsonConvertMap(attrs); + String type = MapUtil.getStr(attrMap, "type"); + String keys = MapUtil.getStr(attrMap, "keys"); + List dicts = Lists.newArrayList(); + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.SEPARATE.getValue())) { + List strings = BeanUtil.jsonConvertList(keys); + for (String string : strings) { + Map attrMap1 = JSON.parseObject(string, new TypeReference>() { + }); + String entitys = MapUtil.get(attrMap1, "entitys", String.class); + if (StringUtils.isNotBlank(entitys)) { + if (entitys.contains(entityId.toString())) { + List dictIds = MapUtil.get(attrMap1, "keys", List.class); + dicts.addAll(dictIds); + } + } + } + } else { + List dictIds = JSON.parseObject(keys, new TypeReference<>() { + }); + dicts.addAll(dictIds); + } + if (CollectionUtil.isEmpty(dicts)) { + return Lists.newArrayList(); + } + + List dictRelationEntities = thingManageContextService.findDictRelationAllByEntityIdAndCodes(entityId, null).orElseGet(Lists::newArrayList); + if (CollectionUtil.isEmpty(dictRelationEntities)) { + return Lists.newArrayList(); + } + dictRelationEntities = dictRelationEntities.stream().filter(d -> dicts.stream().anyMatch(s -> ObjectUtil.equals(Long.parseLong(s), d.getId()))).collect(Collectors.toList()); + + List entities = thingManageContextService.findViewEntityAllByIds(Collections.singletonList(entityId)).orElseGet(Lists::newArrayList); + if (CollectionUtil.isEmpty(entities)) { + return Lists.newArrayList(); + } + IotThingViewDTO thingEntity = entities.get(0); + return dictRelationEntities.stream().map(s -> { + IotThingDictRelationListDTO dto = new IotThingDictRelationListDTO(); + dto.setId(s.getId()); + dto.setEntityId(s.getEntityId()); + dto.setEntityName(thingEntity.getEntityName()); + dto.setEntityCode(thingEntity.getEntityCode()); + dto.setDictId(s.getId()); + dto.setDictCode(s.getCode()); + //dto.setDictType(s.getType()); + dto.setDictName(s.getName()); + return dto; + }).collect(Collectors.toList()); + } + + @Override + public Map telemetryById(Map params) { + IotThingApiEntity apiEntity = verifyApiEntity(params); + if (ObjectUtil.isNull(apiEntity)) { + return Maps.newHashMap(); + } + //分页信息 + Integer page = MapUtil.getInt(params, "page"); + Integer limit = MapUtil.getInt(params, "limit"); + + String reqParams = MapUtil.getStr(params, "reqParams"); + if (StringUtils.isNotBlank(reqParams)) { + apiEntity.setReqParams(reqParams); + } + Long tenantCode = null; + //说明要登录 + String remark = apiEntity.getRemark(); + if (StringUtils.equals(remark, "0")) { + tenantCode = UserContext.getRealTenantCode(); + } + //获取函数主体 + if (StringUtils.isBlank(apiEntity.getCallBody())) { + return encapsulationQuery(apiEntity.getThingCondition(), apiEntity.getAttrCondition(), apiEntity.getTimeCondition(), reqParams, page, limit, apiEntity.getSort(), tenantCode); + } else { + return functionScriptList(apiEntity.getCallBody(), null, + encapsulationQuery(apiEntity.getThingCondition(), apiEntity.getAttrCondition(), apiEntity.getTimeCondition(), reqParams, page, limit, apiEntity.getSort(), tenantCode)); + } + } + + @Override + public Map telemetry(Map params) { + IotThingApiEntity apiEntity = verifyApiEntity(params); + if (ObjectUtil.isNull(apiEntity)) { + return Maps.newHashMap(); + } + //分页信息 + Integer page = MapUtil.getInt(params, "page"); + Integer limit = MapUtil.getInt(params, "limit"); + + String reqParams = MapUtil.getStr(params, "reqParams"); + if (StringUtils.isNotBlank(reqParams)) { + apiEntity.setReqParams(reqParams); + } + Long tenantCode = MapUtil.getLong(params, "tenantCode"); + //获取函数主体 + if (StringUtils.isBlank(apiEntity.getCallBody())) { + return encapsulationQuery1(apiEntity.getThingCondition(), apiEntity.getAttrCondition(), apiEntity.getTimeCondition(), reqParams, page, limit, apiEntity.getSort(), tenantCode); + } else { + return functionScriptList(apiEntity.getCallBody(), null, + encapsulationQuery(apiEntity.getThingCondition(), apiEntity.getAttrCondition(), apiEntity.getTimeCondition(), reqParams, page, limit, apiEntity.getSort(), tenantCode)); + } + } + + /** + * 获取API的所有信息 + */ + private IotThingApiEntity verifyApiEntity(Map params) { + //超级API的id + Long id = MapUtil.getLong(params, "id"); + //路由id + Long routeId = MapUtil.getLong(params, "routeId"); + //路由token + String routeToken = MapUtil.getStr(params, "token"); + //获取API对象 + IotThingApiEntity apiEntity = null; + if (ObjectUtil.isNotNull(id)) { + apiEntity = getById(id); + } + if (ObjectUtil.isNull(id) && ObjectUtil.isNotNull(routeId)) { + apiEntity = mapper.selectOneByQuery(QueryWrapper.create().eq(IotThingApiEntity::getRouteId, routeId)); + } + if (ObjectUtil.isNotNull(apiEntity)) { + if (StringUtils.isBlank(apiEntity.getThingCondition())) { + throw new SysException("物条件不能为空"); + } + //校验调用入参情况 + verifyInvokeParams(apiEntity, routeToken); + } + return apiEntity; + } + + + /** + * 自定义条件查询 + * + * @return Map + */ + @Override + public Map queryByCustomize(IotThingApiDebugDTO iotThingApiDebugDTO) { + //获取函数主体 + if (StringUtils.isBlank(iotThingApiDebugDTO.getCallBody())) { + + return encapsulationQuery(iotThingApiDebugDTO.getThingCondition(), iotThingApiDebugDTO.getAttrCondition(), iotThingApiDebugDTO.getTimeCondition(), null, iotThingApiDebugDTO.getPage(), iotThingApiDebugDTO.getLimit(), Constant.DESC, null); + } else { + return functionScript(iotThingApiDebugDTO.getCallBody(), null, + encapsulationQuery(iotThingApiDebugDTO.getThingCondition(), iotThingApiDebugDTO.getAttrCondition(), iotThingApiDebugDTO.getTimeCondition(), null, iotThingApiDebugDTO.getPage(), iotThingApiDebugDTO.getLimit(), Constant.DESC, null)); + } + } + + @Override + public Map debug(IotThingApiDebugDTO iotThingApiDebugDTO) { + //数据集计算条件 + String thingCondition = iotThingApiDebugDTO.getThingCondition(); + String attrCondition = iotThingApiDebugDTO.getAttrCondition(); + String timeCondition = iotThingApiDebugDTO.getTimeCondition(); + if (StringUtils.isBlank(thingCondition) || StringUtils.isBlank(attrCondition) || StringUtils.isBlank(timeCondition)) { + throw new SysException("查询条件为空,请先增加查询条件"); + } + //js计算条件 + String calculationBody = iotThingApiDebugDTO.getCallBody(); + if (StringUtils.isBlank(calculationBody)) { + throw new SysException("计算公式不能为空"); + } + Map returnMap = encapsulationQuery(thingCondition, attrCondition, timeCondition, null, iotThingApiDebugDTO.getPage(), iotThingApiDebugDTO.getLimit(), null, null); + if (CollectionUtil.isEmpty(returnMap)) { + throw new SysException("查询物属性的值为空"); + } + try { + //测试入参入库 + if (ObjectUtil.isNotNull(iotThingApiDebugDTO.getId())) { + IotThingApiEntity entity = new IotThingApiEntity(); + entity.setId(iotThingApiDebugDTO.getId()); + entity.setCallBody(iotThingApiDebugDTO.getCallBody()); + entity.setThingCondition(iotThingApiDebugDTO.getThingCondition()); + entity.setAttrCondition(iotThingApiDebugDTO.getAttrCondition()); + entity.setTimeCondition(iotThingApiDebugDTO.getTimeCondition()); + updateById(entity); + + } + //计算 + String payload = null; + return functionScript(calculationBody, payload, returnMap); + } catch (Exception e) { + log.error(e.getMessage()); + throw new SysException("计算失败,请检查"); + } + } + + @Override + public Map websocketApi(Long id) { + IotThingApiDTO dto = getByIdAs(id, IotThingApiDTO.class); + if (ObjectUtil.isNull(dto)) { + return Maps.newHashMap(); + } + //todo 这里主要是组态设计的websocket请求,可能需要修改 + return encapsulationQuery(dto.getThingCondition(), dto.getAttrCondition(), dto.getTimeCondition(), dto.getReqParams(), null, null, dto.getSort(), null); + } + + @Override + public void importExcel(MultipartFile file) { + List sheetData = ExcelUtils.importExcel(file, 1, 1, IotThingApiExcel.class, 0); + if (CollectionUtil.isEmpty(sheetData)) { + throw new SysException("excel数据为空,无需导入"); + } + //去重 + List distinctApis = sheetData.stream().filter(s -> StringUtils.isNotBlank(s.getName())) + .collect( + Collectors.collectingAndThen(Collectors.toCollection(() -> + new TreeSet<>(Comparator.comparing(IotThingApiExcel::getName))), ArrayList::new)); + List iotThingApiEntities = ConvertUtils.sourceToTarget(distinctApis, IotThingApiEntity.class); + if (CollectionUtil.isNotEmpty(iotThingApiEntities)) { + List collect = iotThingApiEntities.stream().peek(s -> s.setRouteId(getRouteId())).collect(Collectors.toList()); + mapper.insertBatch(collect); + } + } + + + /** + * 获取过滤查询条件 + * + * @param reqParams 查询条件 + */ + private String packageQueryFilterParams(String reqParams) { + if (StringUtils.isBlank(reqParams)) { + return null; + } + //过滤条件 + Map conditionMap = BeanUtil.jsonConvertMap(reqParams); + //若没有属性就不处理 + String attrs = MapUtil.getStr(conditionMap, "attrs"); + if (StringUtils.isNotBlank(attrs)) { + Map attrMap = BeanUtil.jsonConvertMap(attrs); + String type = MapUtil.getStr(attrMap, "type"); + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.SEPARATE.getValue())) { + conditionMap.remove("attrs"); + conditionMap.put("splitAttrs", JSONObject.parseObject(attrs)); + } + } + return JSONObject.toJSONString(conditionMap); + } + + + /** + * 根据API 定义JSON的条件 获取物相关的属性遥测数据 + * :这样的遥测数据都是以MAP的方式返回,所以可以共用方法 + * + * @param thingCondition 物条件:列表,组,标签,属性 ,物关系等条件 + * @param attrCondition 属性条件:全属性和分属性 + * @param timeCondition 时间:时间范围,时间分组 + * @param sort 排序 + * @param tenantCode 用户数据过滤 + */ + private Map encapsulationQuery(String thingCondition, String attrCondition, String timeCondition, String reqParams, Integer page, Integer limit, String sort, Long tenantCode) { + //组装查询物条件的信息 + Map resultMap = Maps.newHashMap(); + //返回给前端的参数 + Map parmsMap = BeanUtil.jsonConvertMaps(thingCondition, attrCondition, timeCondition); + parmsMap.put("sort", sort); + resultMap.put("param", parmsMap); + resultMap.put("filterParam", reqParams); + //过滤条件的封装 + if (StringUtils.isNotBlank(reqParams)) { + reqParams = packageQueryFilterParams(reqParams); + Map conditionMap = BeanUtil.jsonConvertMap(reqParams); + // thingCondition = (String)conditionMap.get("entitys"); + attrCondition = (String) conditionMap.get("attrs"); + //timeCondition = (String)conditionMap.get("times");TODO 暂时时间没有过滤 + } + //属性查询类型 + Map entityResultMap = new HashMap<>(); + Map entityInfoMap = new HashMap<>(); + ApiEntityAttrDTO attrsEntity = ApiEntityAttrDTO.createFromJson(attrCondition); + String type = attrsEntity.getType(); + //查询物和属性的参数 + Map> paramMap = Maps.newHashMap(); + //分属性 + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.SEPARATE.getValue())) { + Map> entityAttrMap = attrsEntity.getEntityAttrMap(); + entityAttrMap.forEach((entityId, attrIds) -> { + Optional optional = thingManageContextService.findViewEntityById(entityId); + if (optional.isPresent()) { + String code = optional.get().getEntityCode(); + Optional> dictRelationDTOList = thingManageContextService.findDictRelationAllByIds(attrIds); + Collection dictCodes = dictRelationDTOList.orElseGet(Collections::emptyList).stream().map(IotThingDictRelationDTO::getCode).toList(); + paramMap.put(code, dictCodes); + // Map collect = dictRelationDTOList.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingDictRelationDTO::getCode, Function.identity(),(existing, replacement) -> existing)); + entityInfoMap.put(code, optional.get()); + } + }); + } + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.NO_SEPARATE.getValue())) { + String entitys = MapUtil.getStr(parmsMap,"entitys"); + JSONObject jsonObject = JSON.parseObject(entitys); + String listIds = jsonObject.getString("list"); + List ids = JSON.parseArray(listIds,Long.class); + + String typeList = jsonObject.getString("type"); + List types = JSON.parseArray(typeList,String.class); + + String search = jsonObject.getString("search"); + List name = Lists.newArrayList(); + List code = Lists.newArrayList(); + if(StringUtils.isNotBlank(search)){ + JSONObject searchJson = JSON.parseObject(search); + String searchName = searchJson.getString("name"); + String searchCode = searchJson.getString("code"); + name = JSON.parseArray(searchName,String.class); + code = JSON.parseArray(searchCode,String.class); + } + String tagList = jsonObject.getString("tag"); + List tags = JSON.parseArray(tagList,String.class); + String groupJson = jsonObject.getString("group"); + List group = JSON.parseArray(groupJson,Long.class); + JSONArray relation = jsonObject.getJSONArray("relation"); + List relationList = Lists.newArrayList(); + if(null != relation && !relation.isEmpty()){ + relationList = relation.stream().map(s-> BeanUtil.jsonConvertBean(s.toString(), ApiEntityRelationDTO.class)).toList(); + } + Optional> entityByCondition = thingManageContextService.findEntityByCondition(ids, types, tags, name, code, group, relationList, UserContext.getRealTenantCode(), true); + List entityDTOList = entityByCondition.get(); + Optional> dictRelationDTOList = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(entityDTOList.stream().map(IotThingEntityDictDTO::getId).toList(),attrsEntity.getAttrCodes()); + Map> listMap = dictRelationDTOList.orElseGet(Collections::emptyList).stream().collect(Collectors.groupingBy(IotThingDictRelationParamDTO::getEntityId)); + listMap.forEach((entityId, dictRelationDTOS) -> { + Optional optional = thingManageContextService.findViewEntityById(entityId); + if (optional.isPresent()) { + String entityCode = optional.get().getEntityCode(); + List dictCodes = dictRelationDTOS.stream().map(IotThingDictRelationParamDTO::getCode).toList(); + paramMap.put(entityCode, dictCodes); + // Map collect = dictRelationDTOS.stream().collect(Collectors.toMap(IotThingDictRelationParamDTO::getCode, Function.identity())); + entityInfoMap.put(entityCode, optional.get()); + } + }); + } + entityResultMap.put("info", entityInfoMap); + //没有属性直接返回 + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.NONE.getValue()) || MapUtil.isEmpty(paramMap)) { + resultMap.put("result", entityResultMap); + return resultMap; + } + //时间条件 + ApiTimeDTO timeDTO = ApiTimeDTO.createFromJson(timeCondition); + Boolean isLast = timeDTO.getIsLast(); + //不分页处理 + if ((ObjectUtil.isNull(limit) || ObjectUtil.isNull(page)) && (Objects.isNull(timeDTO.getMax()) || Objects.equals(timeDTO.getMax(),0)) ) { + if (isLast) { + //最新值没有聚合的说法 + List tsKvDTOList = tsKvService.findLatestByMultiMap(paramMap, StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } else { + //聚合 + if (timeDTO.getIsAgg()) { + List tsKvDTOList = tsKvService.findTsKvIntervalAggByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), + AggType.match(timeDTO.getFunc()), timeDTO.getIntervalVal(), timeDTO.getTimeType(), StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } else { + List tsKvDTOList = tsKvService.findTsKvByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } + } + } else { + if(timeDTO.getMax()>0){ + limit = timeDTO.getMax(); + } + if(Objects.isNull(page)){ + page = 1; + } + if (isLast) { + PageData pageData = tsKvService.findPageLatestByMultiMap(paramMap, StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } else { + //聚合 + if (timeDTO.getIsAgg()) { + PageData pageData = tsKvService.findPageTsKvIntervalAggByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), + AggType.match(timeDTO.getFunc()), timeDTO.getIntervalVal(), timeDTO.getTimeType(), StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } else { + PageData pageData = tsKvService.findPageTsKvByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } + } + } + resultMap.put("result", entityResultMap); + return resultMap; + } + + /** + * 根据API 定义JSON的条件 获取物相关的属性遥测数据 + * :这样的遥测数据都是以MAP的方式返回,所以可以共用方法 + * + * @param thingCondition 物条件:列表,组,标签,属性 ,物关系等条件 + * @param attrCondition 属性条件:全属性和分属性 + * @param timeCondition 时间:时间范围,时间分组 + * @param sort 排序 + * @param tenantCode 用户数据过滤 + */ + private Map encapsulationQuery1(String thingCondition, String attrCondition, String timeCondition, String reqParams, Integer page, Integer limit, String sort, Long tenantCode) { + //组装查询物条件的信息 + Map resultMap = Maps.newHashMap(); + //返回给前端的参数 + Map parmsMap = BeanUtil.jsonConvertMaps(thingCondition, attrCondition, timeCondition); + parmsMap.put("sort", sort); + resultMap.put("param", parmsMap); + resultMap.put("filterParam", reqParams); + //过滤条件的封装 + if (StringUtils.isNotBlank(reqParams)) { + reqParams = packageQueryFilterParams(reqParams); + Map conditionMap = BeanUtil.jsonConvertMap(reqParams); + // thingCondition = (String)conditionMap.get("entitys"); + attrCondition = (String) conditionMap.get("attrs"); + //timeCondition = (String)conditionMap.get("times");TODO 暂时时间没有过滤 + } + //属性查询类型 + Map entityResultMap = new HashMap<>(); + Map entityInfoMap = new HashMap<>(); + ApiEntityAttrDTO attrsEntity = ApiEntityAttrDTO.createFromJson(attrCondition); + String type = attrsEntity.getType(); + //查询物和属性的参数 + Map> paramMap = Maps.newHashMap(); + //分属性 + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.SEPARATE.getValue())) { + Map> entityAttrMap = attrsEntity.getEntityAttrMap(); + entityAttrMap.forEach((entityId, attrIds) -> { + Optional optional = thingManageContextService.findViewEntityById(entityId); + if (optional.isPresent()) { + String code = optional.get().getEntityCode(); + Optional> dictRelationDTOList = thingManageContextService.findDictRelationAllByIds(attrIds); + Collection dictCodes = dictRelationDTOList.orElseGet(Collections::emptyList).stream().map(IotThingDictRelationDTO::getCode).toList(); + paramMap.put(code, dictCodes); + // Map collect = dictRelationDTOList.orElseGet(Collections::emptyList).stream().collect(Collectors.toMap(IotThingDictRelationDTO::getCode, Function.identity(),(existing, replacement) -> existing)); + // optional.get().setAttrs(collect); + entityInfoMap.put(code, optional.get()); + } + }); + } + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.NO_SEPARATE.getValue())) { + Optional> dictRelationDTOList = thingManageContextService.findDictRelationAllByIds(attrsEntity.getAttrIds()); + Map> listMap = dictRelationDTOList.orElseGet(Collections::emptyList).stream().collect(Collectors.groupingBy(IotThingDictRelationDTO::getEntityId)); + listMap.forEach((entityId, dictRelationDTOS) -> { + Optional optional = thingManageContextService.findViewEntityById(entityId); + if (optional.isPresent()) { + String code = optional.get().getEntityCode(); + List dictCodes = dictRelationDTOS.stream().map(IotThingDictRelationDTO::getCode).toList(); + paramMap.put(code, dictCodes); + //Map collect = dictRelationDTOS.stream().collect(Collectors.toMap(IotThingDictRelationDTO::getCode, Function.identity())); + // optional.get().setAttrs(collect); + entityInfoMap.put(code, optional.get()); + } + }); + } + entityResultMap.put("info", entityInfoMap); + //没有属性直接返回 + if (StringUtils.equalsAnyIgnoreCase(type, ApiSeparateAttr.NONE.getValue()) || MapUtil.isEmpty(paramMap)) { + resultMap.put("result", entityResultMap); + return resultMap; + } + //时间条件 + ApiTimeDTO timeDTO = ApiTimeDTO.createFromJson(timeCondition); + Boolean isLast = timeDTO.getIsLast(); + //不分页处理 + if ((ObjectUtil.isNull(limit) || ObjectUtil.isNull(page)) && (Objects.isNull(timeDTO.getMax()) || Objects.equals(timeDTO.getMax(),0)) ) { + if (isLast) { + //最新值没有聚合的说法 + List tsKvDTOList = tsKvService.findLatestByMultiMap(paramMap, StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } else { + //聚合 + if (timeDTO.getIsAgg()) { + List tsKvDTOList = tsKvService.findTsKvIntervalAggByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), + AggType.match(timeDTO.getFunc()), timeDTO.getIntervalVal(), timeDTO.getTimeType(), StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } else { + List tsKvDTOList = tsKvService.findTsKvByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), StringUtils.equals(sort, Constant.ASC)); + entityResultMap.put("values", tsKvDTOList); + } + } + } else { + if(timeDTO.getMax()>0){ + limit = timeDTO.getMax(); + } + if(Objects.isNull(page)){ + page = 1; + } + if (isLast) { + PageData pageData = tsKvService.findPageLatestByMultiMap(paramMap, StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } else { + //聚合 + if (timeDTO.getIsAgg()) { + PageData pageData = tsKvService.findPageTsKvIntervalAggByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), + AggType.match(timeDTO.getFunc()), timeDTO.getIntervalVal(), timeDTO.getTimeType(), StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } else { + PageData pageData = tsKvService.findPageTsKvByMultiMap(paramMap, timeDTO.getStart(), timeDTO.getEnd(), StringUtils.equals(sort, Constant.ASC), page, limit); + entityResultMap.put("values", pageData.getList()); + } + } + } + resultMap.put("result", entityResultMap); + return resultMap; + } + + /** + * 校验超级API + */ + private void verifyInvokeParams(IotThingApiEntity apiEntity, String routeToken) { + //更新调用次数 + long finalCallsNum; + if (ObjectUtil.isNull(apiEntity.getCallsNum())) { + finalCallsNum = 1L; + } else { + finalCallsNum = apiEntity.getCallsNum() + 1; + } + mapper.updateCallsNum(apiEntity.getId(), finalCallsNum); + //组态设计 + if (StringUtils.equals(apiEntity.getType(), ApiTypes.CONFIG.getType())) { + return; + } + //校验token相关信息 + if (StringUtils.equals(DatasetStatus.START.getStatus(), apiEntity.getEnableStatus())) { + if (!StringUtils.equals(apiEntity.getToken(), routeToken)) { + throw new SysException("token校验失败"); + } + if (ObjectUtil.isNotNull(apiEntity.getExpire()) && apiEntity.getExpire() < System.currentTimeMillis()) + throw new SysException("token过期,该接口已过期"); + } + String thingCondition = apiEntity.getThingCondition(); + if (StringUtils.isBlank(thingCondition)) { + throw new SysException("物实体条件不能为空"); + } + } + + /** + * 方法调用 + * + * @param script 入参 + * @param payload 入参 + * @param metadata 入参 + * @return 结果 + */ + private Map functionScript(String script, String payload, Map metadata) { + String output = ""; + String errorText = ""; + ScriptEngine engine = null; + try { + //计算 + engine = scriptCreateService.createScriptEngine(System.currentTimeMillis(), ScriptLanguage.NASHORN, script, false, "payload", "msg"); + output = engine.executeToStringAsync(new ScriptMsg(payload, metadata)).get(20L, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("Error evaluating JS function", e); + errorText = StringUtils.isBlank(e.getMessage()) ? "Error evaluating JS function" : e.getMessage(); + } finally { + if (engine != null) { + engine.destroy(); + } + } + if (StringUtils.isNotBlank(errorText)) { + throw new SysException(errorText); + } + return JSON.parseObject(output, new TypeReference>() { + }); + } + + /** + * 方法调用 + * + * @param script 入参 + * @param payload 入参 + * @param metadata 入参 + * @return 结果 + */ + private Map functionScriptList(String script, String payload, Map metadata) { + String output = ""; + String errorText = ""; + ScriptEngine engine = null; + try { + //计算 + engine = scriptCreateService.createScriptEngine(System.currentTimeMillis(), ScriptLanguage.NASHORN, script, false, "payload", "msg"); + output = engine.executeToStringAsync(new ScriptMsg(payload, metadata)).get(20L, TimeUnit.SECONDS); + } catch (Exception e) { + log.error("Error evaluating JS function", e); + errorText = StringUtils.isBlank(e.getMessage()) ? "Error evaluating JS function" : e.getMessage(); + } finally { + if (engine != null) { + engine.destroy(); + } + } + if (StringUtils.isNotBlank(errorText)) { + throw new SysException(errorText); + } + return JSON.parseObject(output, new TypeReference>() { + }); + } + + + private void apiLog(IotThingApiEntity apiEntity, String caller) { + try { + IotThingApiLogDTO apiLogDTO = new IotThingApiLogDTO(); + apiLogDTO.setApiId(apiEntity.getId()); + apiLogDTO.setCaller(caller); + apiLogDTO.setApiName(apiEntity.getName()); + String thingCondition = apiEntity.getThingCondition(); + String attrCondition = apiEntity.getAttrCondition(); + String timeCondition = apiEntity.getTimeCondition(); + Map map = new HashMap<>(); + map.put("entitys", JSON.parseObject(thingCondition)); + map.put("attrs", JSON.parseObject(attrCondition)); + map.put("times", JSON.parseObject(timeCondition)); + apiLogDTO.setCallEntity(JSON.toJSONString(map)); + apiLogDTO.setCreator(UserContext.getUserId()); + apiLogDTO.setCreateDate(DateTimeUtils.getCurrentTime() / 1000); + apiLogDTO.setUpdater(UserContext.getUserId()); + apiLogDTO.setUpdateDate(DateTimeUtils.getCurrentTime() / 1000); + //调用IP + HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); + apiLogDTO.setIp(IpUtils.getIpAddr(request)); + apiLogService.saveDto(apiLogDTO); + } catch (Exception e) { + log.error(e.getMessage()); + } + } + + private String getMethodUrl(String methodName) { + try { + Class controllerClass = IotThingApiController.class; + String methodUrl = null; + // 获取所有处理器方法和对应的请求映射信息 + Map handlerMethods = handlerMapping.getHandlerMethods(); + for (Map.Entry entry : handlerMethods.entrySet()) { + HandlerMethod handlerMethod = entry.getValue(); + Method method = handlerMethod.getMethod(); + Class declaringClass = method.getDeclaringClass(); + if (declaringClass == controllerClass && method.getName().equals(methodName)) { + Set patterns = entry.getKey().getPathPatternsCondition().getPatterns(); + methodUrl = patterns.iterator().next().getPatternString(); + break; + } + } + return methodUrl; + } catch (Exception e) { + log.error("获取相关方法失败", e); + return null; + } + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigDTO.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigDTO.java new file mode 100644 index 0000000..9df3c02 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigDTO.java @@ -0,0 +1,148 @@ +package com.thing.thing.bizconfig.dto; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; + +import org.springframework.util.CollectionUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Data +@Schema(description = "设备业务配置") +public abstract class IotThingBizConfigDTO implements Serializable { + @Serial private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + + @Schema(description = "物实体id") + private String thingId; + + @Schema(description = "物code") + private String thingCode; + + @Schema(description = "物名称") + private String thingName; + + @Schema(description = "配置类型: 暂时只有变压器") + private String type; + + /** 配置项列表 */ + @JsonIgnore private List configItems; + + /** map(配置项名称,配置项) */ + @JsonIgnore private Map configItemMap; + + /** 将子类具体业务配置类的属性,转化为配置项: 用于数据入库 */ + public abstract void generateItems(); + + /** 将配置项的值填充到子业务类的具体属性中: 用于查询 */ + public abstract void fillConfigValue(); + + /** + * 将配置实体对象转为具体设备的业务配置DTO + * + * @param configEntity 配置实体 + * @param clazz 具体业务的设备配置类 + * @return 具体业务的设备配置类DTO + */ + public static T convertToDeviceConfig( + IotThingBizConfigEntity configEntity, Class clazz) { + if (Objects.isNull(configEntity)) { + return null; + } + T deviceConfig = ConvertUtils.sourceToTarget(configEntity, clazz); + deviceConfig.setConfigItems( + ConvertUtils.sourceToTarget( + configEntity.getConfigItems(), IotThingBizConfigItemDTO.class)); + deviceConfig.setThingId(String.valueOf(configEntity.getThingId())); + deviceConfig.fillConfigValue(); + return deviceConfig; + } + + @SuppressWarnings("UnusedReturnValue") + public IotThingBizConfigDTO appendItem(String itemName, String itemAlias, Object itemValue) { + if (itemExist(itemName)) { + return this; + } + if (Objects.isNull(configItems)) { + configItems = new ArrayList<>(); + } + configItems.add( + new IotThingBizConfigItemDTO() + .setConfigId(id) + .setItemName(itemName) + .setItemAlias(itemAlias) + .setItemValue(Objects.isNull(itemValue) ? null : itemValue.toString())); + return this; + } + + @SuppressWarnings("SameParameterValue") + protected String getItemValue(String itemName) { + return getItemValue(itemName, v -> v); + } + + /** + * 获取配置项值 + * + * @param itemName 配置项名称(等价于)子类的业务字段名称 + * @param function 类型转换函数,配置项值存储的都是String类型,通过该函数进行转换为业务字段实际类型 此类中应定义完善的转化函数,比如: + * @see #toBigDecimal() + * @return 函数的返回值 + * @param 函数的返回值类型 + */ + protected T getItemValue(String itemName, Function function) { + IotThingBizConfigItemDTO item = getItem(itemName); + return Objects.isNull(item) + ? null + : Objects.isNull(item.getItemValue()) ? null : function.apply(item.getItemValue()); + } + + private IotThingBizConfigItemDTO getItem(String itemName) { + return getConfigMap().get(itemName); + } + + private Map getConfigMap() { + if (CollectionUtils.isEmpty(configItems)) { + return Collections.emptyMap(); + } + if (Objects.nonNull(configItemMap)) { + return configItemMap; + } + configItemMap = + configItems.stream() + .collect( + Collectors.toMap( + IotThingBizConfigItemDTO::getItemName, + Function.identity())); + return configItemMap; + } + + private boolean itemExist(String itemName) { + if (Objects.isNull(configItems)) { + return false; + } + return configItems.stream().anyMatch(c -> Objects.equals(c.getItemName(), itemName)); + } + + /** 字符串转BigDecimal */ + protected Function toBigDecimal() { + return val -> BigDecimal.valueOf(Double.parseDouble(val)); + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigItemDTO.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigItemDTO.java new file mode 100644 index 0000000..e6d1796 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/dto/IotThingBizConfigItemDTO.java @@ -0,0 +1,49 @@ +package com.thing.thing.bizconfig.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Data +@Schema(description = "设备业务配置") +@Accessors(chain = true) +public class IotThingBizConfigItemDTO implements Serializable { + @Serial private static final long serialVersionUID = 1L; + + @Schema(description = "数据主键id") + private Long id; + + @Schema(description = "设备业务配置id") + private Long configId; + + @Schema(description = "配置项名称") + private String itemName; + + @Schema(description = "配置项别名") + private String itemAlias; + + @Schema(description = "配置项值") + private String itemValue; + + @Schema(description = "创建者id") + private Long creator; + + @Schema(description = "创建时间") + private Long createDate; + + @Schema(description = "更新者id") + private Long updater; + + @Schema(description = "更新时间") + private Long updateDate; +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigEntity.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigEntity.java new file mode 100644 index 0000000..929f94a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigEntity.java @@ -0,0 +1,47 @@ +package com.thing.thing.bizconfig.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.List; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_biz_config") +public class IotThingBizConfigEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 物实体id + */ + private Long thingId; + /** + * 物code + */ + private String thingCode; + /** + * 物名称 + */ + private String thingName; + /** + * 配置类型: 暂时只有变压器 + */ + private String type; + + @Column(ignore = true) + private List configItems; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigItemEntity.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigItemEntity.java new file mode 100644 index 0000000..69dc377 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/entity/IotThingBizConfigItemEntity.java @@ -0,0 +1,42 @@ +package com.thing.thing.bizconfig.entity; + +import com.mybatisflex.annotation.Table; + +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_biz_config_item") +public class IotThingBizConfigItemEntity extends BaseDateEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 设备业务配置id + */ + private Long configId; + /** + * 配置项名称 + */ + private String itemName; + /** + * 配置项别名 + */ + private String itemAlias; + /** + * 配置项值 + */ + private String itemValue; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigItemMapper.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigItemMapper.java new file mode 100644 index 0000000..c08398f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigItemMapper.java @@ -0,0 +1,21 @@ +package com.thing.thing.bizconfig.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.bizconfig.entity.IotThingBizConfigItemEntity; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Mapper +public interface IotThingBizConfigItemMapper extends PowerBaseMapper { + + void updateBatchByConfigIdAndName(@Param("configId") Long configId, @Param("items") List items); +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigMapper.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigMapper.java new file mode 100644 index 0000000..0ccda80 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/mapper/IotThingBizConfigMapper.java @@ -0,0 +1,15 @@ +package com.thing.thing.bizconfig.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 设备业务配置 + * + * @author ssy + * @since 3.0 2023-12-18 + */ +@Mapper +public interface IotThingBizConfigMapper extends PowerBaseMapper {} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigItemService.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigItemService.java new file mode 100644 index 0000000..ac46597 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigItemService.java @@ -0,0 +1,20 @@ +package com.thing.thing.bizconfig.service; + +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.bizconfig.entity.IotThingBizConfigItemEntity; + +import java.util.Collection; +import java.util.List; + +/** + * @author siyang + * @date 2024/7/8 14:41 + * @description 设备业务配置 + */ +public interface IotThingBizConfigItemService extends IBaseService { + List findByConfigIds(Collection configIds); + + void deleteByConfigIds(Collection configIds); + + void updateBatchByConfigIdAndName(Long configId, List configItems); +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigService.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigService.java new file mode 100644 index 0000000..485f3d8 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/IotThingBizConfigService.java @@ -0,0 +1,37 @@ +package com.thing.thing.bizconfig.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.bizconfig.dto.IotThingBizConfigDTO; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author siyang + * @date 2024/7/8 14:41 + * @description 设备业务配置 + */ +public interface IotThingBizConfigService extends IBaseService { + PageData handlePage(Map params); + + List handleList(Map params); + + IotThingBizConfigEntity handleDetail(Long id); + + IotThingBizConfigEntity getByThingIdAndType(Long thingId, String type); + + void handleSave(IotThingBizConfigDTO configDTO); + + void handleBatchSave(List configDTOList); + + void handleDelete(Long[] ids); + + void handleDelete(String[] ids); + + void handleDelete(Collection ids); + + void handleUpdate(IotThingBizConfigDTO configDTO); +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigItemServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigItemServiceImpl.java new file mode 100644 index 0000000..35b5c87 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigItemServiceImpl.java @@ -0,0 +1,58 @@ +package com.thing.thing.bizconfig.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.bizconfig.entity.IotThingBizConfigItemEntity; +import com.thing.thing.bizconfig.mapper.IotThingBizConfigItemMapper; +import com.thing.thing.bizconfig.service.IotThingBizConfigItemService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +/** + * @author siyang + * @date 2024/7/8 14:45 + * @description 设备业务配置 + */ +@Slf4j +@Service +public class IotThingBizConfigItemServiceImpl extends BaseServiceImpl implements IotThingBizConfigItemService { + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + @Override + public List findByConfigIds(Collection configIds) { + if (CollectionUtils.isEmpty(configIds)) { + log.warn("查询设备配置项异常,配置Id列表未指定"); + return Collections.emptyList(); + } + return list(QueryWrapper.create().in(IotThingBizConfigItemEntity::getConfigId, configIds)); + } + + @Override + public void deleteByConfigIds(Collection configIds) { + if (CollectionUtils.isEmpty(configIds)) { + return; + } + mapper.deleteByQuery( + new QueryWrapper().in(IotThingBizConfigItemEntity::getConfigId, configIds)); + } + + @Override + public void updateBatchByConfigIdAndName(Long configId, List configItems) { + if(CollectionUtils.isEmpty(configItems)){ + return; + } + List items = ConvertUtils.sourceToTarget(configItems, IotThingBizConfigItemEntity.class); + mapper.updateBatchByConfigIdAndName(configId, items); + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigServiceImpl.java new file mode 100644 index 0000000..6ba856e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/bizconfig/service/impl/IotThingBizConfigServiceImpl.java @@ -0,0 +1,168 @@ +package com.thing.thing.bizconfig.service.impl; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.bizconfig.dto.IotThingBizConfigDTO; +import com.thing.thing.bizconfig.dto.IotThingBizConfigItemDTO; +import com.thing.thing.bizconfig.entity.IotThingBizConfigEntity; +import com.thing.thing.bizconfig.entity.IotThingBizConfigItemEntity; +import com.thing.thing.bizconfig.mapper.IotThingBizConfigMapper; +import com.thing.thing.bizconfig.service.IotThingBizConfigItemService; +import com.thing.thing.bizconfig.service.IotThingBizConfigService; + +import lombok.RequiredArgsConstructor; + +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author siyang + * @date 2024/7/8 14:47 + * @description 设备业务配置 + */ +@Service +@RequiredArgsConstructor +public class IotThingBizConfigServiceImpl extends BaseServiceImpl implements IotThingBizConfigService { + private final IotThingBizConfigItemService iotThingBizConfigItemService; + + @Override + public QueryWrapper getWrapper(Map params) { + String type = MapUtils.getString(params, "type"); + Long thingId = MapUtils.getLong(params, "thingId"); + String thingCode = MapUtils.getString(params, "thingCode"); + String thingName = MapUtils.getString(params, "thingName"); + Long tenantCode = UserContext.getTenantCode(); + + return QueryWrapper.create() + .eq(IotThingBizConfigEntity::getType, type, StringUtils::isNotBlank) + .eq(IotThingBizConfigEntity::getThingId, thingId, Objects::nonNull) + .eq(IotThingBizConfigEntity::getThingCode, thingCode, StringUtils::isNotBlank) + .eq(IotThingBizConfigEntity::getThingName, thingName, StringUtils::isNotBlank) + .eq(IotThingBizConfigEntity::getTenantCode, tenantCode); + } + + @Override + public PageData handlePage(Map params) { + PageData pageData = getPageData(params, IotThingBizConfigEntity.class); + List list = pageData.getList(); + fillItemInfo(list); + return new PageData<>(list, pageData.getTotal()); + } + + @Override + public List handleList(Map params) { + List list = list(getWrapper(params)); + fillItemInfo(list); + return list; + } + + @Override + public IotThingBizConfigEntity handleDetail(Long id) { + IotThingBizConfigEntity config = getById(id); + fillItemInfo(List.of(config)); + return config; + } + + @Override + public IotThingBizConfigEntity getByThingIdAndType(Long thingId, String type) { + IotThingBizConfigEntity config = + getOne( + QueryWrapper.create() + .eq(IotThingBizConfigEntity::getThingId, thingId) + .eq(IotThingBizConfigEntity::getType, type) + .limit(1)); + if (Objects.nonNull(config)) { + fillItemInfo(List.of(config)); + } + return config; + } + + @Override + public void handleSave(IotThingBizConfigDTO configDTO) { + configDTO.generateItems(); + IotThingBizConfigEntity configEntity = getByThingIdAndType(Long.parseLong(configDTO.getThingId()), configDTO.getType()); + if (Objects.nonNull(configEntity)) { + configEntity.setConfigItems( + ConvertUtils.sourceToTarget( + configDTO.getConfigItems(), IotThingBizConfigItemEntity.class)); + handleUpdate(configEntity); + return; + } + IotThingBizConfigEntity entity = ConvertUtils.sourceToTarget(configDTO, IotThingBizConfigEntity.class); + entity.setThingId(Long.parseLong(configDTO.getThingId())); + mapper.insert(entity); + Long configId = entity.getId(); + List configItems = configDTO.getConfigItems(); + List itemEntities = + configItems.stream() + .map( + item -> + ConvertUtils.sourceToTarget( + item, IotThingBizConfigItemEntity.class)) + .filter(Objects::nonNull) + .peek(item -> item.setConfigId(configId)) + .collect(Collectors.toList()); + iotThingBizConfigItemService.saveBatch(itemEntities); + } + + @Override + public void handleBatchSave(List configDTOList) { + configDTOList.forEach(this::handleSave); + } + + @Override + public void handleDelete(Long[] ids) { + List configIds = Arrays.asList(ids); + handleDelete(configIds); + } + + @Override + public void handleDelete(String[] ids) { + if (Objects.isNull(ids) || ids.length == 0) { + return; + } + Long[] configIds = + Arrays.stream(ids) + .mapToLong(Long::parseLong) + .boxed() + .toList() + .toArray(new Long[] {}); + handleDelete(configIds); + } + + @Override + public void handleDelete(Collection ids) { + mapper.deleteBatchByIds(ids); + iotThingBizConfigItemService.deleteByConfigIds(ids); + } + + @Override + public void handleUpdate(IotThingBizConfigDTO configDTO) { + configDTO.generateItems(); + updateDto(configDTO); + iotThingBizConfigItemService.updateBatchByConfigIdAndName( + configDTO.getId(), + ConvertUtils.sourceToTarget( + configDTO.getConfigItems(), IotThingBizConfigItemEntity.class)); + } + + private void handleUpdate(IotThingBizConfigEntity configEntity) { + iotThingBizConfigItemService.updateBatchByConfigIdAndName(configEntity.getId(), configEntity.getConfigItems()); + } + + public void fillItemInfo(List list) { + Set configIds = list.stream().map(IotThingBizConfigEntity::getId).collect(Collectors.toSet()); + List items = iotThingBizConfigItemService.findByConfigIds(configIds); + Map> configItemGroup = + items.stream() + .collect(Collectors.groupingBy(IotThingBizConfigItemEntity::getConfigId)); + list.forEach(c -> c.setConfigItems(configItemGroup.get(c.getId()))); + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/cache/service/CacheCallback.java b/modules/thing/src/main/java/com/thing/thing/cache/service/CacheCallback.java new file mode 100644 index 0000000..2f2a7ab --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/cache/service/CacheCallback.java @@ -0,0 +1,53 @@ +package com.thing.thing.cache.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.Result; +import com.thing.transport.api.TransportServiceCallback; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author ls + **/ +public interface CacheCallback { + + void onSuccess(T result, U additionalInfo); + + void onError(Throwable e); + + CacheCallback EMPTY = new CacheCallback() { + @Override + public void onSuccess(Void result, Void additionalInfo) { + + } + + @Override + public void onError(Throwable e) { + + } + }; + + + +/* final ConcurrentHashMap> thingMap; + + private final CacheInit cacheInit; + + public CacheCallback(ConcurrentHashMap> thingMap,CacheInit cacheInit) { + this.thingMap = thingMap; + this.cacheInit = cacheInit; + }*/ + + +// @Override +// public void onSuccess(T msg) { +// cacheInit.destroy(); +// +// thingMap.set(new Result()); +// } +// +// @Override +// public void onError(Throwable e) { +// thingMap.setResult(new Result().error(e.getMessage())); +// } +} diff --git a/modules/thing/src/main/java/com/thing/thing/cache/service/CacheInit.java b/modules/thing/src/main/java/com/thing/thing/cache/service/CacheInit.java new file mode 100644 index 0000000..8f863b5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/cache/service/CacheInit.java @@ -0,0 +1,180 @@ +package com.thing.thing.cache.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.model.service.IotThingModelService; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.PreDestroy; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Slf4j +@Component +public class CacheInit { + + private final ThingCache thingCache; + private final IotThingModelService modelService; + private final IotThingEntityService entityService; + private final IotThingDictRelationService dictRelationService; + private final IotThingRelationRootService relationRootService; + private final IotThingRelationDetailService relationDetailService; + + protected volatile ScheduledExecutorService notificationsConsumerExecutor = Executors.newSingleThreadScheduledExecutor(); + + public CacheInit(ThingCache thingCache, IotThingModelService modelService, IotThingEntityService entityService, IotThingDictRelationService dictRelationService, IotThingRelationRootService relationRootService, IotThingRelationDetailService relationDetailService) { + this.thingCache = thingCache; + this.modelService = modelService; + this.entityService = entityService; + this.dictRelationService = dictRelationService; + this.relationRootService = relationRootService; + this.relationDetailService = relationDetailService; + } + + @PostConstruct + public void initCache() { + log.info("thing 【物模型:物实体:物指标】 cache init start"); + //物模型的初始化 + cacheModel(); + //定时打印缓存 + // notificationsConsumerExecutor.scheduleAtFixedRate(thingCache::printStats, 0, 3000, TimeUnit.SECONDS); + log.info("thing 【物模型:物实体:物指标】 cache init end"); + } + + private void cacheModel() { + modelService.findList(CacheNameEnum.ModelField.THING_MODEL_CREATE_DATE.getField(), + Constant.DESC, null, null, null, null, null, null, null); + //物实体的初始化 + cacheEntity(); + } + + private void cacheEntity() { + entityService.findList(CacheNameEnum.EntityField.THING_ENTITY_CREATE_DATE.getField(), Constant.DESC, null,null, null, + null, null, null, null, null); + cacheDictRelation(); + cacheRelation(); + cacheRelationDetail(); + } + + private void cacheDictRelation() { + dictRelationService.findList(CacheNameEnum.DictField.THING_DICT_RELATION_SORT.getField(), Constant.DESC, + null, null, null, null, null, null, null); + } + + private void cacheRelation() { + //根关系缓存 + relationRootService.findList(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_SORT.getField(), + Constant.DESC, null, null, null,null); + } + + //关系详情缓存 + private void cacheRelationDetail() { + relationDetailService.findList(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField(), Constant.DESC, null, null, null, null); + } + + public static void relationDetailMap(List list,ThingCache thingCache) { + if(CollectionUtils.isEmpty(list)){ + return; + } + Map relationDetailMap = list.stream().collect(Collectors.toMap( + r -> r.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asText() + + ":" + r.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ID.getField()).asText() + + ":" + r.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asText(), + Function.identity() + )); + thingCache.putMap(CacheNameEnum.THING_DETAIL_RELATION, relationDetailMap); + } + + public static void relationRootMap(List list,ThingCache thingCache) { + if(CollectionUtils.isEmpty(list)){ + return; + } + Map rootMap = list.stream().collect(Collectors.toMap( + r -> r.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField()).asText() + + ":" + r.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField()).asText() + + ":" + r.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asText(), + r -> { + r.put("fromId", "0"); + r.put("rootId", r.get("id").asText()); + r.put("toId", r.get("id").asText()); + r.put("toName", r.get("name").asText()); + r.put("rootThingId", "0"); + return r; + } + )); + thingCache.putMap(CacheNameEnum.THING_ROOT_RELATION, rootMap); + } + + public static void dictRelationMap(List dictList,ThingCache thingCache) { + if (CollectionUtils.isEmpty(dictList)) { + return; + } + List entityDTOList = thingCache.getTopicMap(CacheNameEnum.THING_ENTITY); + Map dictMap = dictList.stream() + .filter(dictRelationDTO -> + entityDTOList.stream().anyMatch(e -> + Objects.equals(e.get(CacheNameEnum.EntityField.THING_ENTITY_ID.getField()).asLong() + , dictRelationDTO.get(CacheNameEnum.DictField.THING_DICT_ENTITY_ID.getField()).asLong()))) + .collect(Collectors.toMap( + d -> { + Optional first = entityDTOList.stream().filter(e -> + Objects.equals(e.get(CacheNameEnum.EntityField.THING_ENTITY_ID.getField()).asLong(), + d.get(CacheNameEnum.DictField.THING_DICT_ENTITY_ID.getField()).asLong())) + .findFirst(); + return first.get().get(CacheNameEnum.EntityField.THING_ENTITY_TENANT_CODE.getField()).asText() + + ":" + first.get().get(CacheNameEnum.EntityField.THING_ENTITY_CODE.getField()).asText() + + ":" + d.get(CacheNameEnum.DictField.THING_DICT_CODE.getField()).asText() + + ":" + d.get(CacheNameEnum.DictField.THING_DICT_ID.getField()).asText(); + }, Function.identity())); + thingCache.putMap(CacheNameEnum.THING_DICT_RELATION, dictMap); + } + public static void entityMap(List entityList,ThingCache thingCache) { + if (CollectionUtils.isEmpty(entityList)) { + return; + } + Map entityMap = entityList.stream() + .filter(entityDTO -> entityDTO.has(CacheNameEnum.EntityField.THING_ENTITY_TENANT_CODE.getField()) + && !Objects.isNull(entityDTO.get(CacheNameEnum.EntityField.THING_ENTITY_TENANT_CODE.getField()).asLong())) + .collect(Collectors.toMap( + entityDTO -> entityDTO.get(CacheNameEnum.EntityField.THING_ENTITY_TENANT_CODE.getField()).asText() + + ":" + entityDTO.get(CacheNameEnum.EntityField.THING_ENTITY_CODE.getField()).asText() + + ":" + entityDTO.get(CacheNameEnum.EntityField.THING_ENTITY_ID.getField()).asText() + , + Function.identity() + )); + thingCache.putMap(CacheNameEnum.THING_ENTITY, entityMap); + } + + public static void modelMap(List modelList,ThingCache thingCache) { + if (CollectionUtils.isEmpty(modelList)) { + return; + } + Map modelMap = modelList.stream().collect(Collectors.toMap( + model -> model.get(CacheNameEnum.ModelField.THING_MODEL_CODE.getField()).asText() + + ":" + model.get(CacheNameEnum.ModelField.THING_MODEL_ID.getField()).asText() + ,Function.identity())); + thingCache.putMap(CacheNameEnum.THING_MODEL, modelMap); + } + + @PreDestroy + public void destroy() { + thingCache.clear(); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/cache/service/ThingCache.java b/modules/thing/src/main/java/com/thing/thing/cache/service/ThingCache.java new file mode 100644 index 0000000..b816449 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/cache/service/ThingCache.java @@ -0,0 +1,169 @@ +package com.thing.thing.cache.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +@Slf4j +@Component +public final class ThingCache { + + /** key 为 topic: code - JsonObject 为 模型 */ + private final ConcurrentHashMap> thingMap = new ConcurrentHashMap<>(); + + public void putMap(String topic, Map map) { + if (!map.isEmpty()) { + thingMap.computeIfAbsent(topic, k -> new ConcurrentHashMap<>()).putAll(map); + } + } + + public List getTopicMap(String topic) { + ConcurrentHashMap map = thingMap.get(topic); + if (map == null || map.isEmpty()) { + return List.of(); + } + return map.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public List findTopicMap(String topic, String key) { + ConcurrentHashMap map = thingMap.get(topic); + if (map == null || map.isEmpty()) { + return List.of(); + } + return map.entrySet().stream().sorted(Map.Entry.comparingByKey()).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public List getTopicMap(String topic, String key) { + ConcurrentHashMap map = thingMap.get(topic); + if (map == null || map.isEmpty()) { + return List.of(); + } + return map.entrySet().stream() + .filter(entry -> StringUtils.contains(entry.getKey(), key)) + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .toList(); + } + + public List getTopicMap(String topic, List keys) { + ConcurrentHashMap map = thingMap.get(topic); + if (map == null || map.isEmpty()) { + return List.of(); + } + return map.entrySet().stream() + .filter(entry -> keys.stream().anyMatch(key-> StringUtils.contains(entry.getKey(), key))) + .sorted(Map.Entry.comparingByKey()) + .map(Map.Entry::getValue) + .toList(); + } + + public List getTopicMap(String topic, int size) { + Map map = thingMap.get(topic); + if (map == null || map.isEmpty() || size <= 0) { + return List.of(); + } + return map.entrySet().stream().sorted(Map.Entry.comparingByKey()).limit(size).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public Map getMap(String topic, int size) { + Map map = thingMap.get(topic); + if (map == null || map.isEmpty() || size <= 0) { + return Map.of(); + } + return map.entrySet().stream().sorted(Map.Entry.comparingByKey()).limit(size).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } + + public List findKeyMap(String topic, String key) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + return null; + } + return valMap.entrySet().stream().filter(entry -> entry.getKey().contains(key)).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public ObjectNode findObjectNode(String topic, String key) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + return null; + } + Optional first = valMap.entrySet().stream().filter(entry -> entry.getKey().contains(key)).map(Map.Entry::getValue).findFirst(); + return first.orElse(null); + } + + public void deleteKeyMap(String topic, String key) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + return ; + } + valMap.remove(key); + } + + public void clearTopic(String topic) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isNotEmpty(valMap)){ + valMap.clear(); + } + } + + public void deleteAllKeyMap(String topic, Collection key) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + return ; + } + key.forEach(d-> { + List keyList = valMap.keySet().stream().filter(k -> k.contains(d)).toList(); + keyList.forEach(valMap::remove); + }); + } + + public void updateKeyMap(String topic, String key,ObjectNode value) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + valMap = new ConcurrentHashMap<>(); + } + //直接利用key覆盖 + valMap.put(key,value); + } + + public List findAllKeyMap(String topic, Collection key) { + ConcurrentHashMap valMap = thingMap.get(topic); + if(MapUtils.isEmpty(valMap)){ + return null; + } + return valMap.entrySet().stream().filter(entry -> + key.stream().anyMatch(k -> entry.getKey().contains(k)) + ).map(Map.Entry::getValue).collect(Collectors.toList()); + } + + public ObjectNode getKeyMap(String topic, String key) { + ConcurrentHashMap map = thingMap.get(topic); + if (map == null || map.isEmpty()) { + return null; + } + return thingMap.get(topic).get(key); + } + + public int getTopicSize(String topic) { + return thingMap.get(topic).size(); + } + + public void printStats() { + thingMap.forEach((topic, queue) -> { + log.info("---dataMap-内存主题:{}----->数据量:{}------{}", topic, queue.size(), queue); + }); + } + + public void clear() { + thingMap.clear(); + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/cache/service/ThingModelCacheService.java b/modules/thing/src/main/java/com/thing/thing/cache/service/ThingModelCacheService.java new file mode 100644 index 0000000..9359c11 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/cache/service/ThingModelCacheService.java @@ -0,0 +1,20 @@ +package com.thing.thing.cache.service; + +import java.util.Set; + +/** + * @author SiYang + * @date 2024/01/19 16:16 + * @description + */ +public interface ThingModelCacheService { + + Set getThingCodes(); + + void put(String thingCode); + + Set getAuthThingCodes(Long tenantCode); + + void putAuthThingCodes(Long tenantCode, Set thingCodes); + +} diff --git a/modules/thing/src/main/java/com/thing/thing/cache/service/impl/ThingModelCacheServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/cache/service/impl/ThingModelCacheServiceImpl.java new file mode 100644 index 0000000..c8e5d22 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/cache/service/impl/ThingModelCacheServiceImpl.java @@ -0,0 +1,65 @@ +package com.thing.thing.cache.service.impl; + +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.cache.service.AbstractCacheService; +import com.thing.thing.cache.service.ThingModelCacheService; +import com.thing.thing.context.service.ThingManageContextService; +import jakarta.annotation.Resource; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ThingModelCacheServiceImpl extends AbstractCacheService + implements ThingModelCacheService { + + + + + @Resource + private ThingManageContextService thingManageContextService; + + + @Override + public String getCacheName() { + return CacheNameEnum.THING_MODEL + ", " + CacheNameEnum.THING_ENTITY; + } + + @Override + public void initCache() { + thingManageContextService.getAllModelCodes(); + thingManageContextService.getAuthThingEntityMap(); + } + + @Override + public void put(String thingCode) { + Set cacheCodes = thingManageContextService.getAllModelCodes(); + thingManageContextService.putModelCodeToCache(thingCode, cacheCodes); + } + + @Override + public Set getAuthThingCodes(Long tenantCode) { + return Optional.ofNullable(thingManageContextService.getAuthThingEntityMap().get(tenantCode)) + .orElse(new HashSet<>()); + } + + @Override + public void putAuthThingCodes(Long tenantCode, Set thingCodes) { + Map> authThingMap = thingManageContextService.getAuthThingEntityMap(); + thingManageContextService.putAuthThingEntityMap(tenantCode, thingCodes, authThingMap); + } + + @Override + public Set getThingCodes() { + return thingManageContextService.getAllModelCodes(); + } + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/common/dto/BasicsDTO.java b/modules/thing/src/main/java/com/thing/thing/common/dto/BasicsDTO.java new file mode 100644 index 0000000..031b4cc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/common/dto/BasicsDTO.java @@ -0,0 +1,18 @@ +package com.thing.thing.common.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import java.io.Serializable; + +public abstract class BasicsDTO implements Serializable { + + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/context/controller/ThingManageContextController.java b/modules/thing/src/main/java/com/thing/thing/context/controller/ThingManageContextController.java new file mode 100644 index 0000000..36963a4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/context/controller/ThingManageContextController.java @@ -0,0 +1,37 @@ +package com.thing.thing.context.controller; + +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.web.response.Result; +import com.thing.thing.context.service.ThingManageContextService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; + +/** + * 物模型上下文 + * + * @author xc + * @since 3.0 2024-03-18 + */ +@RestController +@RequestMapping("v2/manage") +@Tag(name = "物管理上下文") +public class ThingManageContextController { + + @Resource + private ThingManageContextService service; + + @DeleteMapping("/clearCache/{cacheName}") + @Operation(summary = "删除") + @LogOperation("删除") + public Result clearCache(@PathVariable String cacheName) { + return new Result<>().ok(service.clearCache(cacheName)); + } + + @DeleteMapping("/clearCache") + public Result clearCache() { + return new Result<>().ok(service.clearCache()); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/context/service/ThingManageContextService.java b/modules/thing/src/main/java/com/thing/thing/context/service/ThingManageContextService.java new file mode 100644 index 0000000..15d9341 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/context/service/ThingManageContextService.java @@ -0,0 +1,146 @@ +package com.thing.thing.context.service; + + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityDictDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.model.dto.IotThingModelDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; + +import java.util.*; + +public interface ThingManageContextService { + + /** 查询 Model 接口 **/ + Optional> findAllModel(String orderField, String order, + String code, String token,String origin,String status,String realType,Long startTime,Long endTime); + + Optional> findModelByCodes(Collection codes); + + Optional> findModelByGateway(String gateway); + + void saveModelBatch(List models); + + /** 查询 Entity 接口 **/ + + Optional findEntityById(Long id); + + Optional> findEntityAllById(Collection ids); + + Optional> findEntityByCondition(List ids, + List types, + List tags, + List name, + List code, + List group, + List relation, + Long tenantCode, + boolean isEntity); + + Optional findEntityByCode(String code,Long tenantCode,boolean isEntity); + + Optional findEntityByThingList(Long thingId, Long realTenantCode, boolean isEntity); + + Optional> findEntityAllByCode(Collection codes,Long tenantCode,boolean isEntity); + + Optional> findEntityAllByCodeAndTenantCode(Collection codes,Long tenantCode,boolean isEntity); + + Optional> findViewAllEntity(String orderField,String order, + String name,String type,Long tenantCode,Long deptId,String realType,String tags,String enableStatus,String templateMark); + + Optional findViewEntityById(Long id); + + Optional> findViewEntityAllByIds(List ids); + + Optional findViewByCode(String code, Long tenantCode, boolean isEntity); + + /** 查询 Dict 接口 **/ + + Optional findDictByCode(String code); + + Optional> findDictByGroupNames(List groupNames); + + Optional> findDictByCodes(Collection code); + + Optional> findDictByGroupNameAndCode(String groupName,Collection code); + + /** 查询 DictRelation 接口 **/ + Optional> findDictRelationAllByIds(Collection ids); + + Optional> findDictRelationAllByEntityIdsAndGroupNamesAndCodes(Collection entityIds,Collection groupNames,Collection codes); + + Optional findDictRelationByEntityIdAndCode(Long entityId, String code); + + Optional> findDictRelationAllByEntityIdAndCodes(Long entityId, Collection codes); + + Optional> findDictRelationAllByEntityIdsAndCodes(Collection entityIds, Collection codes); + + /** 查询 root 接口 **/ + + /** 查询 rootDetail 接口 **/ + + /** 兄弟节点 **/ + Optional> findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(Long rootId,Long toId,Long rootThingId); + /** 子节点 **/ + Optional> findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(Long rootId, Long fromId, Long rootThingId); + /** 父节点 **/ + Optional findRootDetailParentNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId); + + Optional> findRootDetailAllByRootId(Long rootId); + + Optional> findRootDetailAllByFromIds(Collection fromIds); + + Optional> findRootDetailTreeList(List rootIds); + + Optional> findRootDetailSortTreeList(Long rootId, Long rootThingId); + +// List findRootDetailAllByRootIdsAndCodes(Collection rootIds, Collection codes); + + + /** 插入接口 **/ + + void saveModel(List codes); + + void saveOneModel(IotThingModelEntity entity); + + void saveAllModel(List entities); + + void saveEntity(List entities); + + +// void insertBatchModel(List entities); + + void batchSaveOrUpdateEntity(List collect); + + + /** 更新接口 **/ + + void updateModelAuthNumByCodes(List codes); + + + /** 缓存接口 **/ + Set getAllModelCodes(); + + Map> getAuthThingEntityMap(); + + Set putModelCodeToCache(String thingCode, Set cacheCodes); + + Map> putAuthThingEntityMap(Long tenantCode, Set thingCodes, Map> authThingMap); + + /** 清除特定缓存 **/ + + String clearCache(); + + String clearCache(String cacheName); + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/context/service/impl/ThingManageContextServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/context/service/impl/ThingManageContextServiceImpl.java new file mode 100644 index 0000000..6de1fb0 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/context/service/impl/ThingManageContextServiceImpl.java @@ -0,0 +1,330 @@ +package com.thing.thing.context.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.enumeration.GateWayStatus; +import com.thing.common.core.enumeration.QueueOriginType; +import com.thing.common.core.enumeration.ThingStatus; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.TokenGenerator; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.service.IotThingDictService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityDictDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.entity.dto.IotThingViewDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.model.dto.IotThingModelDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.thing.model.service.IotThingModelService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class ThingManageContextServiceImpl implements ThingManageContextService { + + @Resource + private IotThingModelService modelService; + + @Resource + private IotThingEntityService entityService; + + @Resource + private IotThingDictService dictService; + + @Resource + private IotThingDictRelationService dictRelationService; + +// @Resource +// private IotThingRelationRootService relationRootService; + + @Resource + private IotThingRelationDetailService relationDetailService; + + @Resource + private CacheManager cacheManager; + + @Override + public Optional> findAllModel(String orderField, String order, + String code, String token,String origin,String status,String realType,Long startTime,Long endTime) { + return Optional.ofNullable(modelService.findList(orderField,order,code,token,origin,status,realType,startTime,endTime)); + } + + @Override + public Optional> findModelByCodes(Collection codes) { + return Optional.ofNullable(modelService.findByCodeIn(codes)); + } + + @Override + public Optional> findModelByGateway(String gateway) { + return modelService.findByGateway(gateway); + } + + @Override + public void saveModelBatch(List models) { + List entities = ConvertUtils.sourceToTarget(models, IotThingModelEntity.class); + modelService.batchSaveOrUpdate(entities); + } + + @Override + public Optional findEntityById(Long id) { + return Optional.ofNullable(entityService.findEntityById(id)); + } + + @Override + public Optional> findViewAllEntity(String orderField,String order, + String name,String type,Long tenantCode,Long deptId,String realType,String tags,String enableStatus,String templateMark) { + return Optional.ofNullable(entityService.findList(orderField,order,name,type,tenantCode,deptId,realType,tags,enableStatus,templateMark)); + } + + @Override + public Optional findViewEntityById(Long id) { + return Optional.ofNullable(entityService.findById(id)); + } + + @Override + public Optional> findViewEntityAllByIds(List ids) { + if(CollectionUtils.size(ids) == 1){ + return Optional.of(Collections.singletonList(entityService.findById(ids.get(0)))); + } + return Optional.ofNullable(entityService.findByIds(ids)); + } + + @Override + public Optional> findEntityAllById(Collection ids) { + List iotThingEntities = entityService.listByIds(ids); + return Optional.ofNullable(ConvertUtils.sourceToTarget(iotThingEntities,IotThingEntityDTO.class)); + } + + @Override + public Optional> findEntityByCondition(List ids, List types, List tags, List name, + List code, List group, List relation, Long tenantCode, + boolean isEntity) { + return Optional.ofNullable(entityService.findEntityByCondition(ids,types,tags,name,code,group,relation,tenantCode,isEntity)); + } + + @Override + public Optional findEntityByCode(String code, Long tenantCode,boolean isEntity) { + return entityService.findEntityByCode(code,tenantCode,isEntity); + } + + @Override + public Optional findEntityByThingList(Long thingId, Long tenantCode, boolean isEntity) { + return entityService.findEntityByThingList(thingId,tenantCode,isEntity); + } + + @Override + public Optional> findEntityAllByCode(Collection codes,Long tenantCode,boolean isEntity) { + return entityService.findByCodes(codes,tenantCode,isEntity); + } + + @Override + public Optional> findEntityAllByCodeAndTenantCode(Collection codes, Long tenantCode, boolean isEntity) { + return Optional.ofNullable(entityService.findEntityAllByCode(codes,tenantCode,isEntity)); + } + + @Override + public Optional findViewByCode(String code, Long tenantCode, boolean isEntity) { + return entityService.findByCode(code, tenantCode,isEntity); + } + + @Override + public Optional> findDictRelationAllByIds(Collection ids) { + return Optional.ofNullable(dictRelationService.findAllByIdsAndEntityIds(new ArrayList<>(ids), null)); + } + + @Override + public Optional> findDictRelationAllByEntityIdsAndGroupNamesAndCodes(Collection entityIds, Collection groupNames, Collection codes) { + return Optional.ofNullable(dictRelationService.findDictRelationAllByEntityIdsAndGroupNamesAndCodes(entityIds, groupNames, codes)); + } + + @Override + public Optional findDictRelationByEntityIdAndCode(Long entityId, String code) { + return Optional.ofNullable(dictRelationService.findDictRelationByEntityIdAndCode(entityId,code)); + } + + + @Override + public Optional> findDictRelationAllByEntityIdAndCodes(Long entityId, Collection codes) { + return Optional.ofNullable(dictRelationService.findAllByEntityIdAndCodes(entityId,codes)); + } + + @Override + public Optional> findDictRelationAllByEntityIdsAndCodes(Collection entityIds, Collection codes) { + return Optional.ofNullable(dictRelationService.findAllByEntityIdsAndCodes(entityIds,codes)); + } + + @Override + public Optional> findRootDetailAllByRootId(Long rootId) { + return Optional.ofNullable(relationDetailService.findByRootId(rootId)); + } + + @Override + public Optional> findRootDetailAllByFromIds(Collection fromIds) { + return Optional.ofNullable(relationDetailService.findRelationAllByFromIds(fromIds)); + } + + @Override + public Optional> findRootDetailTreeList(List rootIds) { + return Optional.ofNullable(relationDetailService.findTreeList(rootIds)); + } + + @Override + public Optional> findRootDetailSortTreeList(Long rootId, Long rootThingId) { + return Optional.ofNullable(relationDetailService.findRootDetailSortTreeList(rootId,rootThingId)); + } + + @Override + public Optional> findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(Long rootId,Long toId,Long rootThingId) { + return Optional.ofNullable(relationDetailService.findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(rootId,toId,rootThingId)); + } + + @Override + public Optional> findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(Long rootId, Long fromId, Long rootThingId) { + return Optional.ofNullable(relationDetailService.findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(rootId,fromId,rootThingId)); + } + + @Override + public Optional findRootDetailParentNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId) { + return Optional.ofNullable(relationDetailService.findRootDetailParentNodeByRootIdAndToIdAndRootThingId(rootId,toId,rootThingId)); + } + + @Override + public Optional findDictByCode(String code) { + return Optional.ofNullable(dictService.findDictByCode(code)); + } + + @Override + public Optional> findDictByGroupNames(List groupNames) { + return Optional.ofNullable(dictService.findDictByGroupNames(groupNames)); + } + + @Override + public Optional> findDictByCodes(Collection code) { + return Optional.ofNullable(dictService.findDictByCodes(code)); + } + + @Override + public Optional> findDictByGroupNameAndCode(String groupName, Collection code) { + return Optional.ofNullable(dictService.findDictByGroupNameAndCodes(groupName,code)); + } + + @Override + public void saveModel(List codes) { + List list = codes.stream().distinct().map(code -> new IotThingModelEntity() + .setCode(code) + .setToken(TokenGenerator.generateValue()) + .setGateway(GateWayStatus.NO_GATE_WAY.getValue()) + .setStatus(ThingStatus.NOT_CONNECTED.getCode()) + .setOrigin(QueueOriginType.MQTT_CLIENT.name()) + .setStatusTs(DateTimeUtils.getCurrentTime()) + ).toList(); + modelService.saveBatch(list); + } + + @Override + public void saveOneModel(IotThingModelEntity entity) { + modelService.save(entity); + } + + @Override + public void saveAllModel(List entities) { + modelService.batchSaveOrUpdate(entities); + } + + @Override + public void saveEntity(List entities) { + entityService.saveBatch(entities); + } + + @Override + public void batchSaveOrUpdateEntity(List collect) { + if (CollectionUtil.isEmpty(collect)) { + return; + } + entityService.batchSaveOrUpdate(collect); + + } + + @Override + public void updateModelAuthNumByCodes(List codes){ + modelService.updateModelAuthNumByCodes(codes); + } + + @Override + @SuppressWarnings("unchecked") + @Cacheable(value = CacheNameEnum.THING_MODEL, key = "'allModelCode'") + public Set getAllModelCodes() { + return modelService.getMapper() + .selectListByQuery(QueryWrapper.create().select(IotThingModelEntity::getCode)) + .stream() + .map(IotThingModelEntity::getCode) + .collect(Collectors.toSet()); + } + + @Override + @Cacheable(value = CacheNameEnum.AUTH_THING_CODES, key = "'allEntity'") + public Map> getAuthThingEntityMap() { + List things = entityService.list(); + return things.stream().filter(thing -> Objects.nonNull(thing.getTenantCode())) + .collect( + Collectors.groupingBy( + IotThingEntity::getTenantCode, + Collectors.mapping(IotThingEntity::getCode, Collectors.toSet()))); + } + + @Override + @CachePut(value = CacheNameEnum.THING_MODEL, key = "'allModelCode'") + public Set putModelCodeToCache(String thingCode, Set cacheCodes) { + cacheCodes.add(thingCode); + return cacheCodes; + } + + @Override + @CachePut(value = CacheNameEnum.THING_MODEL, key = "'allEntity'") + public Map> putAuthThingEntityMap(Long tenantCode, Set thingCodes, Map> authThingMap) { + authThingMap.computeIfAbsent(tenantCode, k -> new HashSet<>()).addAll(thingCodes); + return authThingMap; + } + + @Override + public String clearCache() { + Collection cacheNames = cacheManager.getCacheNames(); + for (String cacheName : cacheNames) { + Cache cache = cacheManager.getCache(cacheName); + if (cache != null) { + cache.clear(); + } + } + return "All caches cleared successfully."; + } + + @Override + public String clearCache(String cacheName) { + cacheManager.getCache(cacheName).clear(); + return "Cache " + cacheName + " cleared successfully."; + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/dict/controller/IotThingDictController.java b/modules/thing/src/main/java/com/thing/thing/dict/controller/IotThingDictController.java new file mode 100644 index 0000000..836bb32 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/controller/IotThingDictController.java @@ -0,0 +1,172 @@ +package com.thing.thing.dict.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.excel.IotThingDictExcel; +import com.thing.thing.dict.service.IotThingDictService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.lang3.ArrayUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@RestController +@RequestMapping("v2/dict") +@Tag(name="物字典信息表") +public class IotThingDictController { + + @Resource + private IotThingDictService service; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@Parameter(hidden = true) @RequestParam Map params){ + PageData page = service.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> list(@Parameter(hidden = true) @RequestParam Map params){ + List list = service.findAll(params); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingDictDTO data = service.findById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingDictDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.saveDict(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingDictDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.updateDict(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDeleteById(ids); + return new Result<>(); + } + + + @GetMapping("unit") + @Operation(summary="物字典移动分组") + public Result> unit() { + return new Result>().ok(service.unit()); + } + + + @GetMapping("getMaxSort") + @Operation(summary="获取最大类型的排序信息") + public Result getMaxSort(String type) { + return new Result().ok(service.findMaxSort(type)); + } + + + @GetMapping("group") + @Operation(summary="组名称集合") + public Result> groups() { + return new Result>().ok(service.groups()); + } + + @GetMapping("type") + @Operation(summary="字典类型集合") + public Result>> type() { + return new Result>>().ok(service.type()); + } + + @PostMapping("copy") + @Operation(summary="物字典移动分组") + public Result copy(@Validated @RequestBody IotThingDictDTO dto) { + service.copy(dto.getGroupNames(), dto.getIds()); + return new Result<>(); + } + + @PostMapping("template") + @Operation(summary="物字典模板") + @LogOperation("物字典模板下载") + public void template(HttpServletResponse response) throws Exception { + ExcelUtils.exportExcel(Lists.newArrayList(), "物字典", "字典列表", IotThingDictExcel.class, "物字典.xls", response); + } + + @PostMapping("import") + @Operation(summary="导入") + public Result importExcel(MultipartFile file, HttpServletRequest request) { + service.importExcel(file,request); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + if(ArrayUtils.isNotEmpty(ids)){ + params.put("ids", Arrays.asList(ids)); + } + List list = service.findAll(params); + ExcelUtils.exportExcel(ConvertUtils.sourceToTarget(list,IotThingDictExcel.class), "物字典", "字典列表", IotThingDictExcel.class, "物字典.xls", response); + } + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictDTO.java b/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictDTO.java new file mode 100644 index 0000000..d026cc9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictDTO.java @@ -0,0 +1,74 @@ +package com.thing.thing.dict.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@Data +@Schema(description = "物字典信息表") +public class IotThingDictDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private Long id; + @Schema(description = "字典编号") + private String code; + @Schema(description = "字典名称") + private String name; + @Schema(description = "字典单位") + private String unit; + @Schema(description = "频率(s)") + private Integer rate; + @Schema(description = "颜色") + private String color; + @Schema(description = "数据类型: instant - 瞬时 ;increase - 递增 ; region - 区间; control - 控制 ;alarm - 告警") + private String dataType; + private String typeName; + @Schema(description = "图标") + private String url; + @Schema(description = "备注") + private String remark; + @Schema(description = "扩展字段") + private String extendData; + @Schema(description = "是否系统字典:0-默认系统 1-自定义") + private Integer isDefault; + @Schema(description = "排序") + private Long sort; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "监控分析页面默认配置json串") + private String businessType; + @Schema(description = "组名称") + private String groupName; + @Schema(description = "能源品种类型") + private String evType; + @Schema(description = "最大值") + private BigDecimal maxNum; + @Schema(description = "最小值") + private BigDecimal minNum; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "组名称集合-copy") + private List groupNames; + @Schema(description = "字典主键集合-copy") + private List ids; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictRelationListDTO.java b/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictRelationListDTO.java new file mode 100644 index 0000000..079647a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/dto/IotThingDictRelationListDTO.java @@ -0,0 +1,70 @@ +package com.thing.thing.dict.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 物字典的关系表 +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Data +@Schema( name= "物字典&实体的信息") +public class IotThingDictRelationListDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + private Long id; + + @Schema(description = "实体id") + private Long entityId; + + @Schema(description = "实体编码") + private String entityCode; + + @Schema(description = "实体名称") + private String entityName; + + @Schema(description = "字典id") + private Long dictId; + + @Schema(description = "字典类型:attr、tag") + private String dictType; + + @Schema(description = "字典编码") + private String dictCode; + + @Schema(description = "字典名称") + private String dictName; + + @Schema(description = "字典单位") + private String dictUnit; + + @Schema(description = "颜色") + private String dictColor; + + + @Schema(description = "存储类型") + private String storeType; + + @Schema(description = "组id") + private Long groupId; + + @Schema(description = "组名称") + private String groupName; + + @Schema(description = "组编码") + private String groupCode; + + @Schema(description = "字典备注") + private String remark; + + @Schema(description = "业务类型") + private String businessType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/entity/IotThingDictEntity.java b/modules/thing/src/main/java/com/thing/thing/dict/entity/IotThingDictEntity.java new file mode 100644 index 0000000..20d0c04 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/entity/IotThingDictEntity.java @@ -0,0 +1,99 @@ +package com.thing.thing.dict.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 物字典信息表 + * + * @author xc + * @since 3.0 2024-03-22 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_dict") +public class IotThingDictEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典编号 + */ + private String code; + /** + * 字典名称 + */ + private String name; + /** + * 字典单位 + */ + private String unit; + /** + * 频率(s) + */ + private Integer rate; + /** + * 颜色 + */ + private String color; + /** + * 数据类型: instant - 瞬时 + * increase - 递增 + * region - 区间 + * control - 控制 + * alarm - 告警 + */ + private String dataType; + /** + * 图标 + */ + private String url; + /** + * 备注 + */ + private String remark; + /** + * 扩展字段 + */ + private String extendData; + /** + * 是否系统字典:0-默认系统 1-自定义 + */ + private Integer isDefault; + + /** + * 排序 + */ + private Long sort; + + /** + * 监控分析页面默认配置json串 + */ + private String businessType; + /** + * 组名称 + */ + private String groupName; + /** + * 最大值 + */ + private BigDecimal maxNum; + /** + * 最小值 + */ + private BigDecimal minNum; + + /** + * 能源品种类型 + */ + private String evType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/excel/IotThingDictExcel.java b/modules/thing/src/main/java/com/thing/thing/dict/excel/IotThingDictExcel.java new file mode 100644 index 0000000..e1b990f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/excel/IotThingDictExcel.java @@ -0,0 +1,51 @@ +package com.thing.thing.dict.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 物字典信息表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +public class IotThingDictExcel { + + @Excel(name = "序号") + private Long sort; + + @Excel(name = "组名称",orderNum = "1") + private String groupName; + + @Excel(name = "数据类型:instant-瞬时;increase-递增;region-区间;control-控制;alarm-告警",orderNum = "2") + private String dataType; + + @Excel(name = "字典编号",orderNum = "3") + private String code; + + @Excel(name = "字典名称",orderNum = "4") + private String name; + + @Excel(name = "字典单位",orderNum = "5") + private String unit; + + @Excel(name = "频率(s)",orderNum = "6") + private Integer rate; + + @Excel(name = "备注",orderNum = "7") + private String remark; + + @Excel(name = "是否系统字典:0-默认系统 1-自定义",orderNum = "8") + private Integer isDefault; + + @Excel(name = "最大值",orderNum = "9") + private BigDecimal maxNum; + + @Excel(name = "最小值",orderNum = "10") + private BigDecimal minNum; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/mapper/IotThingDictMapper.java b/modules/thing/src/main/java/com/thing/thing/dict/mapper/IotThingDictMapper.java new file mode 100644 index 0000000..30253b6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/mapper/IotThingDictMapper.java @@ -0,0 +1,16 @@ +package com.thing.thing.dict.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.dict.entity.IotThingDictEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@Mapper +public interface IotThingDictMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/service/IotThingDictService.java b/modules/thing/src/main/java/com/thing/thing/dict/service/IotThingDictService.java new file mode 100644 index 0000000..059833e --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/service/IotThingDictService.java @@ -0,0 +1,60 @@ +package com.thing.thing.dict.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.entity.IotThingDictEntity; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 物字典信息表 + * + * @author xc + * @since 3.0 2024-03-22 + */ +public interface IotThingDictService extends IBaseService { + + PageData pageList(Map params); + + List findAll(Map params); + + IotThingDictDTO findById(Long id); + + List findByIds(Collection ids); + + IotThingDictDTO findDictByCode(String code); + + IotThingDictDTO findDictByCodeAndGroupName(String code,String groupName); + + List findDictByGroupNames(List groupNames); + + List findDictByCodes(Collection code); + + List findDictByGroupNameAndCodes(String groupName,Collection code); + + List unit(); + + Integer findMaxSort(Integer isDefault); + + Long findMaxSort(String type); + + void importExcel(MultipartFile file, HttpServletRequest request); + + void copy(List groupNames,List dictIds); + + void saveDict(IotThingDictDTO dto); + + void updateDict(IotThingDictDTO dto); + + void batchDeleteById(Long[] ids); + + List groups(); + + List> type(); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dict/service/impl/IotThingDictServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/dict/service/impl/IotThingDictServiceImpl.java new file mode 100644 index 0000000..48067e9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dict/service/impl/IotThingDictServiceImpl.java @@ -0,0 +1,387 @@ +package com.thing.thing.dict.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.google.common.collect.Maps; +import com.mybatisflex.core.constant.SqlConsts; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryCondition; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.DictTypeEnum; +import com.thing.common.core.enumeration.IsDefaultEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.BizUtils; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.entity.IotThingDictEntity; +import com.thing.thing.dict.excel.IotThingDictExcel; +import com.thing.thing.dict.mapper.IotThingDictMapper; +import com.thing.thing.dict.service.IotThingDictService; +import jakarta.servlet.http.HttpServletRequest; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.max; +import static com.thing.thing.dict.entity.table.IotThingDictEntityTableDef.IOT_THING_DICT_ENTITY; + +/** + * 物字典信息表 + * + * @author xc + * @since 3.0 2024-03-22 + */ +@Service +public class IotThingDictServiceImpl extends BaseServiceImpl implements IotThingDictService { + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + + String name = (String) params.get("name"); + String code = (String) params.get("code"); + String groupName = (String) params.get("groupName"); + String dataType = (String) params.get("dataType"); + String groupNames = (String)params.get("groupNames"); + String evType = (String)params.get("evType"); + List groupNameList = StringUtils.isNotBlank(groupNames)? Arrays.asList(groupNames.split(",")):null; + List ids = MapUtil.get(params, "ids", List.class); + List codes = MapUtil.get(params, "codes", List.class); + + wrapper + .like(IotThingDictEntity::getCode, code, StringUtils.isNotEmpty(code)) + .like(IotThingDictEntity::getName, name, StringUtils.isNotEmpty(name)) + .like(IotThingDictEntity::getGroupName, groupName, StringUtils.isNotEmpty(groupName)) + .eq(IotThingDictEntity::getDataType, dataType, StringUtils.isNotEmpty(dataType)) + .in(IotThingDictEntity::getGroupName, groupNameList, CollectionUtils.isNotEmpty(groupNameList)) + .in(IotThingDictEntity::getId, ids, CollectionUtils.isNotEmpty(ids)) + .eq(IotThingDictEntity::getEvType, evType, StringUtils.isNotEmpty(evType)) + .in(IotThingDictEntity::getCode, codes, CollectionUtils.isNotEmpty(codes)); + + wrapper.where( + IOT_THING_DICT_ENTITY.IS_DEFAULT.eq(0) + .or(IOT_THING_DICT_ENTITY.IS_DEFAULT.eq(1).and(IOT_THING_DICT_ENTITY.TENANT_CODE.eq(UserContext.getRealTenantCode()))) + ); + return wrapper; + } + + @Override + public PageData pageList(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, "sort", false); + Page paginate = mapper.paginateAs(new Page<>(MapUtil.getInt(params, "page", 1), + MapUtil.getInt(params, "limit", 10)), + queryWrapper, IotThingDictDTO.class); + if(paginate.getTotalRow()!=0){ + paginate.getRecords().forEach(dto -> { + if(StringUtils.isNotBlank(dto.getDataType())) { + dto.setTypeName(DictTypeEnum.match(dto.getDataType())); + } + }); + } + return new PageData<>(paginate.getRecords(), paginate.getTotalRow()); + } + + @Override + public List findAll(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, "create_date", false); + return mapper.selectListByQueryAs(queryWrapper, IotThingDictDTO.class); + } + + @Override + public IotThingDictDTO findById(Long id) { + return mapper.selectOneByQueryAs(QueryWrapper.create().eq(IotThingDictEntity::getId,id),IotThingDictDTO.class); + } + + @Override + public List findByIds(Collection ids) { + return mapper.selectListByQueryAs(QueryWrapper.create().in(IotThingDictEntity::getId,ids),IotThingDictDTO.class); + } + + @Override + public IotThingDictDTO findDictByCode(String code) { + IotThingDictDTO iotThingDictDTO = mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(IotThingDictEntity::getIsDefault, 1), + IotThingDictDTO.class); + if(Objects.isNull(iotThingDictDTO)){ + return mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getIsDefault, 0), + IotThingDictDTO.class); + } + return iotThingDictDTO; + } + + @Override + public IotThingDictDTO findDictByCodeAndGroupName(String code, String groupName) { + IotThingDictDTO iotThingDictDTO = mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getGroupName, groupName) + .eq(IotThingDictEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(IotThingDictEntity::getIsDefault, 1), + IotThingDictDTO.class); + if(Objects.isNull(iotThingDictDTO)){ + return mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getIsDefault, 0), + IotThingDictDTO.class); + } + return iotThingDictDTO; + } + + @Override + public List findDictByGroupNames(List groupNames) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingDictEntity::getGroupName, groupNames) + .eq(IotThingDictEntity::getTenantCode, UserContext.getRealTenantCode()), + IotThingDictDTO.class); + } + + @Override + public List findDictByCodes(Collection code) { + List list = mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(IotThingDictEntity::getIsDefault, 1), + IotThingDictDTO.class); + if(CollectionUtils.isEmpty(list)){ + return mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getIsDefault, 0), + IotThingDictDTO.class); + } + return list; + } + + @Override + public List findDictByGroupNameAndCodes(String groupName, Collection code) { + List list = mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getGroupName, groupName) + .in(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getTenantCode, UserContext.getRealTenantCode()) + .eq(IotThingDictEntity::getIsDefault, 1), + IotThingDictDTO.class); + if(CollectionUtils.isEmpty(list)){ + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingDictEntity::getGroupName, groupName) + .in(IotThingDictEntity::getCode, code) + .eq(IotThingDictEntity::getIsDefault, 0), + IotThingDictDTO.class); + } + return list; + } + + @Override + public List unit() { + return mapper.selectAll().stream().map(IotThingDictEntity::getUnit) + .filter(Objects::nonNull) + .distinct().toList(); + } + + @Override + public Integer findMaxSort(Integer isDefault) { + QueryWrapper queryWrapper = QueryWrapper + .create() + .select(max(IOT_THING_DICT_ENTITY.SORT)).eq(String.valueOf(IOT_THING_DICT_ENTITY.IS_DEFAULT), 1); + Integer sort = mapper.selectOneByQueryAs(queryWrapper, Integer.class); + return Objects.isNull(sort) ? 0 : sort; + } + + @Override + public Long findMaxSort(String type) { + if(StringUtils.isBlank(type)){ + QueryWrapper queryWrapper = QueryWrapper + .create() + .select(max(IOT_THING_DICT_ENTITY.SORT)); + Long num = mapper.selectOneByQueryAs(queryWrapper, Long.class); + return Objects.isNull(num) ? 1 : num+1; + } + String match = DictTypeEnum.match(type); + if(StringUtils.isBlank(match)){ + throw new SysException("字典类型不匹配"); + } + List list = mapper.selectListByCondition(QueryCondition.create(IOT_THING_DICT_ENTITY.DATA_TYPE, SqlConsts.EQUALS, type)); + return list.stream().mapToLong(IotThingDictEntity::getSort).max().orElse(1L); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void importExcel(MultipartFile file, HttpServletRequest request) { + List sheetData = + ExcelUtils.importExcel(file, 1, 1, IotThingDictExcel.class, 0); + if (CollectionUtil.isEmpty(sheetData)) { + throw new SysException("无数据"); + } + if (sheetData.stream() + .anyMatch(s -> + ObjectUtil.equals(s.getIsDefault(),IsDefaultEnum.Y.getValue()))) { + Boolean admin = UserContext.isAdmin(); + Long tenantCode = SecurityUser.getUser().getTenantCode(); + Long realTenantCode = UserContext.getRealTenantCode(); + if(admin && !Objects.equals(tenantCode,realTenantCode)){ + throw new SysException("当前切换用户无权增加系统默认字典,请切换到admin用户处理"); + } + if(!admin){ + throw new SysException("当前账户无权导入系统默认字典"); + } + } + List blankList = + sheetData.stream() + .filter(s -> StringUtils.isBlank(BizUtils.trimAll(s.getCode())) || StringUtils.isBlank(BizUtils.trimAll(s.getDataType()))) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(blankList)) { + throw new SysException("字典编码和字典类型不能为空,请检查下excel数据"); + } + Map codeCountMap = sheetData.stream() + .collect(Collectors.groupingBy(IotThingDictExcel::getCode, Collectors.counting())); // 使用 groupingBy 收集器按照 code 进行分组,并计数 + String duplicates = codeCountMap.entrySet().stream() + .filter(entry -> entry.getValue() > 1) // 过滤出计数大于 1 的 entry + .map(Map.Entry::getKey) // 获取过滤后的 entry 的 key,即重复的 code 值 + .collect(Collectors.joining(",")); // 将重复的 code 值用逗号拼接成字符串 + if (StringUtils.isNotEmpty(duplicates)) { + throw new SysException("重复的 code 值:" + duplicates); + } + List iotThingDictEntities = ConvertUtils.sourceToTarget(sheetData, IotThingDictEntity.class); + //按照数据类型进行分组 + Map> dictListMap = iotThingDictEntities.stream().collect(Collectors.groupingBy(IotThingDictEntity::getDataType)); + dictListMap.forEach((k,v)->{ + Long maxSort = findMaxSort(k); + AtomicLong sort = new AtomicLong(ObjectUtil.isNull(maxSort) ? 0L : maxSort); + List list = v.stream().peek(e -> e.setSort(sort.getAndIncrement()) + /*.setTenantCode(SecurityUser.getUser().getTenantCode()) + .setCompanyId(SecurityUser.getUser().getCompanyId()) + .setDeptId(SecurityUser.getUser().getDeptId())*/ + ).toList(); + mapper.insertBatch(list); + }); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void copy(List groupNames, List dictIds) { + if(CollectionUtils.isEmpty(groupNames)){ + throw new SysException("组名称不能为空"); + } + List copyDictEntities = mapper.selectListByIds(dictIds); + if(CollectionUtils.isEmpty(copyDictEntities)){ + throw new SysException("未找到对应字典数据"); + } + List iotThingDictDTOList = findDictByGroupNames(groupNames); + Long maxSort = findMaxSort(""); + AtomicLong sort = new AtomicLong(maxSort); + // 构建实体 + List list = groupNames.stream() + .flatMap(groupName -> copyDictEntities.stream() + //过滤完已经存在的字典 + .filter(d-> iotThingDictDTOList.stream().noneMatch(iotThingDictDTO -> + StringUtils.equals(groupName,iotThingDictDTO.getGroupName()) + && StringUtils.equals(d.getCode(),iotThingDictDTO.getCode()) + && StringUtils.equals(d.getDataType(),iotThingDictDTO.getDataType()) + )) + //构建字典对象 + .peek(e -> { + e.setGroupName(groupName).setId(null); + e.setSort(sort.incrementAndGet()); + }) + ).toList(); + if(CollectionUtils.isNotEmpty(list)){ + mapper.insertBatch(list); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveDict(IotThingDictDTO dto) { + Boolean admin = UserContext.isAdmin(); + Long tenantCode = SecurityUser.getUser().getTenantCode(); + Long realTenantCode = UserContext.getRealTenantCode(); + if(ObjectUtil.equals(dto.getIsDefault(), IsDefaultEnum.Y.getValue()) && admin && !Objects.equals(tenantCode,realTenantCode)){ + throw new SysException("当前切换用户无权增加系统默认字典,请切换到admin用户处理"); + } + if (ObjectUtil.equals(dto.getIsDefault(), IsDefaultEnum.Y.getValue()) && !admin) { + throw new SysException("用户无权增加系统默认字典"); + } + IotThingDictDTO dictDTO = findDictByCodeAndGroupName(dto.getCode(),dto.getGroupName()); + if(Objects.nonNull(dictDTO)){ + throw new SysException("字典编码已经存在"); + } + String url = BizUtils.trimAll(dto.getUrl()); + if(StringUtils.isNotBlank(url)){ + url = OSSFactory.cutOut(dto.getUrl()); + dto.setUrl(url); + } + IotThingDictEntity iotThingDictEntity = ConvertUtils.sourceToTarget(dto, IotThingDictEntity.class); + //iotThingDictEntity.setTenantCode(SecurityUser.getUser().getTenantCode()).setCompanyId(SecurityUser.getUser().getCompanyId()).setDeptId(SecurityUser.getUser().getDeptId()); + mapper.insert(iotThingDictEntity); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateDict(IotThingDictDTO dto) { + Long id = dto.getId(); + IotThingDictEntity iotThingDictEntity = mapper.selectOneById(id); + Boolean admin = UserContext.isAdmin(); + Long tenantCode = SecurityUser.getUser().getTenantCode(); + Long realTenantCode = UserContext.getRealTenantCode(); + if(ObjectUtil.equals(dto.getIsDefault(), IsDefaultEnum.Y.getValue()) && admin && !Objects.equals(tenantCode,realTenantCode)){ + throw new SysException("当前切换用户无权增加系统默认字典,请切换到admin用户处理"); + } + if (ObjectUtil.equals(dto.getIsDefault(), IsDefaultEnum.Y.getValue()) && !admin) { + throw new SysException("用户无权修改系统默认字典"); + } + String url = BizUtils.trimAll(dto.getUrl()); + if(StringUtils.isNotBlank(url)){ + url = OSSFactory.cutOut(dto.getUrl()); + } + iotThingDictEntity + .setCode(dto.getCode()) + .setName(dto.getName()) + .setUnit(dto.getUnit()) + .setRate(dto.getRate()) + .setDataType(dto.getDataType()) + .setBusinessType(dto.getBusinessType()) + .setSort(dto.getSort()) + .setMaxNum(dto.getMaxNum()) + .setMinNum(dto.getMinNum()) + .setIsDefault(dto.getIsDefault()) + .setColor(dto.getColor()) + .setUrl(url) + .setRemark(dto.getRemark()); + mapper.update(iotThingDictEntity); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchDeleteById(Long[] ids) { + mapper.deleteBatchByIds(Arrays.asList(ids)); + } + + @Override + public List groups() { + List iotThingDictDTOList = findAll(Maps.newHashMap()); + return iotThingDictDTOList.stream().map(IotThingDictDTO::getGroupName).filter(StringUtils::isNotBlank).distinct().toList(); + } + + @Override + public List> type() { + return Arrays.stream(DictTypeEnum.values()) + .sorted(Comparator.comparingInt(DictTypeEnum::getSort)) + .map(s -> Map.of("type", s.getType(), "name", s.getName())) + .toList(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/controller/IotThingDictRelationController.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/controller/IotThingDictRelationController.java new file mode 100644 index 0000000..cf8ce65 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/controller/IotThingDictRelationController.java @@ -0,0 +1,223 @@ +package com.thing.thing.dictRelation.controller; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.param.IotSyncEntityDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; +import java.util.Map; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@RestController +@RequestMapping("v2/dict/relation") +@Tag(name="物字典信息表") +public class IotThingDictRelationController { + + @Resource + private IotThingDictRelationService service; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)"), + @Parameter(name = "entityIds", description = "物实体id,逗号拼接") , + @Parameter(name = "entityName", description = "物实体名称或者编码") , + @Parameter(name = "groupName", description = "字典的组名称") , + @Parameter(name = "name", description = "字典的名称或者编码") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)" + ) + }) + public Result> page( + @RequestParam(required = false) String orderField, + @RequestParam(required = false) String order, + @RequestParam(required = false) Integer page, + @RequestParam(required = false) Integer limit, + @RequestParam(required = false) String entityName, + @RequestParam(required = false) String entityIds, + @RequestParam(required = false) String groupName, + @RequestParam(required = false) String name, + @RequestParam(required = false) String templateMark, + @RequestParam(required = false) String dataType){ + PageData pageList = service.pageList(orderField, order, page, limit,entityName, groupName, entityIds, name, templateMark, dataType); + return new Result>().ok(pageList); + } + + @GetMapping("list") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> list(@RequestParam(required = false) String orderField, + @RequestParam(required = false) String order, + @RequestParam(required = false) String entityName, + @RequestParam(required = false) String entityIds, + @RequestParam(required = false) String groupName, + @RequestParam(required = false) String name, + @RequestParam(required = false) String templateMark, + @RequestParam(required = false) String dataType){ + List list = service.findList(orderField, order, entityName, groupName, entityIds, name, templateMark, dataType, UserContext.getRealTenantCode()); + return new Result>().ok(list); + } + + @GetMapping("noRepeatList") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> noRepeatList(@Parameter(hidden = true) @RequestParam Map params){ + List list = service.noRepeatList(params); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingDictRelationDTO data = service.findById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingDictRelationParamDTO dto){ + //效验数据 + // ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.save(dto); + return new Result<>(); + } + + + @PostMapping("saveBatchByCode") + @Operation(summary="保存") + @LogOperation("保存") + public Result saveCodes(@RequestBody IotThingDictRelationParamDTO dto){ + //效验数据 + // ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.saveCodes(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingDictRelationDTO dto){ + //效验数据 + // ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.update(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDeleteByIds(ids); + return new Result<>(); + } + + + @GetMapping("entity/indicators") + @Operation(summary="指标集合") + public Result> indicators(Long entityId){ + List data = service.findAllByEntityIdAndCodes(entityId,null); + return new Result>().ok(data); + } + + + @GetMapping("indicators/{entityId}") + @Operation(summary="信息") + public Result> findByEntityId(@PathVariable("entityId") Long entityId){ + List data = service.findAllByEntityIdAndCodes(entityId,null); + return new Result>().ok(data); + } + + + @PostMapping("template") + @Operation(summary="模板") + @LogOperation("物指标模板") + public void template(HttpServletResponse response){ + service.template(response); + } + + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + service.export(ids,params,response); + } + + + @PostMapping("import") + @Operation(summary="物指标--物属性/物标签Excel导入") + public Result importExcel(MultipartFile file, HttpServletRequest request) { + service.importExcel(file, request); + return new Result<>(); + } + + + @PostMapping("copy") + @Operation(summary="物指标属性复制") + public Result copy(@Validated @RequestBody IotThingDictRelationParamDTO dto) { + service.copy(dto); + return new Result<>(); + } + + @GetMapping("group") + @Operation(summary="组名称集合") + public Result> groups() { + return new Result>().ok(service.groups()); + } + + + @PostMapping("groups") + @Operation(summary="配置中心组") + public Result> groups(@RequestBody List ids){ + List groups = service.groups(ids); + return new Result>().ok(groups); + } + + + @PostMapping("syncOrUpdateEntity") + @Operation(summary="同步/更新多个物实体(模板版)") + @LogOperation("同步/更新多个物实体(模板版)") + public Result syncOrUpdateEntity(@RequestBody IotSyncEntityDTO dto) { + service.syncOrUpdateEntity(dto); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/dto/IotThingDictRelationDTO.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/dto/IotThingDictRelationDTO.java new file mode 100644 index 0000000..23c4219 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/dto/IotThingDictRelationDTO.java @@ -0,0 +1,79 @@ +package com.thing.thing.dictRelation.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@Data +@Schema(description = "物字典信息表") +@Accessors(chain = true) +public class IotThingDictRelationDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "字典编号") + private String code; + @Schema(description = "字典名称") + private String name; + @Schema(description = "字典单位") + private String unit; + @Schema(description = "频率(s)") + private Integer rate; + @Schema(description = "颜色") + private String color; + @Schema(description = "数据类型: instant - 瞬时 ;increase - 递增 ;region - 区间;control - 控制 ;alarm - 告警") + private String dataType; + @Schema(description = "图标") + private String url; + @Schema(description = "备注") + private String remark; +// @Schema(description = "扩展字段") +// private String extendData; + @Schema(description = "排序") + @JsonSerialize(using = ToStringSerializer.class) + private Long sort; + @Schema(description = "监控分析页面默认配置json串") + private String businessType; + @Schema(description = "组名称") + private String groupName; + @Schema(description = "最大值") + @JsonSerialize(using = ToStringSerializer.class) + private BigDecimal maxNum; + @Schema(description = "最小值") + @JsonSerialize(using = ToStringSerializer.class) + private BigDecimal minNum; + @Schema(description = "物实体ID") + @JsonSerialize(using = ToStringSerializer.class) + private Long entityId; + @Schema(description = "能源品种类型") + private String evType; + @Schema(description = "实体code") + private String entityCode; + @Schema(description = "实体名称") + private String entityName; + @Schema(description = "实体名称") + private String dataTreatingMark; + @Schema(description = "映射子集(子数据列表)") + private String childConfig; + @Schema(description = "是否是模板,1模板,0 非模板(默认)") + private String templateMark; + @Schema(description = "租户编码") + @JsonSerialize(using = ToStringSerializer.class) + private String tenantCode; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/entity/IotThingDictRelationEntity.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/entity/IotThingDictRelationEntity.java new file mode 100644 index 0000000..a858667 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/entity/IotThingDictRelationEntity.java @@ -0,0 +1,121 @@ +package com.thing.thing.dictRelation.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.math.BigDecimal; + +/** + * 物字典信息表 + * + * @author xc + * @since 3.0 2024-03-22 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_dict_relation") +public class IotThingDictRelationEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 字典编号 + */ + private String code; + /** + * 字典名称 + */ + private String name; + /** + * 字典单位 + */ + private String unit; + /** + * 频率(s) + */ + private Integer rate; + /** + * 颜色 + */ + private String color; + /** + * 数据类型: instant - 瞬时 + * increase - 递增 + * region - 区间 + * control - 控制 + * alarm - 告警 + */ + private String dataType; + /** + * 图标 + */ + private String url; + /** + * 备注 + */ + private String remark; + /** + * 扩展字段 + */ + private String extendData; + /** + * 排序 + */ + private Long sort; + /** + * 监控分析页面默认配置json串 + */ + private String businessType; + /** + * 组名称 + */ + private String groupName; + /** + * 最大值 + */ + private BigDecimal maxNum; + /** + * 最小值 + */ + private BigDecimal minNum; + /** + * 物实体ID + */ + private Long entityId; + + /** + * 物实体name + */ + private String entityName; + /** + * 物实体code + */ + private String entityCode; + /** + * 能源品种类型 + */ + private String evType; + + /** + * 是否为数据处理: 0 否 1 是 + */ + private String dataTreatingMark; + + + /** + * 映射子集(子数据列表) + */ + private String childConfig; + + /** + * 是否是模板,1模板,0 非模板(默认) + */ + private String templateMark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/excel/IotThingDictRelationExcel.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/excel/IotThingDictRelationExcel.java new file mode 100644 index 0000000..3fae190 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/excel/IotThingDictRelationExcel.java @@ -0,0 +1,44 @@ +package com.thing.thing.dictRelation.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class IotThingDictRelationExcel { + + @Excel(name = "物名称",width = 20) + private String entityName; + + @Excel(name = "物编码",orderNum = "1",width = 20) + private String entityCode; + + @Excel(name = "物字典名称",orderNum = "2",width = 20) + private String name; + + @Excel(name = "物字典编码",orderNum = "3",width = 20) + private String code; + + @Excel(name = "字典单位",orderNum = "4") + private String unit; + + @Excel(name = "物字典组",orderNum = "5",width = 20) + private String groupName; + + @Excel(name = "数据类型: instant - 瞬时 ;increase - 递增 ;region - 区间;control - 控制 ; alarm - 告警",orderNum = "6",width = 20) + private String dataType; + + @Excel(name = "最大值",orderNum = "7") + private BigDecimal maxNum; + + @Excel(name = "最小值",orderNum = "8") + private BigDecimal minNum; + + @Excel(name = "能源品种类型",orderNum = "9",width = 15) + private String evType; + + @Excel(name = "备注",orderNum = "10") + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/mapper/IotThingDictRelationMapper.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/mapper/IotThingDictRelationMapper.java new file mode 100644 index 0000000..45651bc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/mapper/IotThingDictRelationMapper.java @@ -0,0 +1,16 @@ +package com.thing.thing.dictRelation.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.dictRelation.entity.IotThingDictRelationEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@Mapper +public interface IotThingDictRelationMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/param/IotThingDictRelationParamDTO.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/param/IotThingDictRelationParamDTO.java new file mode 100644 index 0000000..d7c4a8d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/param/IotThingDictRelationParamDTO.java @@ -0,0 +1,98 @@ +package com.thing.thing.dictRelation.param; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; +import java.util.List; + +/** +* 物字典信息表 +* +* @author xc +* @since 3.0 2024-03-22 +*/ +@Data +@Schema(description = "物字典信息表") +@Accessors(chain = true) +public class IotThingDictRelationParamDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "字典编号") + private String code; + @Schema(description = "字典名称") + private String name; + @Schema(description = "字典单位") + private String unit; + @Schema(description = "频率(s)") + private Integer rate; + @Schema(description = "颜色") + private String color; + @Schema(description = "数据类型: instant - 瞬时 ;increase - 递增 ;region - 区间;control - 控制 ;alarm - 告警") + private String dataType; + @Schema(description = "图标") + private String url; + @Schema(description = "备注") + private String remark; + @Schema(description = "扩展字段") + private String extendData; + @Schema(description = "排序") + private Long sort; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "监控分析页面默认配置json串") + private String businessType; + @Schema(description = "组名称") + private String groupName; + @Schema(description = "最大值") + private BigDecimal maxNum; + @Schema(description = "最小值") + private BigDecimal minNum; + @Schema(description = "部门ID") + private Long deptId; + @Schema(description = "物实体ID") + private Long entityId; + @Schema(description = "能源品种类型") + private String evType; + @Schema(description = "实体code") + private String entityCode; + @Schema(description = "实体名称") + private String entityName; + @Schema(description = "实体名称") + private String dataTreatingMark; + @Schema(description = "映射子集(子数据列表)") + private String childConfig; + @Schema(description = "是否是模板,1模板,0 非模板(默认)") + private String templateMark; + @Schema(description = "物实体ID集合(新增指标)") + private List entityIds; + @Schema(description = "属性组(新增指标)") + private List groupNames; + @Schema(description = "属性id集合(新增指标/属性移动)") + private List dictIds; + @Schema(description = "属性编码集合(新增指标/属性移动)") + private List dictCodes; + @Schema(description = "采集频率(默认15分钟)") + private Long gatherFrequency; + @Schema(description = "单位,暂时使用") + private String thingAttrUnit; + /** iot_thing_source 表中的字段**/ + @Schema(description = "物属性值范围(上限、下限、临界等)") + private String thingAttrBoundary; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/service/IotThingDictRelationService.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/service/IotThingDictRelationService.java new file mode 100644 index 0000000..49eeffd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/service/IotThingDictRelationService.java @@ -0,0 +1,75 @@ +package com.thing.thing.dictRelation.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.entity.IotThingDictRelationEntity; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.entity.param.IotSyncEntityDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 物字典信息表 + * + * @author xc + * @since 3.0 2024-03-22 + */ +public interface IotThingDictRelationService extends IBaseService { + + PageData pageList(String orderField,String order,Integer page,Integer limit, + String entityName,String groupName,String entityIds,String name,String templateMark,String dataType); + + List findList(String orderField,String order, + String entityName,String groupName,String entityIds,String name,String templateMark,String dataType,Long tenantCode); + + List findAll(Map params); + + List noRepeatList(Map params); + + List findAllByIdsAndEntityIds(List ids, List entityIds); + + List findDictRelationAllByEntityIdsAndGroupNamesAndCodes(Collection entityIds, Collection groupNames, Collection codes); + + IotThingDictRelationDTO findDictRelationByEntityIdAndCode(Long entityId, String code); + + IotThingDictRelationDTO findById(Long id); + + List findAllByEntityIdAndCodes(Long entityId, Collection codes); + /** 注意 多个物 有相同的属性code **/ + List findAllByEntityIdsAndCodes(Collection entityIds, Collection codes); + /** 此方法可以避免 有相同的属性code **/ + + List findAllByTenantCode(Long tenantCode); + + List groups(); + + List groups(List ids); + + void save(IotThingDictRelationParamDTO dto); + + void saveCodes(IotThingDictRelationParamDTO dto); + + void update(IotThingDictRelationDTO dto); + + void batchDeleteByIds(Long[] ids); + + void saveBatchDictRelation(Collection dto); + + void copy(IotThingDictRelationParamDTO dto); + + void template(HttpServletResponse response); + + void export(Long[] ids,Map params, HttpServletResponse response); + + void importExcel(MultipartFile file, HttpServletRequest request); + + void syncOrUpdateEntity(IotSyncEntityDTO dto); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/dictRelation/service/impl/IotThingDictRelationServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/dictRelation/service/impl/IotThingDictRelationServiceImpl.java new file mode 100644 index 0000000..f8eb1a3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/dictRelation/service/impl/IotThingDictRelationServiceImpl.java @@ -0,0 +1,585 @@ +package com.thing.thing.dictRelation.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.CaseFormat; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.SyncUpdateEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.*; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.dict.dto.IotThingDictDTO; +import com.thing.thing.dict.entity.IotThingDictEntity; +import com.thing.thing.dict.service.IotThingDictService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.entity.IotThingDictRelationEntity; +import com.thing.thing.dictRelation.excel.IotThingDictRelationExcel; +import com.thing.thing.dictRelation.mapper.IotThingDictRelationMapper; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.entity.param.IotSyncEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.transport.api.adaptor.JsonConverter; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.thing.thing.dictRelation.entity.table.IotThingDictRelationEntityTableDef.IOT_THING_DICT_RELATION_ENTITY; + +@Service +public class IotThingDictRelationServiceImpl extends BaseServiceImpl implements IotThingDictRelationService { + + @Resource + private IotThingDictService dictService; + + @Resource + private IotThingEntityService thingEntityService; + + @Resource + private ThingCache cache; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String groupName = (String) params.get("groupName"); + String groupNames = (String) params.get("groupNames"); + String name = (String) params.get("name"); + String entityName = (String) params.get("entityName"); + String entityIds = (String) params.get("entityIds"); + String dataType = (String) params.get("dataType"); + String templateMark = (String) params.get("templateMark"); + List ids = MapUtil.get(params, "ids", List.class); + wrapper.eq(IotThingDictRelationEntity::getGroupName, groupName, StringUtils.isNotBlank(groupName)) + .in(IotThingDictRelationEntity::getId, ids, CollectionUtils.isNotEmpty(ids)) + .eq(IotThingDictRelationEntity::getTemplateMark, templateMark) + .eq(IotThingDictRelationEntity::getDataType, dataType,StringUtils.isNotBlank(dataType)) + .eq(IotThingDictRelationEntity::getTenantCode, UserContext.getRealTenantCode()) + ; + if (StringUtils.isNotBlank(name)) { + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.NAME.like(name).or(IOT_THING_DICT_RELATION_ENTITY.CODE.like(name))); + } + + if (StringUtils.isNotBlank(entityName)) { + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.ENTITY_CODE.like(entityName).or(IOT_THING_DICT_RELATION_ENTITY.ENTITY_NAME.like(entityName))); + } + + if (StringUtils.isNotBlank(groupNames)) { + List groupNamesList = Arrays.stream(StringUtils.split(groupNames, ",")).toList(); + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.GROUP_NAME.in(groupNamesList)); + } + if (StringUtils.isNotBlank(entityIds)) { + List entityIdList = Arrays.stream(StringUtils.split(entityIds, ",")).map(Long::parseLong).toList(); + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.ENTITY_ID.in(entityIdList)); + } + return wrapper; + } + + public QueryWrapper getWrapper(String orderField, + String order, + String entityName, + String groupName, + String entityIds, + String name, + String templateMark, + String dataType, + Long tenantCode) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq(IotThingDictRelationEntity::getGroupName, groupName, StringUtils.isNotBlank(groupName)) + .eq(IotThingDictRelationEntity::getTemplateMark, templateMark) + .eq(IotThingDictRelationEntity::getDataType, dataType,StringUtils.isNotBlank(dataType)) + .eq(IotThingDictRelationEntity::getTenantCode, tenantCode, Objects.isNull(tenantCode)) + ; + if (StringUtils.isNotBlank(name)) { + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.NAME.like(name).or(IOT_THING_DICT_RELATION_ENTITY.CODE.like(name))); + } + + if (StringUtils.isNotBlank(entityName)) { + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.ENTITY_CODE.like(entityName).or(IOT_THING_DICT_RELATION_ENTITY.ENTITY_NAME.like(entityName))); + } + + if (StringUtils.isNotBlank(groupName)) { + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.GROUP_NAME.like(groupName).or(IOT_THING_DICT_RELATION_ENTITY.GROUP_NAME.like(groupName))); + } + + if (StringUtils.isNotBlank(entityIds)) { + String[] entityIdList = entityIds.split(","); + wrapper.where(IOT_THING_DICT_RELATION_ENTITY.ENTITY_ID.in(Arrays.stream(entityIdList).map(Long::parseLong).toList())); + } + if(StringUtils.isBlank(orderField)){ + orderField = CacheNameEnum.DictField.THING_DICT_RELATION_SORT.getField(); + } + + wrapper.orderBy(orderField, StringUtils.equalsIgnoreCase(order,Constant.ASC)); + + return wrapper; + } + + @Override + public PageData pageList(String orderField,String order,Integer page,Integer limit, + String entityName,String groupName,String entityIds,String name,String templateMark,String dataType) + { + List list = findList(orderField, order, entityName, groupName, entityIds, name, templateMark, dataType, UserContext.getRealTenantCode()); + if (CollectionUtils.isEmpty(list)) { + return PageData.empty(); + } + List resList = PageUtils.startPage(list, page, limit); + return new PageData<>(resList, CollectionUtils.size(list)); + } + + @Override + public List findList(String orderField,String order, + String entityName,String groupName, String entityIds,String name, + String templateMark,String dataType,Long tenantCode) + { + List dictRelationList = cache.getTopicMap(CacheNameEnum.THING_DICT_RELATION); + if(CollectionUtils.isEmpty(dictRelationList)){ + List dictRelationDTOList = mapper.selectListByQueryAs(getWrapper(orderField,order,null,null,null,null, + null,null,null), + IotThingDictRelationDTO.class); + if(CollectionUtils.isEmpty(dictRelationDTOList)) { + return new ArrayList<>(); + } + dictRelationList = JsonConverter.convertToJsonObjectListObjectNode(dictRelationDTOList); + //更新缓存 + CacheInit.dictRelationMap(dictRelationList,cache); + } + if(StringUtils.isBlank(orderField)){ + orderField = CacheNameEnum.DictField.THING_DICT_RELATION_SORT.getField(); + } + String finalOrderField = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, orderField);; + Comparator comparator = CompareUtils.getComparator(order, finalOrderField); //封装参数 + List> pairs = buildParam(groupName,templateMark,dataType,tenantCode); + return dictRelationList.stream() + .filter(jsonObject -> { + boolean passesFilter = JacksonUtil.filter(jsonObject, pairs); + if (passesFilter && StringUtils.isNotBlank(entityIds)) { + String[] entityIdList = entityIds.split(","); + long entityId = jsonObject.get(CacheNameEnum.DictField.THING_DICT_ENTITY_ID.getField()).asLong(); + passesFilter = ArrayUtils.contains(entityIdList, String.valueOf(entityId)); + } + return passesFilter; + }) + .filter(jsonObject -> JacksonUtil.filterOr(jsonObject, entityName,CacheNameEnum.DictField.THING_DICT_ENTITY_NAME.getField(),CacheNameEnum.DictField.THING_DICT_ENTITY_CODE.getField())) + .filter(jsonObject -> JacksonUtil.filterOr(jsonObject, name,CacheNameEnum.DictField.THING_DICT_NAME.getField(),CacheNameEnum.DictField.THING_DICT_CODE.getField())) + .sorted(comparator.thenComparing(obj -> obj.get(CacheNameEnum.DictField.THING_DICT_ID.getField()).asLong())).toList(); + } + + @Override + public List findAll(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, null, Boolean.FALSE); + return mapper.selectListByQueryAs(queryWrapper, IotThingDictRelationDTO.class); + } + + @Override + public List noRepeatList(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, null, Boolean.FALSE); + List dictRelationDTOList = mapper.selectListByQueryAs(queryWrapper, IotThingDictRelationDTO.class); + if(CollectionUtils.isNotEmpty(dictRelationDTOList)){ + dictRelationDTOList = dictRelationDTOList.stream() + .collect(Collectors.toMap(IotThingDictRelationDTO::getCode, Function.identity(), (existing, replacement) -> existing)) + .values() + .stream() + .toList(); + } + return dictRelationDTOList; + } + + @Override + public List findAllByIdsAndEntityIds(List ids, List entityIds) { + return mapper.selectListByQueryAs(new QueryWrapper() + .in(IotThingDictRelationEntity::getId, ids, CollectionUtils.isNotEmpty(ids)) + .in(IotThingDictRelationEntity::getEntityId, entityIds, CollectionUtils.isNotEmpty(entityIds)), + IotThingDictRelationDTO.class); + } + + @Override + public List findDictRelationAllByEntityIdsAndGroupNamesAndCodes(Collection entityIds, Collection groupNames, Collection codes) { + return mapper.selectListByQueryAs(new QueryWrapper() + .in(IotThingDictRelationEntity::getEntityId, entityIds, CollectionUtils.isNotEmpty(entityIds)) + .in(IotThingDictRelationEntity::getGroupName, groupNames, CollectionUtils.isNotEmpty(groupNames)) + .in(IotThingDictRelationEntity::getCode, codes, CollectionUtils.isNotEmpty(codes) + ), + IotThingDictRelationDTO.class); + } + + @Override + public IotThingDictRelationDTO findDictRelationByEntityIdAndCode(Long entityId, String code) { + return mapper.selectOneByQueryAs(new QueryWrapper() + .eq(IotThingDictRelationEntity::getEntityId, entityId) + .eq(IotThingDictRelationEntity::getCode, code, StringUtils.isNotBlank(code)) + , IotThingDictRelationDTO.class); + } + + @Override + public IotThingDictRelationDTO findById(Long id) { + return mapper.selectOneByQueryAs(new QueryWrapper().eq(IotThingDictRelationEntity::getId, id), IotThingDictRelationDTO.class); + } + + @Override + public List findAllByEntityIdAndCodes(Long entityId, Collection codes) { + return mapper.selectListByQueryAs(new QueryWrapper() + .eq(IotThingDictRelationEntity::getEntityId, entityId) + .in(IotThingDictRelationEntity::getCode, codes, CollectionUtils.isNotEmpty(codes)) + , IotThingDictRelationDTO.class); + } + + @Override + public List findAllByEntityIdsAndCodes(Collection entityIds, Collection codes) { + return mapper.selectListByQueryAs(new QueryWrapper() + .in(IotThingDictRelationEntity::getEntityId, entityIds) + .in(IotThingDictRelationEntity::getCode, codes, CollectionUtils.isNotEmpty(codes)) + , IotThingDictRelationParamDTO.class); + } + + @Override + public List findAllByTenantCode(Long tenantCode) { + return mapper.selectListByQueryAs(new QueryWrapper() + .in(IotThingDictRelationEntity::getTenantCode, tenantCode).orderBy(IotThingDictRelationEntity::getSort, false) + , IotThingDictRelationDTO.class); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(IotThingDictRelationParamDTO dto) { + List entityIds = dto.getEntityIds(); + List entityDTOList = thingEntityService.findEntityByIds(entityIds); + if (CollectionUtils.isEmpty(entityDTOList)) { + throw new SysException("请选择正确的物实体"); + } + List dictIds = dto.getDictIds(); + List groupNames = dto.getGroupNames(); + if (CollectionUtils.isEmpty(groupNames) && CollectionUtils.isEmpty(dictIds)) { + throw new SysException("请选择正确的属性组或者属性"); + } + //获取需要绑定的字典列表 + List thingDictDTOS = dictService.findAll( + Map.of("groupNames", StringUtils.join(groupNames, ",") + , "ids", dictIds)); + //获取已经绑定的物指标信息列表 + List iotThingDictRelationEntities = mapper.selectListByQuery(QueryWrapper.create() + .in(IotThingDictRelationEntity::getEntityId, entityIds) + .in(IotThingDictRelationEntity::getCode, thingDictDTOS.stream() + .map(IotThingDictDTO::getCode).toList())); + + if (CollectionUtils.isNotEmpty(iotThingDictRelationEntities)) { + //去除已经存在的关系:通过字典code和实体id + thingDictDTOS.removeIf(d -> + iotThingDictRelationEntities.stream() + .anyMatch(relation -> StringUtils.equals(relation.getCode(), d.getCode()) + && entityIds.contains(relation.getEntityId()))); + } + //构建物与字典的关系 + List insertList = entityDTOList.stream().flatMap(entityDTO -> thingDictDTOS.stream().map(iotThingDictDTO -> { + IotThingDictRelationEntity iotThingDictRelationEntity = ConvertUtils.sourceToTarget(iotThingDictDTO, IotThingDictRelationEntity.class); + iotThingDictRelationEntity.setEntityName(entityDTO.getName()) + .setEntityCode(entityDTO.getCode()) + .setEntityId(entityDTO.getId()) + .setDataTreatingMark("0") + .setTemplateMark(entityDTO.getTemplateMark()) + .setId(null); + return iotThingDictRelationEntity; + })).toList(); + if (CollectionUtils.isNotEmpty(insertList)) { + mapper.insertBatch(insertList); + } + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveCodes(IotThingDictRelationParamDTO dto) { + List entityIds = dto.getEntityIds(); + if (CollectionUtils.isEmpty(entityIds)) { + throw new SysException("请选择正确的物实体"); + } + List groupNames = dto.getGroupNames(); + if (CollectionUtils.isEmpty(groupNames)) { + throw new SysException("请选择正确的属性组"); + } + List thingDictDTOS = dictService.findAll( + Map.of("groupNames", StringUtils.join(groupNames, ","), + "codes", dto.getDictCodes())); + + List iotThingDictRelationEntities = mapper.selectListByQuery(QueryWrapper.create() + .in(IotThingDictRelationEntity::getEntityId, entityIds) + .in(IotThingDictRelationEntity::getId, thingDictDTOS.stream() + .map(IotThingDictDTO::getId).toList())); + + if (CollectionUtils.isNotEmpty(iotThingDictRelationEntities)) { + //去除已经存在的关系 + thingDictDTOS.removeIf(item -> iotThingDictRelationEntities.stream().anyMatch(relation -> StringUtils.equals(relation.getCode(), item.getCode()))); + } + //构建物与字典的关系 + List entityDTOList = thingEntityService.findEntityByIds(entityIds); + List insertList = Optional.ofNullable(entityDTOList) + .map(relation -> relation.stream() + .flatMap(item -> + thingDictDTOS.stream() + .map(d -> ConvertUtils.sourceToTarget(d, IotThingDictRelationEntity.class) + .setEntityId(item.getId()) + .setEntityCode(item.getCode()) + .setTemplateMark(item.getTemplateMark()) + .setEntityName(item.getName()) + ) + ).toList() + ).orElse(Collections.emptyList()); + + if (CollectionUtils.isNotEmpty(insertList)) { + mapper.insertBatch(insertList); + } + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void update(IotThingDictRelationDTO dto) { + Long id = dto.getId(); + IotThingDictRelationDTO dictRelationDTO = findById(id); + if (Objects.isNull(dictRelationDTO)) { + throw new SysException("当前指标不存在"); + } + String url = BizUtils.trimAll(dto.getUrl()); + if (StringUtils.isNotBlank(url)) { + url = OSSFactory.cutOut(dto.getUrl()); + dto.setUrl(url); + } + dictRelationDTO.setUnit(dto.getUnit()) + .setRate(dto.getRate()) + .setColor(dto.getColor()) + .setUrl(url) + .setRemark(dto.getRemark()) + .setDataType(dto.getDataType()) + .setBusinessType(dto.getBusinessType()) + .setEvType(dto.getEvType()) + .setMaxNum(dto.getMaxNum()) + .setMinNum(dto.getMinNum()) + .setDataTreatingMark(dto.getDataTreatingMark() == null ? "0" : dto.getDataTreatingMark()) + .setChildConfig(dto.getChildConfig()) + ; + IotThingDictRelationEntity iotThingDictRelationEntity = ConvertUtils.sourceToTarget(dictRelationDTO, IotThingDictRelationEntity.class); + mapper.update(iotThingDictRelationEntity); + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchDeleteByIds(Long[] ids) { + mapper.deleteBatchByIds(Arrays.asList(ids)); + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveBatchDictRelation(Collection dto) { + mapper.insertBatch(ConvertUtils.sourceToTarget(dto, IotThingDictRelationEntity.class)); + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void copy(IotThingDictRelationParamDTO dto) { + List entityIds = dto.getEntityIds(); + List entityDTOList = thingEntityService.findEntityByIds(entityIds); + if (CollectionUtils.isEmpty(entityDTOList)) { + throw new SysException("请选择正确的物实体"); + } + List dictIds = dto.getDictIds(); + if (CollectionUtils.isEmpty(dictIds)) { + throw new SysException("属性信息不能为空"); + } + List iotThingDictRelationEntities = mapper.selectListByIds(dictIds); + if (CollectionUtils.isEmpty(iotThingDictRelationEntities)) { + throw new SysException("请正确选择需要操作的属性"); + } + //查询已经存在的物指标 + List dictRelationDTOList = findAllByEntityIdsAndCodes(entityIds, iotThingDictRelationEntities.stream().map(IotThingDictRelationEntity::getCode).toList()); + //构建物与字典的关系 + List insertList = entityDTOList.stream().flatMap(entityDTO -> iotThingDictRelationEntities.stream().filter(s -> dictRelationDTOList.stream() + .noneMatch(d -> StringUtils.equals(d.getCode(), s.getCode()) && Objects.equals(entityDTO.getId(), d.getEntityId()) && StringUtils.equals(d.getDataType(), s.getDataType()))) + .map(s -> { + IotThingDictRelationEntity dictRelation = ConvertUtils.sourceToTarget(s, IotThingDictRelationEntity.class); + dictRelation.setEntityId(entityDTO.getId()) + .setEntityCode(entityDTO.getCode()) + .setEntityName(entityDTO.getName()) + .setDataTreatingMark("0") + .setTemplateMark(entityDTO.getTemplateMark()) + .setId(null); + return dictRelation; + })).toList(); + if (CollectionUtils.isNotEmpty(insertList)) { + mapper.insertBatch(insertList); + } + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Override + public List groups() { + List dictRelationDTOList = findAllByTenantCode(UserContext.getRealTenantCode()); + return dictRelationDTOList.stream().map(IotThingDictRelationDTO::getGroupName).distinct().toList(); + } + + @Override + public List groups(List ids) { + List iotThingDictRelationEntities = mapper.selectListByQuery(QueryWrapper.create().in(IotThingDictRelationEntity::getEntityId, ids)); + return iotThingDictRelationEntities.stream().map(IotThingDictRelationEntity::getGroupName).distinct().toList(); + } + + @Override + public void template(HttpServletResponse response) { + ExcelUtils.exportExcel(Lists.newArrayList(), "物指标", "物指标", IotThingDictRelationExcel.class, "物指标列表模板.xls", response); + } + + @Override + public void export(Long[] ids, Map params, HttpServletResponse response) { + if (ArrayUtils.isNotEmpty(ids)) { + params.put("ids", ids); + } + List list = findAll(params); + ExcelUtils.exportExcel(ConvertUtils.sourceToTarget(list, IotThingDictRelationExcel.class), "物指标", "物指标列表", IotThingDictRelationExcel.class, "物指标列表.xls", response); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void importExcel(MultipartFile file, HttpServletRequest request) { + List sheetDataList = ExcelUtils.importExcel(file, 1, 1, IotThingDictRelationExcel.class, 0); + if (CollectionUtil.isEmpty(sheetDataList)) { + throw new SysException("导入数据为空"); + } + //根据物编码分类 + Map> listMap = sheetDataList.stream().collect(Collectors.groupingBy(IotThingDictRelationExcel::getEntityCode)); + for (Map.Entry> entry : listMap.entrySet()) { + String entityCode = entry.getKey(); + Optional optional = thingEntityService.findEntityByCode(entityCode, UserContext.getRealTenantCode(), true); + if (optional.isEmpty()) { + throw new SysException("物编码为【" + entityCode + "】的物实体不存在"); + } + List list = entry.getValue(); + if (CollectionUtils.isEmpty(list)) { + continue; + } + List codes = list.stream().map(IotThingDictRelationExcel::getCode).distinct().toList(); + List dictDTOS = dictService.findDictByCodes(codes); + + List noexistCodes = + list.stream().filter(e -> dictDTOS.stream().noneMatch(d -> StringUtils.equals(d.getCode(), e.getCode()))).toList(); + + if (CollectionUtils.isNotEmpty(noexistCodes)) { + Integer maxSort = dictService.findMaxSort(1); + AtomicInteger sort = new AtomicInteger(maxSort); + List dictEntities = noexistCodes.stream().map(e -> new IotThingDictEntity() + .setCode(e.getCode()) + .setName(e.getName()) + .setUnit(e.getUnit()) + .setDataType(e.getDataType()) + .setRemark(e.getRemark()) + .setIsDefault(1) + .setMinNum(e.getMinNum()) + .setMaxNum(e.getMaxNum()) + .setSort((long) sort.incrementAndGet()) + .setGroupName(e.getGroupName()) + ).toList(); + dictService.saveBatch(dictEntities); + } + List iotThingDictRelationEntities = ConvertUtils.sourceToTarget(list, IotThingDictRelationEntity.class); + mapper.insertBatch(iotThingDictRelationEntities); + } + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void syncOrUpdateEntity(IotSyncEntityDTO dto) { + Long id = dto.getId(); + IotThingEntityInfoDTO thingEntityDTO = thingEntityService.findEntityById(id); + if (ObjectUtil.isNull(thingEntityDTO)) { + throw new SysException("模板不存在"); + } + List dictRelationDTOList = findAllByEntityIdAndCodes(id, null); + if (CollectionUtil.isEmpty(dictRelationDTOList)) { + throw new SysException("当前物模板缺失属性信息"); + } + List entityIds = dto.getEntityIds(); + List entities = thingEntityService.findEntityByIds(entityIds); + if (CollectionUtil.isEmpty(entities)) { + throw new SysException("物实体不存在"); + } + //构建字典列表 + List insertList = new ArrayList<>(entities.stream().flatMap(e -> dictRelationDTOList.stream().map(d -> + { + IotThingDictRelationEntity iotThingDictRelationEntity = ConvertUtils.sourceToTarget(d, IotThingDictRelationEntity.class); + iotThingDictRelationEntity + .setEntityId(e.getId()) + .setEntityName(e.getName()) + .setEntityCode(e.getCode()) + .setTemplateMark(e.getTemplateMark()) + .setId(null); + return iotThingDictRelationEntity; + })).toList()); + //同步 + if(StringUtils.equals(SyncUpdateEnum.SYNC.getValue(),dto.getSyncOrUpdate())){ + mapper.deleteByQuery(QueryWrapper.create().in(IotThingDictRelationEntity::getEntityId,entityIds)); + mapper.insertBatch(insertList); + }else{ + List relationDTOList = findAllByEntityIdsAndCodes(entityIds, null); + if(CollectionUtils.isNotEmpty(relationDTOList)){ + insertList. + removeIf(s-> relationDTOList.stream().anyMatch(r-> + StringUtils.equals(r.getCode(),s.getCode()) + && StringUtils.equals(r.getDataType(),s.getDataType()) + && Objects.equals(r.getEntityId(),s.getEntityId()) + )); + } + if(CollectionUtils.isNotEmpty(insertList)){ + mapper.insertBatch(insertList); + } + } + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + + private List> buildParam(String groupName,String templateMark,String dataType,Long tenantCode){ + List> filterList = new ArrayList<>(); + if (StringUtils.isNotBlank(groupName)) { + filterList.add(Pair.of(CacheNameEnum.DictField.THING_DICT_GROUP_NAME.getField(), groupName)); + } + if (StringUtils.isNotBlank(templateMark)) { + filterList.add(Pair.of(CacheNameEnum.DictField.THING_DICT_TEMPLATE_MARK.getField(), templateMark)); + } + + if (StringUtils.isNotBlank(dataType)) { + filterList.add(Pair.of(CacheNameEnum.DictField.THING_DICT_DATA_TYPE.getField(), dataType)); + } + + if (Objects.nonNull(tenantCode)) { + filterList.add(Pair.of(CacheNameEnum.DictField.THING_DICT_TENANT_CODE.getField(), tenantCode.toString())); + } + + return filterList; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/controller/IotThingEntityController.java b/modules/thing/src/main/java/com/thing/thing/entity/controller/IotThingEntityController.java new file mode 100644 index 0000000..e9b549d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/controller/IotThingEntityController.java @@ -0,0 +1,249 @@ +package com.thing.thing.entity.controller; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.MimeTypeEnum; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.sys.oss.cloud.AbstractCloudStorageService; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.entity.dto.*; +import com.thing.thing.entity.dto.old.ApiEntityParamDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.param.DistributeEntityDTO; +import com.thing.thing.entity.param.IotThingEntityParamDTO; +import com.thing.thing.entity.param.IotThingStatusDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** +* 物实体表 +* +* @author xc +* @since 3.0 2024-03-19 +*/ +@RestController +@RequestMapping("v2/entity") +@Tag(name="物实体表") +public class IotThingEntityController { + + @Resource + private IotThingEntityService service; + + @GetMapping("page") + @Operation(summary="分页") + public Result> page( + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) @RequestParam(required = false) Integer page, + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) @RequestParam(required = false) Integer limit, + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "name", description = "名称或者编码") @RequestParam(required = false) String name, + @Parameter(name = "type", description = "物类型") @RequestParam(required = false) String type, + @Parameter(name = "deptId", description = "部门id") @RequestParam(required = false) Long deptId, + @Parameter(name = "realType", description = "真实/虚拟物") @RequestParam(required = false) String realType, + @Parameter(name = "tags", description = "真实/虚拟物") @RequestParam(required = false) String tags, + @Parameter(name = "enableStatus", description = "停用开启")@RequestParam(required = false) String enableStatus, + @Parameter(name = "templateMark", description = "是否是物实体/模板")@RequestParam(required = false) String templateMark) + { + PageData pageList = service.pageList(page,limit,orderField,order, name,type,deptId,realType,tags,enableStatus,templateMark); + return new Result>().ok(pageList); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list( + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "name", description = "名称或者编码") @RequestParam(required = false) String name, + @Parameter(name = "type", description = "物类型") @RequestParam(required = false) String type, + @Parameter(name = "deptId", description = "部门id") @RequestParam(required = false) Long deptId, + @Parameter(name = "realType", description = "真实/虚拟物") @RequestParam(required = false) String realType, + @Parameter(name = "tags", description = "真实/虚拟物") @RequestParam(required = false) String tags, + @Parameter(name = "enableStatus", description = "停用开启")@RequestParam(required = false) String enableStatus, + @Parameter(name = "enableStatus", description = "是否是物实体/模板")@RequestParam(required = false) String templateMark) + { + List list = service.findList(orderField,order,name,type, UserContext.getRealTenantCode(),deptId,realType,tags,enableStatus,templateMark); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingEntityInfoDTO data = service.findEntityById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingEntityParamDTO dto){ + //效验数据 + //ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.save(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingEntityParamDTO dto){ + //效验数据 + //ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.update(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.deleteById(ids); + return new Result<>(); + } + + @PostMapping("template") + @Operation(summary="物实体模板下载") + public void exportTemplate(HttpServletResponse response) { + service.template(response); + } + + @PostMapping("import") + @Operation(summary="物实体导入") + @LogOperation("物实体导入") + public Result importTenant(MultipartFile file, HttpServletRequest request) { + service.importExcel(file, request); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="物实体导出") + @LogOperation("物实体导出") + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + if(ArrayUtils.isNotEmpty(ids)){ + params.put("ids",ids); + } + service.exportExcel(params,response); + } + + @GetMapping("types") + @Operation(summary="物的类型列表") + public Result> types(){ + return new Result>().ok(service.types()); + } + + @GetMapping("tags") + @Operation(summary="物的类型列表") + public Result> tags(){ + return new Result>().ok(service.tags()); + } + + @PostMapping("enable") + @Operation(summary="批量开启或停用物实体") + @LogOperation("批量开启或停用物实体") + public Result enableStatus(@Validated @RequestBody IotThingStatusDTO dto) { + service.enableStatus(dto); + return new Result<>(); + } + + @PostMapping("distribute") + @Operation(summary="权限分配") + @LogOperation("权限分配") + public Result distributePermission(@Validated @RequestBody DistributeEntityDTO dto) { + service.distributePermission(dto); + return new Result<>(); + } + + @GetMapping("lineStatus") + @Operation(summary="设备清单在线离线") + public Result> getThingLineStatus( @RequestParam Map params) { + return new Result>().ok(service.getThingLineStatus(params)); + } + + @GetMapping("groupType") + @Operation(summary="根据类型分组:type-tag(标签分组),type-type(类型分组,默认分组)") + public Result>> groupType(String type) { + return new Result>>().ok(service.groupType(type)); + } + + + @PostMapping("apiEntityList") + @Operation(summary="数据集指定id测试") + public Result> apiEntityList(@RequestBody ApiEntityReqDTO apiEntityReqDTO){ + return new Result>().ok(service.apiEntityList(apiEntityReqDTO)); + } + + @GetMapping("telemetryPage") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> telemetryPage(@RequestParam Map params){ + return new Result>().ok(service.telemetryPage(params)); + } + + @GetMapping("telemetryList") + @Operation(summary="列表") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始", required = true) , + @Parameter(name = Constant.LIMIT, description = "每页显示记录数", required = true) , + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") , + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> telemetryList(@RequestParam Map params){ + return new Result>().ok(service.telemetryList(params)); + } + + + @PostMapping("queryThingAttrs") + @Operation(summary="数据集指定id测试") + public Result> findEntityAttrs(@RequestBody ApiEntityParamDTO entityAllDTO){ + return new Result>().ok(service.findThingsAttrsByCondition(entityAllDTO)); + } + + @PostMapping("image/upload") + @Operation(summary="物图片上传") + public Result imageUpload(@RequestParam("file") MultipartFile file, @RequestParam("thingId") Long thingId) throws IOException { + AbstractCloudStorageService storageService = OSSFactory.build(); + if(storageService == null) { + return new Result().error("获取云存储配置信息失败"); + } + if(!FilenameUtils.isExtension(StringUtils.lowerCase(file.getOriginalFilename()), MimeTypeEnum.getImageMimeType())) { + return new Result().error("请上传图片文件"); + } + String url = storageService.uploadSuffix(file.getInputStream(), FilenameUtils.getExtension(file.getOriginalFilename())); + String completeUrl = OSSFactory.splice(url); + + IotThingEntity entity = new IotThingEntity(); + entity.setId(thingId); + entity.setImg(completeUrl); + service.updateById(entity); + + return new Result().ok(completeUrl); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/ApiEntityReqDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/ApiEntityReqDTO.java new file mode 100644 index 0000000..d8e6901 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/ApiEntityReqDTO.java @@ -0,0 +1,43 @@ +package com.thing.thing.entity.dto; + +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +/** + * @author mark + * 数据集中总参数对象 + **/ +@Data +public class ApiEntityReqDTO implements Serializable { + + + @Serial + private static final long serialVersionUID = -7160193353514097608L; + + private List list; + + private List type; + + private List tag; + + private Map> search; + + private List group; + + private List relation; + + /** + * 租户code + */ + private Long tenantCode; + + + boolean isEntity; + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/DataApiRspDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/DataApiRspDTO.java new file mode 100644 index 0000000..3622b52 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/DataApiRspDTO.java @@ -0,0 +1,20 @@ +package com.thing.thing.entity.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Map; + +@Data +public class DataApiRspDTO implements Serializable { + @Serial + private static final long serialVersionUID = 9082456519571719409L; + /** + * 基本信息 + */ + private Map info; + + private Object values; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotBasicsThingEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotBasicsThingEntityDTO.java new file mode 100644 index 0000000..0dbf198 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotBasicsThingEntityDTO.java @@ -0,0 +1,56 @@ +package com.thing.thing.entity.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 物实体表 +* +* @author mark +* @since 3.0 2024-03-19 +*/ +@Data +public class IotBasicsThingEntityDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "物编码") + private String code; + @Schema(description = "租户内物名称,租户自定义") + private String name; + @Schema(description = "租户内物存在类型,0虚拟 1真实") + private String realType; + @Schema(description = "物类型") + private String type; + @Schema(description = "启用停用,0停用,1启用,(停用的物,不会再获取数据且不会展示在页面,超管除外)") + private String enableStatus; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "图片url") + private String img; + @Schema(description = "权限分配:部门id(逗号拼接)") + private String deptIds; + @Schema(description = "标签") + private String tags; + @Schema(description = "经度") + private String lon; + @Schema(description = "纬度") + private String lat; + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDTO.java new file mode 100644 index 0000000..6b3af58 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDTO.java @@ -0,0 +1,29 @@ +package com.thing.thing.entity.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +@EqualsAndHashCode(callSuper = true) +@Data +@Schema(description = "物实体表") +public class IotThingEntityDTO extends IotBasicsThingEntityDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "物模板标识") + private String templateMark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDictDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDictDTO.java new file mode 100644 index 0000000..42b5431 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityDictDTO.java @@ -0,0 +1,23 @@ +package com.thing.thing.entity.dto; + +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@EqualsAndHashCode(callSuper = true) +@Data +@Schema(description = "物实体表") +public class IotThingEntityDictDTO extends IotBasicsThingEntityDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "物属性集合(超级API)") + private List dictList; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityInfoDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityInfoDTO.java new file mode 100644 index 0000000..928749c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingEntityInfoDTO.java @@ -0,0 +1,48 @@ +package com.thing.thing.entity.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +@Data +public class IotThingEntityInfoDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "物编码") + private String code; + @Schema(description = "租户内物名称,租户自定义") + private String name; + @Schema(description = "租户内物存在类型,0虚拟 1真实") + private String realType; + @Schema(description = "物类型") + private String type; + @Schema(description = "启用停用,0停用,1启用,(停用的物,不会再获取数据且不会展示在页面,超管除外)") + private String enableStatus; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "租户编码") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "图片url") + private String img; + @Schema(description = "权限分配:部门id(逗号拼接)") + private String[] deptIds; + @Schema(description = "标签") + private String[] tags; + @Schema(description = "经度") + private String lon; + @Schema(description = "纬度") + private String lat; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewDTO.java new file mode 100644 index 0000000..79a70a1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewDTO.java @@ -0,0 +1,100 @@ +package com.thing.thing.entity.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@NoArgsConstructor +public class IotThingViewDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "创建时间") + @JsonSerialize(using = ToStringSerializer.class) + private Long createDate; + + @Schema(description = "物模型id") + @JsonSerialize(using = ToStringSerializer.class) + private Long modelId; + + @Schema(description = "物实体id") + @JsonSerialize(using = ToStringSerializer.class) + private Long entityId; + + @Schema(description = "物实体名称") + private String entityName; + + @Schema(description = "物实体编码") + private String entityCode; + + @Schema(description = "物类型") + private String entityType; + + @Schema(description = "租户编码") + @JsonSerialize(using = ToStringSerializer.class) + private Long tenantCode; + + @Schema(description = "公司id") + @JsonSerialize(using = ToStringSerializer.class) + private Long companyId; + + @Schema(description = "部门id") + @JsonSerialize(using = ToStringSerializer.class) + private Long deptId; + + @Schema(description = "部门名称") + private String deptName; + + @Schema(description = "物模型的在线状态") + private String status; + + @Schema(description = "物模型状态更新时间") + @JsonSerialize(using = ToStringSerializer.class) + private Long statusTs; + + @Schema(description = "设备的启停") + private String enableStatus; + + @Schema(description = "备注说明") + private String remark; + + @Schema(description = "图片地址") + private String img; + +// @Schema(description = "关系id") +// @JsonSerialize(using = ToStringSerializer.class) +// private Long relationId; + + @Schema(description = "存在类型:0虚拟1真实") + private String realType; + + @Schema(description = "物模板:0实体 1模板") + private String templateMark; + + @Schema(description = "数据来源") + private String origin; + + @Schema(description = "权限分配:部门id") + private String deptIds; + + @Schema(description = "标签") + private String tags; + + @Schema(description = "经度") + private String lon; + + @Schema(description = "纬度") + private String lat; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewSourceDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewSourceDTO.java new file mode 100644 index 0000000..e1ff4dc --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/IotThingViewSourceDTO.java @@ -0,0 +1,37 @@ +package com.thing.thing.entity.dto; + +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; +import java.util.Map; + + +/** + * 物实体表:配置中心的类 + * + * @author mark + * @since 3.0 2024-03-19 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@NoArgsConstructor +public class IotThingViewSourceDTO extends IotThingViewDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "物属性相关信息:主要返回给前端超级API使用") + private Map attrs; + + @Schema(description = "物属性集合信息:暂时使用,后期删除") + private List dictList; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrArrDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrArrDTO.java new file mode 100644 index 0000000..f0a07a7 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrArrDTO.java @@ -0,0 +1,32 @@ +package com.thing.thing.entity.dto.old; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/* + * 数据集中属性相关参数对象 + * */ +@Data +public class ApiEntityAttrArrDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1870555093936521615L; + /** + * 设备分属性类型:all 全部 split 分属性 + */ + private String type; + + /** + * TS时序属性暂时默认 + */ + private String keyType; + + /** + * 当分属性的时候,使用这个集合 + */ + private List keys; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrDTO.java new file mode 100644 index 0000000..c438663 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrDTO.java @@ -0,0 +1,31 @@ +package com.thing.thing.entity.dto.old; + +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/* + * 数据集中属性相关参数对象 + * */ +@Data +public class ApiEntityAttrDTO implements Serializable { + + private static final long serialVersionUID = 1870555093936521615L; + /** + * 设备分属性类型:all 全部 split 分属性 none + */ + private String type; + + /** + * 分属性条件 entitys 实体列表 keys : 实体属性 keyType TS时序属性暂时默认 + * 不分属性使用这个集合 + */ + private List keys; + + /** + * TS时序属性暂时默认 + */ + private String keyType; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrsDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrsDTO.java new file mode 100644 index 0000000..ac3dded --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityAttrsDTO.java @@ -0,0 +1,26 @@ +package com.thing.thing.entity.dto.old; + +import lombok.Data; + +import java.util.List; + +/** + * @author ls + * @date 2023/6/27 19:35 + * @describe + */ +@Data +public class ApiEntityAttrsDTO { + + /** + * 实体id集合 + */ + private List entitys; + + /** + * 属性id集合 + */ + private List keys; + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityDTO.java new file mode 100644 index 0000000..1962b5d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityDTO.java @@ -0,0 +1,54 @@ +package com.thing.thing.entity.dto.old; + +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/* +* 超级API物相关参数对象 +* */ +@Data +public class ApiEntityDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 4999387168737966053L; + /** + * 组id集合 + */ + private List group; + + /** + * 物id集合 + */ + private List list; + + /** + * 物类型集合 + */ + private List type; + + /** + * 物标签集合 + */ + private List tag; + + /** + * 物搜索条件 + */ + private ApiEntitySearchDTO search; + + /** + * 物属性条件 + */ + private List attrs; + + /** + * 物关系 + */ + private List relation; + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityParamDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityParamDTO.java new file mode 100644 index 0000000..783aa93 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntityParamDTO.java @@ -0,0 +1,56 @@ +package com.thing.thing.entity.dto.old; + +import com.thing.common.orm.dto.BaseDTO; +import com.thing.thing.api.dto.ApiTimeDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +/** + * @author zzx + * 数据集中总参数对象 + **/ +@EqualsAndHashCode(callSuper = true) +@Data +public class ApiEntityParamDTO extends BaseDTO { + + /** + * 物条件的类 + */ + private ApiEntityDTO entitys; + + /** + * 属性不分列表 + */ + private ApiEntityAttrDTO attrs; + + /** + * 分属性条件列表 + */ + private ApiEntityAttrArrDTO splitAttrs; + + /** + * 时间列表 + */ + private ApiTimeDTO times; + + /** + * 排序 + */ + private String sort; + + /** + * 分页参数 + */ + private Integer page; + + /** + * 分页参数 + */ + private Integer limit; + + /** + * 租户code + */ + private Long tenantCode; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntitySearchDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntitySearchDTO.java new file mode 100644 index 0000000..ced3e56 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/dto/old/ApiEntitySearchDTO.java @@ -0,0 +1,21 @@ +package com.thing.thing.entity.dto.old; + +import com.thing.common.orm.dto.BaseDTO; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.util.List; + +/** + * @author zzx + * 数据集中总参数对象 + **/ +@EqualsAndHashCode(callSuper = true) +@Data +public class ApiEntitySearchDTO extends BaseDTO { + + private List name; + + private List code; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/entity/IotThingEntity.java b/modules/thing/src/main/java/com/thing/thing/entity/entity/IotThingEntity.java new file mode 100644 index 0000000..95c27ed --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/entity/IotThingEntity.java @@ -0,0 +1,101 @@ +package com.thing.thing.entity.entity; + +import com.mybatisflex.annotation.Column; +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; +import org.apache.ibatis.type.ArrayTypeHandler; + +import java.io.Serial; + +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_entity") +public class IotThingEntity extends BaseDateEntity { + + @Serial + private static final long serialVersionUID = 1L; + + + /*------------------------修改记录信息--------------------------------*/ + + /** + * thing_code + */ + private String code; + /** + * 租户内物名称,租户自定义 + */ + private String name; + /** + * 租户内物存在类型,0虚拟 1真实 + */ + private String realType; + /** + * 是否是模板,1模板,0 非模板(默认) + */ + private String templateMark; + /** + * 物类型 + */ + private String type; + /** + * 启用停用,0停用,1启用,(停用的物,不会再获取数据且不会展示在页面,超管除外) + */ + private String enableStatus; + /** + * 备注说明 + */ + private String remark; + /** + * 拓展字段 + */ + private String extendData; + + /** + * 图片url + */ + private String img; + + /** + * 权限分配:部门id + */ + //@Column(typeHandler = ArrayTypeHandler.class) + private String deptIds; + + /** + * 标签 + */ + //@Column(typeHandler = ArrayTypeHandler.class) + private String tags; + /** + * 经度 + */ + private String lon; + /** + * 纬度 + */ + private String lat; + + /*------------------------租户信息--------------------------------*/ + + /** + * 租户编码 + */ + private Long tenantCode; + + /** + * 公司id + */ + private Long companyId; + + /** + * 在线离线 + */ +// private String status; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/excel/IotThingViewExcel.java b/modules/thing/src/main/java/com/thing/thing/entity/excel/IotThingViewExcel.java new file mode 100644 index 0000000..2cac215 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/excel/IotThingViewExcel.java @@ -0,0 +1,60 @@ +package com.thing.thing.entity.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import cn.afterturn.easypoi.excel.annotation.ExcelCollection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serial; +import java.io.Serializable; +import java.lang.reflect.Array; + +/** + * 物实体表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@NoArgsConstructor +@AllArgsConstructor +public class IotThingViewExcel implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + @Excel(name = "物编码",width = 25) + private String entityCode; + @Excel(name = "物名称",orderNum = "1",width = 25 ) + private String entityName; + @Excel(name = "物类型",orderNum = "2",width = 25) + private String entityType; + @Excel(name = "存在类型(0:虚拟1:真实)",orderNum = "3",width = 20) + private String realType; + @Excel(name = "在线离线状态:0离线 1在线 2错误 3未接入",orderNum = "4",width = 25) + private String status; + @Excel(name = "启停:0停止 1启用",orderNum = "5",width = 20) + private String enableStatus; + @Excel(name = "备注说明",orderNum = "6",width = 25) + private String remark; + @Excel(name = "标签", orderNum = "7",width = 25) + private String tag; + + public void setTags(String[] tags) { + this.tags = tags; + if(ArrayUtils.isNotEmpty(tags)){ + this.tag = StringUtils.join(tags,","); + } + } + private String[] tags; + private Long entityId; + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/mapper/IotThingEntityMapper.java b/modules/thing/src/main/java/com/thing/thing/entity/mapper/IotThingEntityMapper.java new file mode 100644 index 0000000..c9481ac --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/mapper/IotThingEntityMapper.java @@ -0,0 +1,23 @@ +package com.thing.thing.entity.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.entity.entity.IotThingEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 物实体表 +* +* @author xc +* @since 3.0 2024-03-19 +*/ +@Mapper +public interface IotThingEntityMapper extends PowerBaseMapper { + + IotThingEntity selectCode(@Param("code") String code); + + void batchSaveOrUpdate(@Param("entities") List entities); +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/param/DistributeEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/param/DistributeEntityDTO.java new file mode 100644 index 0000000..11d7a55 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/param/DistributeEntityDTO.java @@ -0,0 +1,30 @@ +package com.thing.thing.entity.param; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 物模型表 + * + * @author mark + * @since 3.0 2023-06-05 + */ +@Data +@Schema( name= "物模型表") +public class DistributeEntityDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "分配租户编码") + private List deptIds; + + @Schema(description = "物编码集合") + @NotEmpty(message = "物的主键集合不能为空") + private List ids; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/param/IotSyncEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/param/IotSyncEntityDTO.java new file mode 100644 index 0000000..2b15fa1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/param/IotSyncEntityDTO.java @@ -0,0 +1,31 @@ +package com.thing.thing.entity.param; + + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +public class IotSyncEntityDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + @Schema(description = "物实体主键集合") + @NotEmpty(message = "物实体id不能为空") + List entityIds; + + @Schema(description = "物实体模板") + @NotNull(message = "物模板不能为空") + Long id; + + @Schema(description = "同步/更新的状态码") + @NotBlank(message="同步或更新的状态码不能为空") + private String syncOrUpdate; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingEntityParamDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingEntityParamDTO.java new file mode 100644 index 0000000..1591070 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingEntityParamDTO.java @@ -0,0 +1,57 @@ +package com.thing.thing.entity.param; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 物实体表 +* +* @author xc +* @since 3.0 2024-03-19 +*/ +@Data +@Schema(description = "物实体表") +public class IotThingEntityParamDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键ID") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "物编码") + private String code; + @Schema(description = "租户内物名称,租户自定义") + private String name; + @Schema(description = "物类型") + private String type; + @Schema(description = "标签") + private List tags; + @Schema(description = "权限分配:部门id(逗号拼接)") + private List deptIds; + @Schema(description = "租户内物存在类型,0虚拟 1真实") + private String realType; + @Schema(description = "启用停用,0停用,1启用,(停用的物,不会再获取数据且不会展示在页面,超管除外)") + private String enableStatus; + @Schema(description = "图片url") + private String img; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "经度") + private String lon; + @Schema(description = "纬度") + private String lat; + @Schema(description = "是否是模板,1模板,0 实体(默认)") + private String templateMark; +// @Schema(description = "属性组(新增指标)") +// private List groupNames; +// @Schema(description = "属性id集合(新增指标/属性移动)") +// private List dictIds; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingStatusDTO.java b/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingStatusDTO.java new file mode 100644 index 0000000..37f48ae --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/param/IotThingStatusDTO.java @@ -0,0 +1,17 @@ +package com.thing.thing.entity.param; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Data +public class IotThingStatusDTO { + + @NotNull(message = "启用停用状态不能为空") + private String enableStatus; + + @NotEmpty(message = "物实体id不能为空") + private List ids; +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/service/IotThingEntityService.java b/modules/thing/src/main/java/com/thing/thing/entity/service/IotThingEntityService.java new file mode 100644 index 0000000..95ca6c6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/service/IotThingEntityService.java @@ -0,0 +1,106 @@ +package com.thing.thing.entity.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.entity.dto.*; +import com.thing.thing.entity.dto.old.ApiEntityParamDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.param.DistributeEntityDTO; +import com.thing.thing.entity.param.IotThingEntityParamDTO; +import com.thing.thing.entity.param.IotThingStatusDTO; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +/** + * 物实体表 + * + * @author xc + * @since 3.0 2024-03-19 + */ +public interface IotThingEntityService extends IBaseService { + + PageData pageList(Integer page,Integer limit,String orderField,String order, + String code,String type,Long deptId,String realType,String tags,String enableStatus,String templateMark); + + List findList(String orderField,String order, + String name,String type,Long tenantCode,Long deptId,String realType,String tags,String enableStatus,String templateMark); + + List findAll(Map params); + + IotThingViewDTO findById(Long id); + + IotThingEntityInfoDTO findEntityById(Long id); + + List findByIds(List ids); + + Optional findByCode(String code, Long tenantCode,boolean isEntity); + + Optional findEntityByThingList(Long thingId, Long tenantCode, boolean isEntity); + + List findEntityByIds(Collection ids); + + List findEntityAllByCode(Collection codes,Long tenantCode, boolean isEntity); + + Optional findEntityByCode(String code, Long tenantCode,boolean isEntity); + + Optional> findByCodes(Collection codes,Long tenantCode,boolean isEntity); + + List findAllByCodeInAndTenantCode(Collection codes, Long tenantCode); + + void save(IotThingEntityParamDTO dto); + + void update(IotThingEntityParamDTO dto); + + void deleteById(Long[] ids); + + List types(); + + List tags(); + + void enableStatus(IotThingStatusDTO dto); + + void distributePermission(DistributeEntityDTO dto); + + void template(HttpServletResponse response); + + void exportExcel(Map params, HttpServletResponse response); + + void importExcel(MultipartFile file, HttpServletRequest request); + + List findEntityByCondition(List ids, + List types, + List tags, + List name, + List code, + List group, + List relation, + Long tenantCode, + boolean isEntity); + + void batchSaveOrUpdate(List collect); + + void saveBatchEntity(List entities); + + Map getThingLineStatus(Map params); + + List> groupType(String type); + + List apiEntityList(ApiEntityReqDTO apiEntityReqDTO); + + PageData telemetryPage(Map params); + + List telemetryList(Map params); + + List findThingsAttrsByCondition(ApiEntityParamDTO entityAllDTO); + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/entity/service/impl/IotThingEntityServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/entity/service/impl/IotThingEntityServiceImpl.java new file mode 100644 index 0000000..7339c67 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/entity/service/impl/IotThingEntityServiceImpl.java @@ -0,0 +1,982 @@ +package com.thing.thing.entity.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.NullNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.CaseFormat; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.mybatisflex.core.query.QueryChain; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.*; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.biz.entity.SysDeptEntity; +import com.thing.sys.biz.mapper.SysDeptMapper; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.dto.*; +import com.thing.thing.entity.dto.old.ApiEntityDTO; +import com.thing.thing.entity.dto.old.ApiEntityParamDTO; +import com.thing.thing.entity.dto.old.ApiEntitySearchDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.excel.IotThingViewExcel; +import com.thing.thing.entity.mapper.IotThingEntityMapper; +import com.thing.thing.entity.param.DistributeEntityDTO; +import com.thing.thing.entity.param.IotThingEntityParamDTO; +import com.thing.thing.entity.param.IotThingStatusDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.group.entity.IotGroupRelationEntity; +import com.thing.thing.group.service.IotGroupRelationService; +import com.thing.thing.model.dto.ModelDetailDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.thing.model.service.IotThingModelService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import com.thing.transport.api.adaptor.JsonConverter; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.thing.thing.entity.entity.table.IotThingEntityTableDef.IOT_THING_ENTITY; +import static com.thing.thing.model.entity.table.IotThingModelEntityTableDef.IOT_THING_MODEL_ENTITY; + + +/** + * 物实体表 + * + * @author xc + * @since 3.0 2024-03-19 + */ +@Slf4j +@Service +public class IotThingEntityServiceImpl extends BaseServiceImpl implements IotThingEntityService { + + @Resource + private IotThingDictRelationService dictRelationService; + + @Resource + private IotThingRelationDetailService relationDetailService; + + @Resource + private IotThingModelService modelService; + + @Resource + private SysDeptMapper sysDeptMapper; + + @Resource + private TsKvService tsKvService; + + @Resource + private ThingCache cache; + + @Resource + private IotGroupRelationService groupRelationService; + + @Override + public QueryWrapper getWrapper(Map params) { + String name = MapUtil.getStr(params, "name"); + String code = MapUtil.getStr(params, "code"); + Long tenantCode = MapUtil.getLong(params, "tenantCode"); + if(Objects.isNull(tenantCode)){ + tenantCode = UserContext.getRealTenantCode(); + } + String type = MapUtil.getStr(params, "type"); + Long deptId = MapUtil.getLong(params, "deptId"); + String realType = MapUtil.getStr(params, "realType"); + String enableStatus = MapUtil.getStr(params, "enableStatus"); + String status = MapUtil.getStr(params, "status"); + String templateMark = MapUtil.getStr(params, "templateMark"); + List ids = MapUtil.get(params, "ids", List.class); + Long id = MapUtil.getLong(params, "id"); + + QueryWrapper wrapper = QueryWrapper.create(); + + if(StringUtils.isNotBlank(name)){ + wrapper.and(IOT_THING_ENTITY.NAME.like(name).or(IOT_THING_ENTITY.CODE.like(name))); + } + + if(StringUtils.isNotBlank(code)){ + wrapper.and(IOT_THING_ENTITY.NAME.like(code).or(IOT_THING_ENTITY.CODE.like(code))); + } + wrapper.select( + IOT_THING_ENTITY.ID.as("entity_id"), + IOT_THING_ENTITY.CODE.as("entity_code"), + IOT_THING_ENTITY.NAME.as("entity_name"), + IOT_THING_ENTITY.REAL_TYPE.as("real_type"), + IOT_THING_ENTITY.TEMPLATE_MARK.as("template_mark"), + IOT_THING_ENTITY.TYPE.as("entity_type"), + IOT_THING_ENTITY.ENABLE_STATUS, + IOT_THING_ENTITY.REMARK, + IOT_THING_ENTITY.IMG, + IOT_THING_ENTITY.CREATE_DATE, + IOT_THING_ENTITY.TENANT_CODE, + IOT_THING_ENTITY.COMPANY_ID, + IOT_THING_ENTITY.DEPT_IDS, + IOT_THING_ENTITY.TAGS, + IOT_THING_ENTITY.LON, + IOT_THING_ENTITY.LAT, + IOT_THING_MODEL_ENTITY.ID.as("model_id"), + IOT_THING_MODEL_ENTITY.STATUS.as("status"), + IOT_THING_MODEL_ENTITY.STATUS_TS, + IOT_THING_MODEL_ENTITY.ORIGIN + ) + .from(IOT_THING_ENTITY.as("e")) + .leftJoin(IOT_THING_MODEL_ENTITY).as("m") + .on(IOT_THING_ENTITY.CODE.eq(IOT_THING_MODEL_ENTITY.CODE)) + .eq(IotThingEntity::getCode, code, StrUtil.isNotBlank(code)) + .eq(IotThingEntity::getType, type, StrUtil.isNotBlank(type)) + .eq(IotThingEntity::getId, id, ObjectUtil.isNotNull(id)) + .eq(IotThingEntity::getRealType, realType, StrUtil.isNotBlank(realType)) + .eq(IotThingEntity::getEnableStatus, enableStatus, StrUtil.isNotBlank(enableStatus)) + .eq(IotThingEntity::getTemplateMark, templateMark, StrUtil.isNotBlank(templateMark)) + + .eq(IotThingEntity::getTenantCode, tenantCode, ObjectUtil.isNotNull(tenantCode)) + .eq(IotThingModelEntity::getStatus, status, StringUtils.isNotBlank(status)) + .in(IotThingEntity::getId, ids, CollectionUtil.isNotEmpty(ids)); + if(Objects.nonNull(deptId)){ + wrapper.like(IotThingEntity::getDeptIds, deptId.toString()); + } + return wrapper; + } + + public QueryWrapper getWrapper( String orderField, + String order, + String code, + String name, + String type, + Long tenantCode, + Long deptId, + String realType, + String enableStatus, + String templateMark){ + QueryWrapper wrapper = QueryWrapper.create(); + if(StringUtils.isNotBlank(code)){ + wrapper.and(IOT_THING_ENTITY.NAME.like(code).or(IOT_THING_ENTITY.CODE.like(code))); + } + if(StringUtils.isNotBlank(name)){ + wrapper.and(IOT_THING_ENTITY.NAME.like(name).or(IOT_THING_ENTITY.CODE.like(name))); + } + wrapper.select( + IOT_THING_ENTITY.ID.as("entity_id"), + IOT_THING_ENTITY.CODE.as("entity_code"), + IOT_THING_ENTITY.NAME.as("entity_name"), + IOT_THING_ENTITY.REAL_TYPE.as("real_type"), + IOT_THING_ENTITY.TEMPLATE_MARK.as("template_mark"), + IOT_THING_ENTITY.TYPE.as("entity_type"), + IOT_THING_ENTITY.ENABLE_STATUS, + IOT_THING_ENTITY.REMARK, + IOT_THING_ENTITY.IMG, + IOT_THING_ENTITY.CREATE_DATE, + IOT_THING_ENTITY.TENANT_CODE, + IOT_THING_ENTITY.COMPANY_ID, + IOT_THING_ENTITY.TAGS, + IOT_THING_ENTITY.LON, + IOT_THING_ENTITY.LAT, + IOT_THING_ENTITY.DEPT_IDS, + IOT_THING_MODEL_ENTITY.ID.as("model_id"), + IOT_THING_MODEL_ENTITY.STATUS.as("status"), + IOT_THING_MODEL_ENTITY.STATUS_TS, + IOT_THING_MODEL_ENTITY.ORIGIN + ) + .from(IOT_THING_ENTITY.as("e")) + .leftJoin(IOT_THING_MODEL_ENTITY).as("m") + .on(IOT_THING_ENTITY.CODE.eq(IOT_THING_MODEL_ENTITY.CODE)) + .eq(IotThingEntity::getCode, code, StrUtil.isNotBlank(code)) + .eq(IotThingEntity::getType, type, StrUtil.isNotBlank(type)) + .like(IotThingEntity::getDeptIds, String.valueOf(deptId), Objects.nonNull(deptId)) + .eq(IotThingEntity::getRealType, realType, StrUtil.isNotBlank(realType)) + .eq(IotThingEntity::getEnableStatus, enableStatus, StrUtil.isNotBlank(enableStatus)) + .eq(IotThingEntity::getTemplateMark, templateMark, StrUtil.isNotBlank(templateMark)) + .eq(IotThingEntity::getTenantCode, tenantCode, Objects.nonNull(tenantCode)) + ; + + if(StringUtils.isBlank(orderField)){ + orderField = CacheNameEnum.EntityField.THING_ENTITY_CREATE_DATE.getField(); + } + wrapper.orderBy(orderField, StrUtil.equals(order, Constant.DESC)); + return wrapper; + } + + private static String getDefaultOrderField() { + return "e.create_date"; + } + + @Override + public PageData pageList( Integer page,Integer limit,String orderField,String order, + String name, + String type, + Long deptId, + String realType, + String tags, + String enableStatus, + String templateMark) { + List list = findList(orderField, order, name, type, UserContext.getRealTenantCode(),deptId, realType, tags,enableStatus, templateMark); + if (CollectionUtils.isEmpty(list)) { + return PageData.empty(); + } + List resList = PageUtils.startPage(list, page, limit); + convertInfo(resList); + return new PageData<>(resList, CollectionUtils.size(list)); + } + + @Override + public List findList(String orderField,String order, + String name, + String type, + Long tenantCode, + Long deptId, + String realType, + String tags, + String enableStatus, + String templateMark) { + List thingList = cache.getTopicMap(CacheNameEnum.THING_ENTITY); + if(CollectionUtils.isEmpty(thingList)){ + List iotThingViewDTOS = mapper.selectListByQueryAs(getWrapper(orderField,order, null, null,null,null, + null, null, null, null) + , IotThingViewDTO.class); + if(CollectionUtils.isEmpty(iotThingViewDTOS)){ + return new ArrayList<>(); + } + thingList = JsonConverter.convertToJsonObjectListObjectNode(iotThingViewDTOS); + //更新缓存 + CacheInit.entityMap(thingList,cache); + } + if(StringUtils.isBlank(orderField)){ + orderField = CacheNameEnum.EntityField.THING_ENTITY_CREATE_DATE.getField(); + } + //封装参数 + List> pairs = buildParam(name,name,type,tenantCode, + deptId,realType,tags,enableStatus,templateMark); + String finalOrderField = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, orderField); + Comparator comparator = CompareUtils.getComparator(order, finalOrderField); + return thingList.stream().filter(jsonObject -> JacksonUtil.filter(jsonObject, pairs)) + .sorted(comparator.thenComparing(obj -> obj.get(CacheNameEnum.EntityField.THING_ENTITY_ID.getField()).asLong())).toList(); + } + + @Override + public List findAll(Map params) { + QueryWrapper queryWrapper = buildOrderWrapper(params, getDefaultOrderField(), false); + return mapper.selectListByQueryAs(queryWrapper, IotThingViewDTO.class); + } + + + @Override + public IotThingViewDTO findById(Long id) { + Map params = Map.of("id", id); + QueryWrapper queryWrapper = buildOrderWrapper(params, getDefaultOrderField(), false); + return mapper.selectOneByQueryAs(queryWrapper, IotThingViewDTO.class); + } + + @Override + public IotThingEntityInfoDTO findEntityById(Long id) { + IotThingEntityDTO thingEntityDTO = mapper.selectOneByQueryAs(new QueryWrapper().eq(IotThingEntity::getId, id), IotThingEntityDTO.class); + if(Objects.isNull(thingEntityDTO)){ + return null; + } + IotThingEntityInfoDTO iotThingEntityInfoDTO = ConvertUtils.sourceToTarget(thingEntityDTO, IotThingEntityInfoDTO.class); + iotThingEntityInfoDTO.setTags( StringUtils.isNotBlank(thingEntityDTO.getTags()) ? thingEntityDTO.getTags().split(",") : new String[]{}); + iotThingEntityInfoDTO.setDeptIds( StringUtils.isNotBlank(thingEntityDTO.getDeptIds()) ? thingEntityDTO.getDeptIds().split(",") : new String[]{}); + return iotThingEntityInfoDTO; + } + + @Override + public List findByIds(List ids) { + Map params = Map.of("ids", ids); + QueryWrapper queryWrapper = buildOrderWrapper(params, getDefaultOrderField(), false); + return mapper.selectListByQueryAs(queryWrapper, IotThingViewDTO.class); + } + + @Override + public Optional findByCode(String code, Long tenantCode,boolean isEntity) { + Map params = new HashMap<>(Map.of("code", code, "templateMark", isEntity?"0":"1")); + if (ObjectUtil.isNotNull(tenantCode)){ + params.put("tenantCode",tenantCode); + } + QueryWrapper queryWrapper = buildOrderWrapper(params, getDefaultOrderField(), false); + return Optional.ofNullable(mapper.selectOneByQueryAs(queryWrapper, IotThingViewDTO.class)); + } + + @Override + public Optional findEntityByThingList(Long thingId, Long tenantCode, boolean isEntity) { + return Optional.ofNullable(mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingEntity::getId, thingId) + .eq(IotThingEntity::getTenantCode, tenantCode) + .eq(IotThingEntity::getTemplateMark,isEntity ? "0": "1"), IotThingEntityDTO.class)); + } + + @Override + public List findEntityByIds(Collection ids) { + return mapper.selectListByQueryAs(QueryWrapper.create().in(IotThingEntity::getId, ids), IotThingEntityDTO.class); + } + + @Override + public List findEntityAllByCode(Collection codes, Long tenantCode, boolean isEntity) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingEntity::getCode,codes,CollectionUtils.isNotEmpty(codes)) + .eq(IotThingEntity::getTenantCode, tenantCode,ObjectUtil::isNotNull) + .eq(IotThingEntity::getTemplateMark,isEntity ? "0" : "1"), IotThingEntityDTO.class); + } + + @Override + public Optional findEntityByCode(String code, Long tenantCode,boolean isEntity) { + return Optional.ofNullable(mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingEntity::getCode, code) + .eq(IotThingEntity::getTenantCode, tenantCode) + .eq(IotThingEntity::getTemplateMark,isEntity ? "0": "1"), IotThingEntityDTO.class)); + } + + @Override + public Optional> findByCodes(Collection codes,Long tenantCode,boolean isEntity) { + return Optional.ofNullable(mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingEntity::getCode, codes,CollectionUtils.isNotEmpty(codes)) + .in(IotThingEntity::getCode, codes,CollectionUtils.isNotEmpty(codes)) + .eq(IotThingEntity::getTemplateMark,isEntity? "0":"1") + ,IotThingEntityDTO.class)); + } + + @Override + public List findAllByCodeInAndTenantCode(Collection codes, Long tenantCode) { + return mapper.selectListByQuery(QueryWrapper.create() + .in(IotThingEntity::getCode, codes) + .eq(IotThingEntity::getTenantCode, tenantCode, ObjectUtil::isNotNull)); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(IotThingEntityParamDTO dto) { + //物编码去空格 + String code = BizUtils.trimAll(dto.getCode()); + //根据用户查询物实体编码 + Optional optional = findByCode(code, UserContext.getRealTenantCode(), + ObjectUtil.equals(TemplateMark.NO.getValue(), dto.getTemplateMark())); + if (optional.isPresent()) { + throw new SysException("物编码已存在,请勿重复添加"); + } + //存在类型 + String realType = dto.getRealType(); + if (ThingRealType.noneMatch(realType)) { + throw new SysException("存在类型不正确"); + } + String img = BizUtils.trimAll(dto.getImg()); + if (StringUtils.isNotBlank(img)) { + img = OSSFactory.cutOut(img); + } + IotThingEntity thingEntity = new IotThingEntity().setCode(code) + .setName(BizUtils.trimAll(dto.getName())) + .setRemark(dto.getRemark()) + .setTemplateMark(dto.getTemplateMark()) + .setRealType(realType) + .setImg(img) + .setType(ObjectUtil.isNull(dto.getType()) ? DefaultType.THING.getValue() : BizUtils.trimAll(dto.getType())) + .setDeptIds(StringUtils.join(dto.getDeptIds(), ",")) + .setRealType(realType) + .setTags(StringUtils.join(dto.getTags(), ",")) + .setLat(dto.getLat()) + .setLon(dto.getLon()) + .setCompanyId(UserContext.getRealCompanyId()) + .setTenantCode(UserContext.getRealTenantCode()) + ; + if (ObjectUtil.equals(TemplateMark.YES.getValue(), dto.getTemplateMark())) { + thingEntity.setEnableStatus(ThingEnableStatus.START.getValue()); + mapper.insert(thingEntity); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + //物模板的新增到此结束 + return; + } + String enableStatus = dto.getEnableStatus(); + //物实体的新增 + if (ThingEnableStatus.noneMatch(enableStatus)) { + throw new SysException("启停状态不正确"); + } + thingEntity.setEnableStatus(enableStatus); + //新增物实体 + mapper.insert(thingEntity); + Optional modelDTOOptional = modelService.findByCode(code); + IotThingModelEntity modelEntity; + if(modelDTOOptional.isPresent()){ + modelEntity = ConvertUtils.sourceToTarget(modelDTOOptional.get(), IotThingModelEntity.class); + modelEntity.setAuthNum(modelEntity.getAuthNum()+1); + modelService.updateById(modelEntity); + //更新物模型缓存 + cache.clearTopic(CacheNameEnum.THING_MODEL); + }else{ + //新增物模型 + modelEntity = new IotThingModelEntity() + .setCode(code) + .setToken(TokenGenerator.generateValue()) + .setStatus(ThingStatus.NOT_CONNECTED.getCode()) + .setOrigin(QueueOriginType.MQTT_CLIENT.name()) + .setRealType(realType) + .setAuthNum(1L); + modelService.save(modelEntity); + //更新物模型缓存 + cache.clearTopic(CacheNameEnum.THING_MODEL); + } + //更新物实体缓存 + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void update(IotThingEntityParamDTO dto) { + IotThingEntity thingEntity = mapper.selectOneById(dto.getId()); + if (ObjectUtil.isNull(thingEntity)) { + throw new SysException("该物不存在"); + } + String realType = dto.getRealType(); + if (ThingRealType.noneMatch(realType)) { + throw new SysException("存在类型不正确"); + } + String templateMark = dto.getTemplateMark(); + if (TemplateMark.noneMatch(templateMark)) { + throw new SysException("实体模板标识不正确"); + } + String code = BizUtils.trimAll(dto.getCode()); + String img = BizUtils.trimAll(dto.getImg()); + if (StringUtils.isNotBlank(img)) { + img = OSSFactory.cutOut(img); + } + String deptIds = thingEntity.getDeptIds(); + if(CollectionUtils.isNotEmpty(dto.getDeptIds())){ + if(StringUtils.isNotBlank(deptIds)){ + deptIds = StringUtils.join( + Stream.concat(Arrays.stream(deptIds.split(",")).map(Long::parseLong), + dto.getDeptIds().stream()).distinct().toList(), + ","); + }else{ + deptIds = StringUtils.join(dto.getDeptIds(),","); + } + } + String tags = thingEntity.getTags(); + if(CollectionUtils.isNotEmpty(dto.getTags())){ + if(StringUtils.isNotBlank(tags)){ + tags = StringUtils.join( + Stream.concat(Arrays.stream(tags.split(",")), + dto.getTags().stream()).distinct().toList(), + ","); + }else{ + tags = StringUtils.join(dto.getTags(),","); + } + } + thingEntity.setCode(code) + .setName(BizUtils.trimAll(dto.getName())) + .setRemark(dto.getRemark()) + .setTemplateMark(templateMark) + .setRealType(realType) + .setImg(img) + .setType(ObjectUtil.isNull(dto.getType()) ? DefaultType.THING.getValue() : BizUtils.trimAll(dto.getType())) + .setDeptIds(deptIds) + .setRealType(realType) + .setTags(tags) + .setLat(dto.getLat()) + .setLon(dto.getLon()); + //模板的修改 + if (ObjectUtil.equals(templateMark, TemplateMark.YES.getValue())) { + mapper.update(thingEntity); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + return; + } + thingEntity.setEnableStatus(dto.getEnableStatus()); + mapper.update(thingEntity); + //更新物实体缓存 + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public void deleteById(Long[] ids) { + mapper.deleteBatchByIds(Arrays.asList(ids)); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public List types() { + QueryWrapper wrapper = new QueryWrapper().select(IOT_THING_ENTITY.TYPE); + List typeList = mapper.selectListByQueryAs(wrapper, String.class); + if (CollectionUtil.isEmpty(typeList)) { + return Lists.newArrayList(DefaultType.THING.getValue()); + } + return typeList.stream().distinct().toList(); + } + + @Override + public List tags() { + return mapper.selectAll().stream().map(IotThingEntity::getTags) + .filter(StringUtils::isNotBlank) + .flatMap(s -> Arrays.stream(StringUtils.split(s,","))) + .distinct() + .toList(); + } + + @Override + public void enableStatus(IotThingStatusDTO dto) { + List ids = dto.getIds(); + List entities = mapper.selectListByIds(ids); + if (CollectionUtil.isEmpty(entities)) { + return; + } + entities.forEach(e -> e.setEnableStatus(dto.getEnableStatus())); + updateBatch(entities); +// entities.forEach(this::updateEntityCache); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public void distributePermission(DistributeEntityDTO dto) { + List ids = dto.getIds(); + List entities = mapper.selectListByIds(ids); + if (CollectionUtil.isEmpty(entities)) { + throw new SysException("物实体不存在"); + } + List deptIds = dto.getDeptIds(); + //将已经分配的物实体排除 + deptIds.removeIf(t -> entities.stream().anyMatch(e -> StringUtils.contains(e.getDeptIds(), t.toString()))); + if (CollectionUtil.isEmpty(deptIds)) { + return; + } + entities.forEach(e -> { + String[] deptArray = StringUtils.split(e.getDeptIds(), ","); + List mergedList; + if(ArrayUtils.isNotEmpty(deptArray)){ + mergedList = Stream.concat( + deptIds.stream(), + Arrays.stream(deptArray).map(Long::valueOf)) + .distinct() + .collect(Collectors.toList()); + }else{ + mergedList = deptIds; + } + e.setDeptIds(StringUtils.join(mergedList,",")); + mapper.update(e); + }); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public void template(HttpServletResponse response) { + IotThingViewExcel excel = new IotThingViewExcel(); + excel.setEntityCode("X_xxxxx_xx"); + excel.setEntityName("物名称"); + excel.setRealType(ThingRealType.REAL.getValue()); + excel.setEnableStatus(ThingEnableStatus.START.getValue()); + excel.setEntityType(DefaultType.THING.getValue()); + excel.setTag("压缩空气,氮气"); + ExcelUtils.exportExcel(Lists.newArrayList(excel), "物实体列表", "物实体", IotThingViewExcel.class, "物实体列表模板.xls", response); + } + + @Override + public void exportExcel(Map params, HttpServletResponse response) { + List list = findAll(params); + List excelList = ConvertUtils.sourceToTarget(list, IotThingViewExcel.class); + ExcelUtils.exportExcel(excelList, "物实体列表", "物实体", IotThingViewExcel.class, "物实体列表.xls", response); + } + + @Override + public void importExcel(MultipartFile file, HttpServletRequest request) { + List sheetData = new ArrayList<>(ExcelUtils.importExcel(file, 1, 1, IotThingViewExcel.class, 0)); + if (CollectionUtil.isEmpty(sheetData)) { + throw new SysException("导入数据是空的"); + } + //判断物实体编码和物实体名称 + if (sheetData.stream().anyMatch(s -> StringUtils.isBlank(BizUtils.trimAll(s.getEntityCode())) || StringUtils.isBlank(BizUtils.trimAll(s.getEntityName())))) { + throw new SysException("物实体编码或者物实体名称是空的"); + } + //校验重复 + Map> groupDataMap = sheetData.stream().collect(Collectors.groupingBy(IotThingViewExcel::getEntityCode)); + for (Map.Entry> entry : groupDataMap.entrySet()) { + List codes = entry.getValue().stream().map(IotThingViewExcel::getEntityCode).collect(Collectors.toList()); + if (CollectionUtil.size(codes) > 1) { + throw new SysException("物实体<" + codes.get(0) + ">编码重复"); + } + } + //去除已经存在当前企业下的物实体 + List thingCodes = sheetData.stream().map(IotThingViewExcel::getEntityCode).map(BizUtils::trimAll).distinct().collect(Collectors.toList()); + List thingEntities = findAllByCodeInAndTenantCode(thingCodes, UserContext.getRealTenantCode()); + if(CollectionUtils.isNotEmpty(thingEntities)){ + thingEntities.forEach(s -> + //过滤sheet中匹配的物,更新物实体表中已经存在的物实体 + sheetData.stream().filter(e -> StringUtils.equals(s.getCode(), e.getEntityCode())).findFirst() + .ifPresent(e -> { + s.setName(BizUtils.trimAll(e.getEntityName())) + .setType(BizUtils.trimAll(e.getEntityType())) + .setRealType(e.getRealType()) + .setEnableStatus(e.getEnableStatus()) + .setTags(e.getTag()) + .setRemark(e.getRemark()); + //物实体的更新 + mapper.update(s); + + } + ) + ); + //排除sheet中匹配的物,更新物实体表中已经存在的物实体 + sheetData.removeIf(s -> thingEntities.stream().anyMatch(e -> StringUtils.equals(s.getEntityCode(), e.getCode()))); + } + //物实体的插入 + if(CollectionUtils.isNotEmpty(sheetData)){ + List list = sheetData.stream().map(s -> new IotThingEntity() + .setCode(BizUtils.trimAll(s.getEntityCode())) + .setName(BizUtils.trimAll(s.getEntityName())) + .setRealType(s.getRealType()) + .setType(StringUtils.isBlank(s.getEntityType()) ? DefaultType.THING.getValue() : s.getEntityType()) + .setTemplateMark(TemplateMark.NO.getValue()) + .setEnableStatus(ThingEnableStatus.START.getValue()) + .setTags(s.getTag()) + .setRemark(s.getRemark()) + .setTemplateMark("0") + .setDeptIds(UserContext.getDeptId().toString()) + .setTenantCode(UserContext.getRealTenantCode()) + .setCompanyId(UserContext.getRealCompanyId()) + ).toList(); + mapper.insertBatch(list); + List modelEntities = sheetData.stream().map(data -> new IotThingModelEntity() + .setCode(data.getEntityCode()) + .setStatus(ThingStatus.NOT_CONNECTED.getCode()) + .setGateway(GateWayStatus.NO_GATE_WAY.getValue()) + .setAuthNum(1L) + .setStatusTs(DateTimeUtils.getCurrentTime()) + .setOrigin(QueueOriginType.MQTT_CLIENT.name()) + .setToken(TokenGenerator.generateValue())).toList(); + if(CollectionUtils.isNotEmpty(modelEntities)){ + modelService.saveOrUpdateBatch(modelEntities); + } + cache.clearTopic(CacheNameEnum.THING_MODEL); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + } + + @Override + public List findEntityByCondition(List ids, List types, List tags, List name, List code, + List group, List relation, Long tenantCode, boolean isEntity) { + boolean flag = false; + QueryChain queryChain = queryChain(); + //拼接ids过滤 + if(CollectionUtils.isNotEmpty(ids)){ + flag = true; + ids.forEach(id -> queryChain.or( + queryWrapper -> { + queryWrapper.eq(IotThingEntity::getId, id) + .and(IOT_THING_ENTITY.TENANT_CODE.eq(tenantCode)) + .and(IOT_THING_ENTITY.TEMPLATE_MARK.eq(isEntity ? "0": "1")) + ; + } + )); + } + //拼接types过滤 + if(CollectionUtils.isNotEmpty(types)){ + flag = true; + types.forEach(type -> queryChain.or( + queryWrapper -> { + queryWrapper.eq(IotThingEntity::getType, type) + .and(IOT_THING_ENTITY.TENANT_CODE.eq(tenantCode)) + .and(IOT_THING_ENTITY.TEMPLATE_MARK.eq(isEntity ? "0": "1")) + ; + } + )); + } + + //拼接tags过滤 + if(CollectionUtils.isNotEmpty(tags)){ + flag = true; + tags.forEach(tag -> queryChain.or( + queryWrapper -> { + queryWrapper.where("'"+tag +"'" +" = ANY (ARRAY[tags])") + .and(IOT_THING_ENTITY.TENANT_CODE.eq(tenantCode)) + .and(IOT_THING_ENTITY.TEMPLATE_MARK.eq(isEntity ? "0": "1")) + ; + } + )); + } + //拼接name过滤 + if(CollectionUtils.isNotEmpty(name)){ + flag = true; + name.forEach(name1 -> queryChain.or( + queryWrapper -> { + queryWrapper.like(IotThingEntity::getName, name1) + .and(IOT_THING_ENTITY.TENANT_CODE.eq(tenantCode)) + .and(IOT_THING_ENTITY.TEMPLATE_MARK.eq(isEntity ? "0": "1")) + ; + } + )); + } + //拼接code过滤 + if(CollectionUtils.isNotEmpty(code)){ + flag = true; + code.forEach(code1 -> queryChain.or( + queryWrapper -> { + queryWrapper.like(IotThingEntity::getCode, code1) + .and(IOT_THING_ENTITY.TENANT_CODE.eq(tenantCode)) + .and(IOT_THING_ENTITY.TEMPLATE_MARK.eq(isEntity ? "0": "1")) + ; + } + )); + } + //组的过滤 + if(CollectionUtils.isNotEmpty(group)){ + List groupRelationEntities = groupRelationService.findAllByGroupIdIn(group); + if(CollectionUtils.isNotEmpty(groupRelationEntities)){ + flag = true; + queryChain.or(IOT_THING_ENTITY.ID.in(groupRelationEntities.stream().map(IotGroupRelationEntity::getEntityId).toList())); + } + } + //物关系查询特殊检索 + if(CollectionUtils.isNotEmpty(relation)){ + List entityByCondition = relationDetailService.findEntityByCondition(relation); + if(CollectionUtils.isNotEmpty(entityByCondition)){ + flag = true; + List resultList = entityByCondition.stream() + .map(IotThingRelationDetailDTO::getToId).distinct() + .toList(); + queryChain.or(IOT_THING_ENTITY.ID.in(resultList)); + } + } + if(!flag){ + return new ArrayList<>(); + } + + List entityDTOList = mapper.selectListByQueryAs(queryChain.toQueryWrapper(), IotThingEntityDictDTO.class); + entityDTOList.forEach(item -> { + List thingDictRelationDTOS = dictRelationService.findAllByEntityIdAndCodes(item.getId(), null); + item.setDictList(thingDictRelationDTOS); + }); + return entityDTOList; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchSaveOrUpdate(List collect) { + collect.forEach(item -> mapper.insertOrUpdateSelective(item)); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void saveBatchEntity(List entities) { + mapper.insertBatch(entities); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public Map getThingLineStatus(Map params) { + Map resultMap = new HashMap<>(); + List thingList = findAll(params); + resultMap.put("online", thingList.stream().filter(item -> ThingStatus.ONLINE.getCode().equals(item.getStatus())).count()); + resultMap.put("offline", thingList.stream().filter(item -> ThingStatus.OFFLINE.getCode().equals(item.getStatus())).count()); + resultMap.put("error", thingList.stream().filter(item -> ThingStatus.ERROR.getCode().equals(item.getStatus())).count()); + resultMap.put("connect", thingList.stream().filter(item -> ThingStatus.NOT_CONNECTED.getCode().equals(item.getStatus())).count()); + return resultMap; + } + + @Override + public List> groupType(String type) { + List> result = Lists.newArrayList(); + Map params = new HashMap<>(); + if(StringUtils.isNotBlank(type)){ + params.put("type",type); + } + params.put("templateMark","0"); + List entityDTOList = findAll(params); + List targetTagList; + if (StringUtils.equals(type,"tag")) { + targetTagList = tags(); + }else + { + targetTagList = types(); + } + if (CollectionUtils.isEmpty(targetTagList)) { + return result; + } + for (String tag : targetTagList) { + Map map = Maps.newHashMapWithExpectedSize(2); + map.put("type",tag); + if (StringUtils.equals(type,"tag")) { + map.put("entityList",entityDTOList.stream() + .filter(item1 -> StringUtils.contains(item1.getTags(), tag)) + .collect(Collectors.toList())); + }else + { + map.put("entityList",entityDTOList.stream() + .filter(item1 -> StringUtils.equals(item1.getEntityType(), tag)) + .collect(Collectors.toList())); + } + result.add(map); + } + return result; + } + + @Override + public List apiEntityList(ApiEntityReqDTO apiEntityReqDTO) { + Map> searchMap = apiEntityReqDTO.getSearch(); + List names = Lists.newArrayList(); + List codes = Lists.newArrayList(); + if(MapUtils.isNotEmpty(searchMap)){ + names = searchMap.get("name"); + codes = searchMap.get("code"); + } + return findEntityByCondition(apiEntityReqDTO.getList(), apiEntityReqDTO.getType(), + apiEntityReqDTO.getTag(), names, codes,apiEntityReqDTO.getGroup(), + apiEntityReqDTO.getRelation(), UserContext.getRealTenantCode(), true); + } + + @Override + public PageData telemetryPage(Map params) { + Long id = MapUtil.getLong(params, "id"); + String dictCode = MapUtil.getStr(params, "dictCode"); + String beginTime = MapUtil.getStr(params, "beginTime"); + String endTime = MapUtil.getStr(params, "endTime"); + Integer page = MapUtil.getInt(params, "page",1); + Integer limit = MapUtil.getInt(params, "limit",10); + + IotThingEntityInfoDTO entityDTO = findEntityById(id); + if(Objects.isNull(entityDTO)){ + throw new SysException("当前物实体不存在"); + } + //查询已经配置的物指标 + List dictRelationDTOList = dictRelationService.findAllByEntityIdAndCodes(id, StringUtils.isBlank(dictCode) + ? Collections.emptyList() : Collections.singleton(dictCode)); + if(CollectionUtils.isEmpty(dictRelationDTOList)){ + return new PageData<>(Lists.newArrayList(),0); + } + //属性集合 + Collection dictCodes = dictRelationDTOList.stream().map(IotThingDictRelationDTO::getCode).toList(); + //没时间筛选:查询最新数据 + if(StringUtils.isBlank(beginTime) || StringUtils.isBlank(endTime)){ + return tsKvService.findPageLatestByMultiMap(Map.of(entityDTO.getCode(),dictCodes),false,page,limit); + }else{ + Long startTime = DateTimeUtils.dateToTimestamp(beginTime, DateTimeUtils.DATE_TIME_PATTERN_STR); + Long overTime = DateTimeUtils.dateToTimestamp(endTime, DateTimeUtils.DATE_TIME_PATTERN_STR); + return tsKvService.findPageTsKvByMultiMap(Map.of(entityDTO.getCode(),dictCodes),startTime,overTime,false,page,limit); + } + } + + @Override + public List telemetryList(Map params) { + String entityCode = MapUtil.getStr(params, "entityCode"); + return tsKvService.findLatestByCodeAndAttrs(entityCode,null,false); + } + + @Override + public List findThingsAttrsByCondition(ApiEntityParamDTO entityAllDTO) { + ApiEntityReqDTO apiEntityReqDTO = new ApiEntityReqDTO(); + ApiEntityDTO thingDTO = entityAllDTO.getEntitys(); + if (ObjectUtil.isNull(thingDTO)) { + return Lists.newArrayList(); + } + //物条件搜索处理 + ApiEntitySearchDTO search = thingDTO.getSearch(); + if (ObjectUtil.isNotNull(search)) { + Map> searchMap = new HashMap<>(); + //名称搜索模糊条件拼装 + List name = search.getName(); + searchMap.put("name",name); + //编码搜索模糊条件拼装 + List code = search.getCode(); + searchMap.put("code",code); + apiEntityReqDTO.setSearch(searchMap); + } + ApiEntityDTO entitys = entityAllDTO.getEntitys(); + apiEntityReqDTO.setType(entitys.getType()); + apiEntityReqDTO.setTag(entitys.getTag()); + apiEntityReqDTO.setGroup(entitys.getGroup()); + + List entityIds = CollectionUtil.isEmpty(thingDTO.getList()) ? Lists.newArrayList() : thingDTO.getList(); + apiEntityReqDTO.setList(entityIds); + + //物关系查询特殊检索 + List relation = entitys.getRelation(); + apiEntityReqDTO.setRelation(relation); + return apiEntityList(apiEntityReqDTO); + } + + private List> buildParam(String code, + String name, + String type, + Long tenantCode, + Long deptId, + String realType, + String tags, + String enableStatus, + String templateMark) + { + List> filterList = new ArrayList<>(); + if (StringUtils.isNotBlank(code)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_CODE.getField(), code)); + } + if (StringUtils.isNotBlank(name)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_NAME.getField(), name)); + } + if (StringUtils.isNotBlank(type)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_TYPE.getField(), type)); + } + if (Objects.nonNull(tenantCode)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_TENANT_CODE.getField(), tenantCode.toString())); + } + if (Objects.nonNull(deptId)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_DEPT_IDS.getField(), deptId.toString())); + } + if (StringUtils.isNotBlank(realType)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_REAL_TYPE.getField(), realType)); + } + if (StringUtils.isNotBlank(tags)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_TAGS.getField(), tags)); + } + if (StringUtils.isNotBlank(enableStatus)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_ENABLE_STATUS.getField(), enableStatus)); + } + if (StringUtils.isNotBlank(templateMark)) { + filterList.add(Pair.of(CacheNameEnum.EntityField.THING_ENTITY_TEMPLATE_MARK.getField(), templateMark)); + } + return filterList; + } + + + private void convertInfo(List list) { + list.forEach(res -> { + List keyMap = cache.findKeyMap(CacheNameEnum.THING_MODEL, res.get(CacheNameEnum.EntityField.THING_ENTITY_CODE.getField()).asText()); + if(CollectionUtils.isNotEmpty(keyMap)){ + res.put("status",keyMap.get(0).get(CacheNameEnum.ModelField.THING_MODEL_STATUS.getField()).asText()); + res.put("statusTs",keyMap.get(0).get(CacheNameEnum.ModelField.THING_MODEL_STATUS_TS.getField()).asText()); + JsonNode jsonNode = res.get(CacheNameEnum.EntityField.THING_ENTITY_DEPT_IDS.getField()); + if(Objects.nonNull(jsonNode) && !(jsonNode instanceof NullNode)){ + List deptIds = Stream.of(StringUtils.split(jsonNode.asText(), ",")).map(Long::parseLong).toList(); + if(CollectionUtils.isNotEmpty(deptIds)){ + List sysDeptEntities = sysDeptMapper.selectListByIds(deptIds); + if(CollectionUtils.isNotEmpty(sysDeptEntities)){ + res.put("deptName",sysDeptEntities.stream().map(SysDeptEntity::getName).collect(Collectors.joining(","))); + } + } + } + } + }); + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/ext/controller/CommonConfigController.java b/modules/thing/src/main/java/com/thing/thing/ext/controller/CommonConfigController.java new file mode 100644 index 0000000..59ded63 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/controller/CommonConfigController.java @@ -0,0 +1,115 @@ +package com.thing.thing.ext.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.ext.dto.CommonConfigDTO; +import com.thing.thing.ext.service.CommonConfigService; + +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.Parameter; + +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.web.bind.annotation.*; + + + +import java.util.List; +import java.util.Map; + +import javax.validation.constraints.NotNull; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +@RestController +@RequestMapping("config/commonconfig") +@Tag(name="能管通用配置表") +@RequiredArgsConstructor +public class CommonConfigController { + private final CommonConfigService commonConfigService; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始"), + @Parameter(name = Constant.LIMIT,description ="每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段"), + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name = "configKey",description ="配置键"), + @Parameter(name = "configValue",description ="配置值"), + @Parameter(name = "configType",description ="配置类型(0-租户配置,1-企业字典)"), + @Parameter(name = "configName",description ="配置名称") + }) + public Result> page( @RequestParam Map params) { + return new Result>().ok(commonConfigService.getPageData(params, CommonConfigDTO.class)); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + CommonConfigDTO data = commonConfigService.getByIdAs(id, CommonConfigDTO.class); + return new Result().ok(data); + } + + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + @SneakyThrows + public Result save(@RequestBody CommonConfigDTO commonConfigDTO) { + //效验数据 + ValidatorUtils.validateEntity(commonConfigDTO, AddGroup.class, DefaultGroup.class); + String configValue = commonConfigService.getConfigValue(commonConfigDTO.getConfigKey()); + if (StringUtils.isNotBlank(configValue)) throw new SysException("该配置键已存在,请修改"); + commonConfigService.saveDto(commonConfigDTO); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + @SneakyThrows + public Result update(@RequestBody CommonConfigDTO commonConfigDTO) { + //效验数据 + ValidatorUtils.validateEntity(commonConfigDTO, UpdateGroup.class, DefaultGroup.class); + commonConfigService.updateDto(commonConfigDTO); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + commonConfigService.batchDelete(ids); + return new Result<>(); + } + + @GetMapping("/getConfigValue") + @Operation(summary="根据key值获取value") + public Result get(@RequestParam @NotNull String configKey){ + String configValue = commonConfigService.getConfigValue(configKey); + return new Result().ok(configValue); + } + + @GetMapping("/getConfigName") + @Operation(summary="获取配置类型") + public Result> getConfigName(){ + return new Result>().ok(Lists.newArrayList(Constant.ATTR_GROUP)); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/ext/dto/CommonConfigDTO.java b/modules/thing/src/main/java/com/thing/thing/ext/dto/CommonConfigDTO.java new file mode 100644 index 0000000..5d7c852 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/dto/CommonConfigDTO.java @@ -0,0 +1,72 @@ +package com.thing.thing.ext.dto; + +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.orm.dto.BaseDTO; + +import io.swagger.v3.oas.annotations.media.Schema; + + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.util.Date; + +import javax.validation.constraints.NotNull; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +@Data +@EqualsAndHashCode(callSuper = false) +@Schema( name= "企业租户配置表") +public class CommonConfigDTO extends BaseDTO { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "配置ID") + private Long id; + + @Schema(description = "配置键") + @NotNull(message = "配置键不能为空!", groups = DefaultGroup.class) + private String configKey; + + @Schema(description = "配置名称") + @NotNull(message = "配置名称不能为空!", groups = DefaultGroup.class) + private String configName; + + @Schema(description = "配置值,请使用json") + @NotNull(message = "配置值不能为空!", groups = DefaultGroup.class) + private String configValue; + + @NotNull(message = "配置类型不能为空!", groups = DefaultGroup.class) + @Schema(description = "配置类型(0-租户配置,1-企业字典)") + private Integer configType; + + @Schema(description = "描述") + private String remark; + + @Schema(description = "租户Code") + private Long tenantCode; + + @Schema(description = "公司ID") + private Long companyId; + + @Schema(description = "部门ID") + private Long deptId; + + @Schema(description = "创建者") + private Long creator; + + @Schema(description = "创建时间") + private Date createDate; + + @Schema(description = "更新者") + private Long updater; + + @Schema(description = "更新时间") + private Date updateDate; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/ext/entity/CommonConfigEntity.java b/modules/thing/src/main/java/com/thing/thing/ext/entity/CommonConfigEntity.java new file mode 100644 index 0000000..5f368ea --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/entity/CommonConfigEntity.java @@ -0,0 +1,49 @@ +package com.thing.thing.ext.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; + +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("tenant_common_config") +public class CommonConfigEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 配置键 + */ + private String configKey; + + /** + * 配置名称 + */ + private String configName; + /** + * 配置值,推荐使用json + */ + private String configValue; + /** + * 配置类型(0-租户配置,1-企业字典) + */ + private Integer configType; + /** + * 描述 + */ + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/ext/mapper/CommonConfigMapper.java b/modules/thing/src/main/java/com/thing/thing/ext/mapper/CommonConfigMapper.java new file mode 100644 index 0000000..d51e66b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/mapper/CommonConfigMapper.java @@ -0,0 +1,18 @@ +package com.thing.thing.ext.mapper; + + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.ext.entity.CommonConfigEntity; + +import org.apache.ibatis.annotations.Mapper; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +@Mapper +public interface CommonConfigMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/ext/service/CommonConfigService.java b/modules/thing/src/main/java/com/thing/thing/ext/service/CommonConfigService.java new file mode 100644 index 0000000..ffbd599 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/service/CommonConfigService.java @@ -0,0 +1,20 @@ +package com.thing.thing.ext.service; + + +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.ext.entity.CommonConfigEntity; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +public interface CommonConfigService extends IBaseService { + /** + * 获取其中一个配置记录的配置值 + * @param configKey + * @return + */ + String getConfigValue(String configKey); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/ext/service/impl/CommonConfigServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/ext/service/impl/CommonConfigServiceImpl.java new file mode 100644 index 0000000..245d36c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/ext/service/impl/CommonConfigServiceImpl.java @@ -0,0 +1,45 @@ +package com.thing.thing.ext.service.impl; + +import cn.hutool.core.util.ObjectUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.thing.ext.mapper.CommonConfigMapper; +import com.thing.thing.ext.entity.CommonConfigEntity; +import com.thing.thing.ext.service.CommonConfigService; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Map; + +/** + * 企业租户配置表 + * + * @author chenyang 56583086@qq.com + * @since 1.0.0 2020-11-05 + */ +@Service +public class CommonConfigServiceImpl extends BaseServiceImpl implements CommonConfigService { + + @Override + public QueryWrapper getWrapper(Map params){ + String configKey = (String) params.get("configKey"); + String configValue = (String) params.get("configValue"); + String configName = (String) params.get("configName"); + Long configType = StringUtils.isNotBlank((String)params.get("configType")) ? Long.parseLong((String)params.get("configType")) : null; + QueryWrapper wrapper = new QueryWrapper(); + wrapper.like(CommonConfigEntity::getConfigKey, configKey, StringUtils.isNotBlank(configKey)) + .like(CommonConfigEntity::getConfigValue, configValue, StringUtils.isNotBlank(configValue)) + .like(CommonConfigEntity::getConfigName, configName, StringUtils.isNotBlank(configName)) + .eq(CommonConfigEntity::getConfigType, configType, ObjectUtil.isNotNull(configType)); + return wrapper; + } + + @Override + public String getConfigValue(String configKey) { + CommonConfigEntity commonConfigEntity = mapper.selectOneExt(QueryWrapper.create().eq(CommonConfigEntity::getConfigKey, configKey)); + return ObjectUtil.isNotNull(commonConfigEntity)? commonConfigEntity.getConfigValue() : ""; + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupInfoController.java b/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupInfoController.java new file mode 100644 index 0000000..4293c45 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupInfoController.java @@ -0,0 +1,265 @@ +package com.thing.thing.group.controller; + +import cn.hutool.core.util.ObjectUtil; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.IsDefaultEnum; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.group.dto.IotGroupInfoDTO; +import com.thing.thing.group.dto.IotGroupInfoFormatDTO; +import com.thing.thing.group.dto.IotSortDTO; +import com.thing.thing.group.entity.IotGroupInfoEntity; +import com.thing.thing.group.service.IotGroupInfoService; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import lombok.RequiredArgsConstructor; + +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +@RestController +@RequestMapping("v2/group") +@Tag(name="新组管理") +@RequiredArgsConstructor +public class IotGroupInfoController { + private final IotGroupInfoService service; + + @GetMapping("page") + @Operation(summary="分页") + @Parameters({ + @Parameter(name = Constant.PAGE,description ="当前页码,从1开始") , + @Parameter(name = Constant.LIMIT,description ="每页显示记录数") , + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name ="name",description ="组名称"), + @Parameter(name ="type",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材"), + @Parameter(name ="businessType",description ="组业务类型"), + @Parameter(name = "deptId",description ="部门id"), + @Parameter(name = "beginTime",description ="开始时间【时间戳】"), + @Parameter(name = "endTime",description ="结束时间【时间戳】"), + }) + public Result> page( @RequestParam Map params){ + PageData page = service.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary="组列表") + @Parameters({ + @Parameter(name = Constant.ORDER_FIELD,description ="排序字段") , + @Parameter(name = Constant.ORDER,description ="排序方式,可选值(asc、desc)"), + @Parameter(name ="name",description ="组名称"), + @Parameter(name ="type",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材"), + @Parameter(name ="businessType",description ="组业务类型"), + @Parameter(name = "deptId",description ="部门id"), + @Parameter(name = "beginTime",description ="开始时间【时间戳】"), + @Parameter(name = "endTime",description ="结束时间【时间戳】"), + }) + public Result> list( @RequestParam Map params){ + List list = service.findAllList(params); + return new Result>().ok(list.parallelStream().sorted(Comparator.comparingLong(IotGroupInfoDTO::getSort)).collect(Collectors.toList())); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotGroupInfoDTO data = service.getByIdAs(id, IotGroupInfoDTO.class); + isOperate(data); + return new Result().ok(data); + } + + /** + * 判断是否可操作 + */ + public static void isOperate(IotGroupInfoDTO data) { + if(ObjectUtil.equals(data.getIsDefault(), IsDefaultEnum.Y.getValue())) { + if(!"1001".equals(String.valueOf(UserContext.getTenantCode()))){ + data.setIsOperate("1"); + } + } + if (!String.valueOf(data.getTenantCode()).equals(String.valueOf(UserContext.getTenantCode()))){ + if(!UserContext.isAdmin()){ + data.setIsOperate("1"); + } + } + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotGroupInfoDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.saveIotGroupInfoDTO(dto); + return new Result().ok("添加成功!"); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotGroupInfoDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.updateIotGroupInfoDTO(dto); + return new Result<>(); + } + + + + @PostMapping("updateSort") + @Operation(summary="修改组排序") + public Result update(@RequestBody List dtos){ + for (IotSortDTO dto : dtos) { + IotGroupInfoEntity entity = new IotGroupInfoEntity(); + entity.setId(dto.getId()); + entity.setSort(dto.getSort()); + service.updateById(entity); + } + return new Result<>(); + } + + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDeleteByIds(ids); + return new Result<>(); + } + + + @PostMapping("template") + @Operation(summary="模板下载") + @LogOperation("模板下载") + public void exportTemplate(HttpServletResponse response) { + service.template(response); + } + + @PostMapping("exportExcel") + @Operation(summary="组模型Excel导出") + @Parameters({ + @Parameter(name = "groupName",description ="组名称"), + @Parameter(name = "deptId",description ="部门id"), + @Parameter(name = "groupScene",description ="场景"), + @Parameter(name = "groupType",description ="类型"), + @Parameter(name = "beginTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间") + }) + public void exportGroupExcel(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + service.exportExcel(Lists.newArrayList(ids), params, response); + } + + @PostMapping("importExcel") + @Operation(summary="组模型Excel导入") + public Result importGroupExcel(MultipartFile file, HttpServletRequest request) { + service.importExcel(file, request); + return new Result(); + } + + @GetMapping("type/{type}") + @Operation(summary="根据类型查询") + public Result> findAllByType(@PathVariable String type) { + List list = service.findAllByType(type); + return new Result>().ok(list); + } + + @GetMapping("type") + @Operation(summary="根据类型查询") + public Result> findAllByType(@RequestParam Map params) { + List list = service.findSysDictList(params); + return new Result>().ok(list); + } + + @GetMapping("queryDictInfo") + @Operation(summary="根据传参类型,获取所有可见的组,或类型名称") + @Parameters({ + @Parameter(name = "queryType",description ="类型 1.所有可见的组名称,2.所有可见的组类型名称"), + @Parameter(name ="type",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材") + }) + public Result> queryDictInfo(String queryType,String type){ + Set strs = service.queryDictInfo(queryType,type); + return new Result>().ok(strs); + } + + @GetMapping("groupTypeListByGroupName") + @Operation(summary="获取所有可见组列表,可按组名查询,新增部件/素材时使用") + @Parameters({ + @Parameter(name ="name",description ="组名称"), + @Parameter(name ="type",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材") + }) + public Result> groupTypeListByGroupName(String name,String type){ + return service.groupTypeListByGroupName(name,type); + } + + + @GetMapping("groupList") + @Operation(summary="返回所有可见组的树结构,部件素材侧边栏使用(组级别是虚拟的,没有id生成的虚拟id)") + @Parameters({ + @Parameter(name ="type",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材") + }) + public Result> groupList(String type, String name){ + return service.listTree(type,name); + } + + + @PostMapping("export") + @Operation(summary="导出") + @Parameters({ + @Parameter(name ="name",description ="组名称"), + @Parameter(name ="groupType",description ="组类型"), + @Parameter(name ="businessType",description ="组类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材")}) + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) throws Exception { + params.put("ids", ids); + List list = service.findAllList(params); + JsonProcessingUtils.exportJson(response, list, "group.json"); + } + + + @PostMapping("importJson") + @Operation(summary="json导入,返回失败的数据信息") + public Result> importJson(MultipartFile file, HttpServletRequest request) { + return new Result>().ok(service.importJson(file, request)); + } + + + @GetMapping("getMaxSort") + @Operation(summary="获取最大类型的排序信息") + public Result getMaxSort(String type) { + return new Result().ok(service.getMaxSort(type)); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupRelationController.java b/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupRelationController.java new file mode 100644 index 0000000..e7fca2b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/controller/IotGroupRelationController.java @@ -0,0 +1,147 @@ +package com.thing.thing.group.controller; + +import com.google.common.collect.Lists; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.group.dto.IotGroupEntityDTO; +import com.thing.thing.group.dto.IotGroupEntityListDTO; +import com.thing.thing.group.dto.IotGroupRelationDTO; +import com.thing.thing.group.excel.IotGroupRelationExcel; +import com.thing.thing.group.service.IotGroupRelationService; + + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + + +import java.util.List; +import java.util.Map; + +import javax.annotation.Resource; + +/** + * 实体&组,实体&物关系 的关系表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@RestController +@RequestMapping("v2/groupRelation") +@Tag(name = "实体&组,实体&物关系 的关系表") +public class IotGroupRelationController { + + @Resource + private IotGroupRelationService service; + + @GetMapping("page") + @Operation(summary = "分页") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> page(@RequestParam Map params) { + PageData page = service.pageList(params); + return new Result>().ok(page); + } + + @GetMapping("list") + @Operation(summary = "列表") + @Parameters({ + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始"), + @Parameter(name = Constant.LIMIT, description = "每页显示记录数"), + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段"), + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") + }) + public Result> list(@RequestParam Map params) { + List list = service.findAllList(params); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + IotGroupRelationDTO data = service.getByIdAs(id, IotGroupRelationDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存") + @LogOperation("保存") + public Result save(@RequestBody IotGroupRelationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.saveDto(dto); + return new Result<>(); + } + + @PostMapping("add") + @Operation(summary = "保存") + @LogOperation("保存") + public Result saveEntity(@Validated @RequestBody IotGroupEntityDTO dto) { + //效验数据 + service.saveEntity(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改") + @LogOperation("修改") + public Result update(@RequestBody IotGroupRelationDTO dto) { + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.updateDto(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids/*IotGroupRelationDelDTO dto*/) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("template") + @Operation(summary = "模板下载") + @LogOperation("模板下载") + public void template(HttpServletResponse response) { + ExcelUtils.exportExcel(Lists.newArrayList(), "物分组", "实体&组", IotGroupRelationExcel.class, "物分组.xls", response); + } + + @PostMapping("export") + @Operation(summary = "导出") + @LogOperation("导出") + public void export(@RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + List list = service.export(ids, params, response); + ExcelUtils.exportExcel(list, "物分组", "实体&组", IotGroupRelationExcel.class, "物分组.xls", response); + } + + @PostMapping("import") + @Operation(summary = "导入") + @LogOperation("导入") + public Result importExcel(MultipartFile file, HttpServletRequest request) { + // service.importExcel(file,request); + return new Result<>(); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityDTO.java new file mode 100644 index 0000000..b73d3c4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityDTO.java @@ -0,0 +1,30 @@ +package com.thing.thing.group.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +@Data +@Schema( name= "实体&组,实体&物关系 的关系表") +public class IotGroupEntityDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "组id") + @NotEmpty(message="组的id不能为空") + private List groupIds; + + @Schema(description = "实体id 比如thing_entity_id,thing_relation_root") + private List entityIds; + + @Schema(description = "实体类型:物实体- thing 物关系-relation") + @NotBlank(message="类型不能为空") + private String entityType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityListDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityListDTO.java new file mode 100644 index 0000000..f470b84 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupEntityListDTO.java @@ -0,0 +1,52 @@ +package com.thing.thing.group.dto; + +import com.thing.common.orm.dto.BaseThingDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +@Data +@EqualsAndHashCode(callSuper = true) +@Schema( name= "实体&组,实体&物关系 的关系表") +public class IotGroupEntityListDTO extends BaseThingDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "组的id") + private Long groupId; + + @Schema(description = "组的名称") + private String groupName; + + @Schema(description = "组的编码") + private String groupCode; + + @Schema(description = "组的类型") + private String groupType; + + @Schema(description = "实体id") + private Long entityId; + + @Schema(description = "实体类型:物实体- thing 物关系-relation") + private String entityType; + + @Schema(description = "实体名称") + private String entityName; + + @Schema(description = "实体编码") + private String entityCode; + + @Schema(description = "虚实类型") + private Integer realType; + + @Schema(description = "部门编码") + private Long deptId; + + @Schema(description = "实体备注") + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoDTO.java new file mode 100644 index 0000000..91cde7f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoDTO.java @@ -0,0 +1,67 @@ +package com.thing.thing.group.dto; + +import com.thing.common.core.validator.group.UpdateGroup; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +@Data +@Schema( name= "组管理") +public class IotGroupInfoDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @NotNull(message = "主键不能为空",groups = UpdateGroup.class) + private Long id; + @Schema(description = "组名称") + @NotBlank(message = "组名称不能为空") + private String name; + @Schema(description = "组类型, thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材") + @NotBlank(message = "组类型不能为空") + private String type; + @Schema(description = "组编码") + @NotBlank(message = "组编码不能为空") + private String code; + @Schema(description = "组业务类型") + private String businessType; + @Schema(description = "0 默认 1.自定义") + private Integer isDefault; + @Schema(description = "缩略图url") + private String thumbnailUrl; + @Schema(description = "备注") + private String remark; + @Schema(description = "所属企业(租户code)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + + @Schema(description = "是否可操作/修改/删除 0可操作/1不可操作") + private String isOperate="0"; + + @Schema(description = "排序") + private Long sort; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoFormatDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoFormatDTO.java new file mode 100644 index 0000000..4be17fd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupInfoFormatDTO.java @@ -0,0 +1,30 @@ +package com.thing.thing.group.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +@Data +public class IotGroupInfoFormatDTO { + + @Schema(description = "id") + private Long id; + + @Schema(description = "组名称") + private String name; + + @Schema(description = "组类型名称") + private String businessType; + + @Schema(description = "缩略图url") + private String thumbnailUrl; + + private Long order; + + @Schema(description = "组类型列表") + private List typeList; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDTO.java new file mode 100644 index 0000000..fa419c2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDTO.java @@ -0,0 +1,50 @@ +package com.thing.thing.group.dto; + +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.orm.dto.BaseThingDTO; +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import javax.validation.constraints.NotNull; +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 实体&组,实体&物关系 的关系表 +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Data +@Schema( name= "实体&组,实体&物关系 的关系表") +public class IotGroupRelationDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + + @Schema(description = "组id") + @NotNull(message="组的id不能为空",groups = AddGroup.class) + private Long groupId; + + @Schema(description = "实体id 比如thing_entity_id,thing_relation_root") + private Long entityId; + + @Schema(description = "实体类型:物实体- thing 物关系-relation") + @NotBlank(message="实体类型不能为空",groups ={AddGroup.class} ) + private String entityType; + + private String entityCode; + + private String unit; + + @NotEmpty(message="需要添加的属性或者实体列表不能为空",groups = AddGroup.class) + @Schema(description = "需要保存的物字典变量") + private List baseThingDTOList; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDelDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDelDTO.java new file mode 100644 index 0000000..f5c0b0f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotGroupRelationDelDTO.java @@ -0,0 +1,31 @@ +package com.thing.thing.group.dto; + +import io.swagger.v3.oas.annotations.media.Schema; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 实体&组,实体&物关系 的关系表 +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Data +@Schema( name= "实体&组,实体&物关系 的关系表") +public class IotGroupRelationDelDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private List ids; + + @Schema(description = "实体类型:物实体- thing 物关系-relation") + @NotBlank(message="实体类型不能为空") + private String entityType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/dto/IotSortDTO.java b/modules/thing/src/main/java/com/thing/thing/group/dto/IotSortDTO.java new file mode 100644 index 0000000..0aa8457 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/dto/IotSortDTO.java @@ -0,0 +1,17 @@ +package com.thing.thing.group.dto; + + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Data +public class IotSortDTO { + + @Schema(description = "id") + private Long id; + + @Schema(description = "排序") + private Long sort; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupInfoEntity.java b/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupInfoEntity.java new file mode 100644 index 0000000..4aa7cb1 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupInfoEntity.java @@ -0,0 +1,65 @@ +package com.thing.thing.group.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.util.Objects; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_group_info") +public class IotGroupInfoEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + private static final String ATTR_GROUP_TYPE = "attr"; + + /** + * 组名称 + */ + private String name; + /** + * 组类型,业务字典表维护 thing/attr/tag/relation + */ + private String type; + + /** + * 组编码 + */ + private String code; + /** + * 组业务类型 + */ + private String businessType; + /** + * 0 默认 1.自定义 + */ + private Integer isDefault; + /** + * 缩略图url + */ + private String thumbnailUrl; + /** + * 备注 + */ + private String remark; + + /** + * 排序 + */ + private Long sort; + + public boolean isAttrGroup(){ + return Objects.equals(ATTR_GROUP_TYPE, type); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupRelationEntity.java b/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupRelationEntity.java new file mode 100644 index 0000000..33bd2c5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/entity/IotGroupRelationEntity.java @@ -0,0 +1,42 @@ +package com.thing.thing.group.entity; + +import com.mybatisflex.annotation.Id; +import com.mybatisflex.annotation.Table; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 实体&组,实体&物关系 的关系表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_group_relation") +public class IotGroupRelationEntity implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + /** + * id + */ + @Id + private Long id; + /** + * 组id + */ + private Long groupId; + /** + * 实体id 比如thing_entity_id,thing_relation_root + */ + private Long entityId; + + private String entityType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupInfoExcel.java b/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupInfoExcel.java new file mode 100644 index 0000000..3ef0913 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupInfoExcel.java @@ -0,0 +1,33 @@ +package com.thing.thing.group.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +@Data +public class IotGroupInfoExcel { + + @Excel(name = "序号") + private Long sort; + + @Excel(name = "组名称",orderNum = "1") + private String name; + + @Excel(name = "组编码",orderNum = "2") + private String code; + + @Excel(name = "系统类型:thing",orderNum = "3") + private String type; + + @Excel(name = "组描述",orderNum = "4") + private String remark; + + @Excel(name = "是否系统默认: 0 默认 1.自定义",orderNum = "5") + private Integer isDefault; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupRelationExcel.java b/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupRelationExcel.java new file mode 100644 index 0000000..186166a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/excel/IotGroupRelationExcel.java @@ -0,0 +1,37 @@ +package com.thing.thing.group.excel; + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 实体&组,实体&物关系 的关系表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +public class IotGroupRelationExcel { + + @Excel(name = "组名称" ) + private String groupName; + + @Excel(name = "组编码",orderNum = "1") + private String groupCode; + + @Excel(name = "物名称",orderNum = "2") + private String entityName; + + @Excel(name = "物类型",orderNum = "3") + private String entityType; + + @Excel(name = "物编码",orderNum = "4") + private String entityCode; + + @Excel(name = "物存在类型,0虚拟 1真实",orderNum = "5") + private Integer realType; + + @Excel(name = "备注说明",orderNum = "6") + private String remark; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupInfoMapper.java b/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupInfoMapper.java new file mode 100644 index 0000000..708cf5c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupInfoMapper.java @@ -0,0 +1,22 @@ +package com.thing.thing.group.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.group.entity.IotGroupInfoEntity; + +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** +* 组管理 +* +* @author xc +* @since 3.0 2023-06-02 +*/ +@Mapper +public interface IotGroupInfoMapper extends PowerBaseMapper { + + int findCountByGroupIdInMaterial(List asList); + + int findCountByGroupIdInSection(List asList); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupRelationMapper.java b/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupRelationMapper.java new file mode 100644 index 0000000..f150d7f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/mapper/IotGroupRelationMapper.java @@ -0,0 +1,33 @@ +package com.thing.thing.group.mapper; + +import com.mybatisflex.core.paginate.Page; +import com.thing.common.orm.dto.BaseThingDTO; +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.group.dto.IotGroupEntityListDTO; +import com.thing.thing.group.dto.IotGroupRelationDTO; +import com.thing.thing.group.entity.IotGroupRelationEntity; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; +import java.util.Map; + +/** +* 实体&组,实体&物关系 的关系表 +* +* @author xc/ls +* @since 3.0 2023-06-05 +*/ +@Mapper +public interface IotGroupRelationMapper extends PowerBaseMapper { + + List getAttrExtendDataByTenantCode(Map params); + + // Page thingPageList(Page page, Map params, List groupIds, List ids); + + Page dictPageList(Page page,Map params); + + List thingPageList(Map params, List groupIds,List ids); + + List dictPageList(Map params, List groupIds,List ids); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupInfoService.java b/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupInfoService.java new file mode 100644 index 0000000..ee7d784 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupInfoService.java @@ -0,0 +1,102 @@ +package com.thing.thing.group.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.IBaseService; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.thing.group.dto.IotGroupInfoDTO; +import com.thing.thing.group.dto.IotGroupInfoFormatDTO; +import com.thing.thing.group.entity.IotGroupInfoEntity; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.springframework.web.multipart.MultipartFile; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +public interface IotGroupInfoService extends IBaseService { + + /** + * 组分页 + */ + PageData pageList(Map params); + + /** + * 组列表 + */ + List findAllList(Map params); + + /** + * 保存组信息 + */ + void saveIotGroupInfoDTO(IotGroupInfoDTO dto); + + /** + * 更新组信息 + */ + void updateIotGroupInfoDTO(IotGroupInfoDTO dto); + + void batchDeleteByIds(Long[] ids); + + /** + * 模板下载 + */ + void template(HttpServletResponse response); + + /** + * 根据查询条件查询组信息导出excel + */ + void exportExcel(List ids,Map params, HttpServletResponse response); + + /** + * 导入excel + */ + void importExcel(MultipartFile file, HttpServletRequest request); + + /** + * 根据类型查询组列表 + */ + List findAllByType(String type); + + /** + * 根据类型查询组列表 + */ + List findSysDictList(Map params); + + Set queryDictInfo(String queryType, String type); + + Result> listTree(String type, String name); + + Result> groupTypeListByGroupName(String name,String type); + + List importJson(MultipartFile file, HttpServletRequest request); + + /** + * 根据组编码查询组信息 + */ + IotGroupInfoEntity findByCode(String code); + /** + * 根据组编码查询组信息 + */ + List findByCodeIn(List code); + + /** + * 根据主键查询组信息 + */ + List findByIdIn(Collection id); + + void transferGroupInfo(Long id); + + Long getMaxSort(String type); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupRelationService.java b/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupRelationService.java new file mode 100644 index 0000000..5395e34 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/service/IotGroupRelationService.java @@ -0,0 +1,31 @@ +package com.thing.thing.group.service; + +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.group.dto.IotGroupEntityDTO; +import com.thing.thing.group.dto.IotGroupEntityListDTO; +import com.thing.thing.group.entity.IotGroupRelationEntity; +import com.thing.thing.group.excel.IotGroupRelationExcel; +import jakarta.servlet.http.HttpServletResponse; + +import java.util.List; +import java.util.Map; + +public interface IotGroupRelationService extends IBaseService { + + PageData pageList(Map params); + + List findAllList(Map params); + + List export(Long[] ids, Map params, HttpServletResponse response); + + Map getAttrExtendDataMapByTenantCode(Map params); + + List findAllByGroupId(Long groupId); + + List findAllByGroupIdIn(List groupId); + + void syncGroupRelation(Long id, long newId); + + void saveEntity(IotGroupEntityDTO dto); +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupInfoServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupInfoServiceImpl.java new file mode 100644 index 0000000..41afe52 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupInfoServiceImpl.java @@ -0,0 +1,518 @@ +package com.thing.thing.group.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.google.common.collect.Lists; +import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.GroupTypeEnum; +import com.thing.common.core.enumeration.IsDefaultEnum; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.BizUtils; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.utils.DateTimeUtils; +import com.thing.common.core.utils.JsonProcessingUtils; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.biz.dto.SysDictDataDTO; +import com.thing.sys.biz.service.SysDictDataService; +import com.thing.sys.security.context.UserContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.thing.group.controller.IotGroupInfoController; +import com.thing.thing.group.dto.IotGroupInfoDTO; +import com.thing.thing.group.dto.IotGroupInfoFormatDTO; +import com.thing.thing.group.entity.IotGroupInfoEntity; +import com.thing.thing.group.entity.IotGroupRelationEntity; +import com.thing.thing.group.excel.IotGroupInfoExcel; +import com.thing.thing.group.mapper.IotGroupInfoMapper; +import com.thing.thing.group.service.IotGroupInfoService; +import com.thing.thing.group.service.IotGroupRelationService; +import com.thing.util.TenantSubsetUtil; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.max; +import static com.thing.thing.group.entity.table.IotGroupInfoEntityTableDef.IOT_GROUP_INFO_ENTITY; + +/** + * 组管理 + * + * @author xc + * @since 3.0 2023-06-02 + */ +@Service +@RequiredArgsConstructor +public class IotGroupInfoServiceImpl extends BaseServiceImpl implements IotGroupInfoService { + private final TenantSubsetUtil tenantSubsetUtil; + private final SysDictDataService sysDictDataService; + @Resource @Lazy private IotGroupRelationService groupRelationService; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + + String type = BizUtils.trimAll(MapUtil.getStr(params, "type")); + appendSql(wrapper, type); + + String name = BizUtils.trimAll(MapUtil.getStr(params, "name")); + if(StringUtils.isNotBlank(name)){ + if(Arrays.asList("material", "section").contains(type)){ + wrapper.and(IOT_GROUP_INFO_ENTITY.NAME.like(name).or(IOT_GROUP_INFO_ENTITY.BUSINESS_TYPE.like(name))); + } else { + wrapper.and(IOT_GROUP_INFO_ENTITY.NAME.like(name).or(IOT_GROUP_INFO_ENTITY.CODE.like(name))); + } + } + + String businessType = BizUtils.trimAll(MapUtil.getStr(params, "businessType")); + wrapper.like("business_type", businessType, StringUtils.isNotBlank(businessType)); + + //开始时间 + String beginTime = MapUtil.getStr(params, "beginTime"); + //结束时间 + String endTime = MapUtil.getStr(params, "endTime"); + if (StringUtils.isNotBlank(beginTime) && StringUtils.isNotBlank(endTime)) { + wrapper.between("create_date", DateTimeUtils.convertTimeToLong(beginTime), DateTimeUtils.convertTimeToLong(endTime)); + } + //部门id + Long deptId = MapUtils.getLong(params, "deptId"); + if(ObjectUtil.isNotNull(deptId)){ + wrapper.eq(IotGroupInfoEntity::getDeptId, deptId, ObjectUtil.isNotNull(deptId)); + } + + List ids = MapUtil.get(params, "ids",ArrayList.class); + if (CollectionUtil.isNotEmpty(ids)) { + wrapper.in("id", ids); + } + wrapper.orderBy(IotGroupInfoEntity::getSort).asc(); + + return wrapper; + } + + @Override + public PageData pageList(Map params) { + params.put(Constant.ORDER_FIELD,"type"); + PageData pageData = getPageData(params, IotGroupInfoDTO.class,true); + //数据创建者,以及超管,拥有编辑删除权限, + //默认数据不得编辑删除 + pageData.getList().forEach(IotGroupInfoController::isOperate); + pageData.setList(pageData.getList().parallelStream().sorted(Comparator.comparingLong(IotGroupInfoDTO::getSort)).collect(Collectors.toList())); + return pageData; + } + + @Override + public void saveIotGroupInfoDTO(IotGroupInfoDTO dto) { + if (!UserContext.isAdmin() && ObjectUtil.equals(dto.getIsDefault(), IsDefaultEnum.Y.getValue())) { + throw new SysException("暂无添加系统默认组的权限"); + } + String type = BizUtils.trimAll(dto.getType()); + if (GroupTypeEnum.noneMatch(type)) { + throw new SysException("组类型不正确"); + } + String code = BizUtils.trimAll(dto.getCode()); + String businessType = BizUtils.trimAll(dto.getBusinessType()); + QueryWrapper wrapper = new QueryWrapper(); + //过滤条件 + appendSql(wrapper, type); + wrapper.eq("name", dto.getName(), ObjectUtil.isNotEmpty(dto.getName())) + .eq("code", code, ObjectUtil.isNotEmpty(code)) + .eq("business_type", businessType, ObjectUtil.isNotEmpty(dto.getBusinessType())); + if (mapper.selectCountByQuery(wrapper) > 0) { + throw new SysException("该组已存在,请勿重复添加!"); + } + IotGroupInfoEntity entity =ConvertUtils.convertWithTypeAdapt(dto, IotGroupInfoEntity.class); + if(StringUtils.isEmpty(entity.getCode())){ + entity.setCode(UUID.randomUUID().toString().substring(0,6)); + }else { + entity.setCode(code); + } + entity.setName(BizUtils.trimAll(dto.getName())); + entity.setBusinessType(businessType); + save(entity); + } + + @Override + public void template(HttpServletResponse response) { + ExcelUtils.exportExcel(Lists.newArrayList(), "组管理列表", "组管理列表", IotGroupInfoExcel.class, "组管理列表模板.xls", response); + } + + @Override + public void exportExcel(List ids, Map params, HttpServletResponse response) { + List list = listAs(params, IotGroupInfoDTO.class); + if(CollectionUtil.isNotEmpty(ids)){ + list = list.stream().filter(s -> ids.contains(s.getId())).collect(Collectors.toList()); + } + List groupInfoExcels = ConvertUtils.sourceToTarget(list, IotGroupInfoExcel.class); + ExcelUtils.exportExcel(groupInfoExcels, "组列表", "组列表", IotGroupInfoExcel.class, "组列表.xls", response); + } + + @Override + public void importExcel(MultipartFile file, HttpServletRequest request) { + List sheetData = ExcelUtils.importExcel(file, 1, 1, IotGroupInfoExcel.class, 0); + if(CollectionUtil.isEmpty(sheetData)){ + throw new SysException("导入数据空"); + } + if (!UserContext.isAdmin() && sheetData.stream().anyMatch(s-> ObjectUtil.equals(s.getIsDefault(),IsDefaultEnum.Y.getValue()))) { + throw new SysException("暂无添加系统默认组的权限"); + } + List blankList = sheetData.stream() + .filter(s -> StringUtils.isBlank(BizUtils.trimAll(s.getName())) + || StringUtils.isBlank(BizUtils.trimAll(s.getCode())) + || StringUtils.isBlank(BizUtils.trimAll(s.getType()))) + .sorted(Comparator.comparing(IotGroupInfoExcel::getSort, Comparator.nullsFirst(Long::compareTo))) + .collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(blankList)){ + throw new SysException("组名称/编码/类型不能为空"); + } + + QueryWrapper queryWrapper = QueryWrapper + .create() + .select(max(IOT_GROUP_INFO_ENTITY.SORT)); + + Long maxSort = mapper.selectOneByQueryAs(queryWrapper, Long.class); + maxSort = ObjectUtil.defaultIfNull(maxSort, 1L); + AtomicLong sort = new AtomicLong(maxSort); + List insertList = Lists.newArrayList(); + for (IotGroupInfoExcel sheetDatum : sheetData) { + String name = BizUtils.trimAll(sheetDatum.getName()); + String code = BizUtils.trimAll(sheetDatum.getCode()); + String type = BizUtils.trimAll(sheetDatum.getType()); + if (GroupTypeEnum.noneMatch(type)) { + continue; + } + boolean existsExt = mapper.existsExt(QueryWrapper.create() + .eq(IotGroupInfoEntity::getName, name) + .eq(IotGroupInfoEntity::getCode, code) + .eq(IotGroupInfoEntity::getType, type)); + if(existsExt){ + continue; + } + IotGroupInfoEntity entity = new IotGroupInfoEntity(); + entity.setCode(code); + entity.setName(name); + entity.setType(type); + entity.setIsDefault(sheetDatum.getIsDefault()); + entity.setSort(sort.getAndIncrement()); + entity.setRemark(sheetDatum.getRemark()); + insertList.add(entity); + } + if(CollectionUtil.isNotEmpty(insertList)){ + saveBatch(insertList); + } + } + + @Override + public List findAllByType(String type) { + List groupInfoEntities = mapper.selectListByQuery( + QueryWrapper.create() + .eq(IotGroupInfoEntity::getType, type) + .eq(IotGroupInfoEntity::getTenantCode,UserContext.getRealTenantCode()) + .eq(IotGroupInfoEntity::getCompanyId,UserContext.getRealCompanyId()) + .eq(IotGroupInfoEntity::getIsDefault,IsDefaultEnum.N.getValue()) + .or(IOT_GROUP_INFO_ENTITY.TYPE.eq(type) + .and(IOT_GROUP_INFO_ENTITY.IS_DEFAULT.eq(IsDefaultEnum.Y.getValue()))) + ); + return ConvertUtils.sourceToTarget(groupInfoEntities,IotGroupInfoDTO.class); + } + + @Override + public List findSysDictList(Map params) { + return sysDictDataService.list(params); + } + + public void delete(Long[] ids) { + List groupEntities = mapper.selectListByIds(Arrays.asList(ids)); + if (CollectionUtil.isEmpty(groupEntities)) { + throw new SysException("删除的组不存在"); + } + //如果组已经绑定字典&物实体&物模板,不让其修改组类型 +// List thingDictEntities = dictService.findAllByGroupIdIn(Arrays.asList(ids)); +// if (CollectionUtil.isNotEmpty(thingDictEntities)) { +// //绑定过字典的组 +// List existsDictGroups = groupEntities.stream() +// .filter(g -> thingDictEntities.stream().anyMatch(t -> ObjectUtil.equals(t.getGroupId(), g.getId()))) +// .map(IotGroupInfoEntity::getName) +// .toList(); +// throw new SysException("组<" + String.join(",", existsDictGroups) + ">已经绑定字典,请先删除字典"); +// } +// List groupRelationEntities = groupRelationService.findAllByGroupIdIn(Arrays.asList(ids)); +// if (CollectionUtil.isNotEmpty(groupRelationEntities)) { +// //绑定过字典的组 +// List existsDictGroups = groupEntities.stream() +// .filter(g -> groupRelationEntities.stream().anyMatch(t -> ObjectUtil.equals(t.getGroupId(), g.getId()))) +// .map(IotGroupInfoEntity::getName) +// .toList(); +// throw new SysException("组<" + String.join(",", existsDictGroups) + ">已经绑定物实体,请先删除物实体"); +// } +// +// int mCount = mapper.findCountByGroupIdInMaterial(Arrays.asList(ids)); +// int sCount = mapper.findCountByGroupIdInSection(Arrays.asList(ids)); +// +// if(mCount!=0||sCount!=0){ +// throw new SysException("当前组下存在素材/部件,不可删除"); +// } +// +// +// if (CollectionUtil.isNotEmpty(groupRelationEntities)) { +// //绑定过字典的组 +// List existsDictGroups = groupEntities.stream() +// .filter(g -> groupRelationEntities.stream().anyMatch(t -> ObjectUtil.equals(t.getGroupId(), g.getId()))) +// .map(IotGroupInfoEntity::getName) +// .toList(); +// throw new SysException("组<" + String.join(",", existsDictGroups) + ">已经绑定物实体,请先删除物实体"); +// } +// +// batchDelete(ids); + } + + + @Override + public Set queryDictInfo(String queryType, String type) { + QueryWrapper wrapper = new QueryWrapper(); + //过滤条件 + appendSql(wrapper, type); + if ("1".equals(queryType)) { + wrapper.select("name"); + } else { + wrapper.select("business_type"); + } + List objects = mapper.selectObjectListByQuery(wrapper); + return new HashSet(objects); + } + + @Override + public Result> listTree(String type, String name) { + List resultList = new ArrayList<>(); + HashMap param = new HashMap(); + param.put("type", type); + param.put("name", name); + List infos = listAs(param, IotGroupInfoDTO.class); + Map> map = infos.stream().collect(Collectors.groupingBy(IotGroupInfoDTO::getName)); + + for (Object temp : map.keySet()) { + IotGroupInfoFormatDTO dto = new IotGroupInfoFormatDTO(); + dto.setName(temp.toString()); + dto.setBusinessType(temp.toString()); + dto.setId((new Random().nextInt()*-1- 19900000000L)); + List infoDTOS = map.get(temp).parallelStream().sorted(Comparator.comparingLong(IotGroupInfoDTO::getSort)).collect(Collectors.toList()); + + dto.setThumbnailUrl(infoDTOS.get(0).getThumbnailUrl()); + map.get(temp).forEach(info->{ + info.setName(info.getBusinessType()); + }); + dto.setOrder(infoDTOS.get(0).getSort()); + dto.setTypeList(ConvertUtils.sourceToTarget(infoDTOS,IotGroupInfoFormatDTO.class)); + resultList.add(dto); + } + return new Result>().ok(resultList.parallelStream().sorted(Comparator.comparingLong(IotGroupInfoFormatDTO::getOrder)).collect(Collectors.toList())); + } + + @Override + public Result> groupTypeListByGroupName(String name, String type) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.eq("type", type, ObjectUtil.isNotEmpty(type)) + .eq("name", name, ObjectUtil.isNotEmpty(name)); + List data = ConvertUtils.sourceToTarget(mapper.selectListByQuery(wrapper), IotGroupInfoDTO.class); + return new Result>().ok(data); + } + + @Override + public List importJson(MultipartFile file, HttpServletRequest request) { + List resultErrorList = new ArrayList<>(); + + String jsonString = JsonProcessingUtils.readJson(file); + List iotGroupList = JSON.parseObject(jsonString, new TypeReference<>() { + }); + if (CollectionUtil.isEmpty(iotGroupList)) { + throw new SysException("导入json为空,请检查json后再进行导入"); + } + for (IotGroupInfoDTO temp : iotGroupList) { + saveIotGroupInfoDTO(temp); + resultErrorList.add(temp); + } + return resultErrorList; + } + + @Override + public List findAllList(Map params) { + params.put("page","1"); + params.put("limit","99999"); + return pageList(params).getList(); + } + + @Override + public IotGroupInfoEntity findByCode(String code) { + IotGroupInfoEntity entity = mapper.selectOneByQuery(QueryWrapper.create().eq(IotGroupInfoEntity::getCode, code)); + if(ObjectUtil.equals(entity.getIsDefault(),IsDefaultEnum.N.getValue()) && !ObjectUtil.equals(entity.getTenantCode(),UserContext.getTenantCode())){ + return null; + } + return entity; + } + + @Override + public List findByCodeIn(List code) { + /** + * .in(IotGroupInfoEntity::getTenantCode,UserContext.getRealTenantCode()) + * .in(IotGroupInfoEntity::getCompanyId,UserContext.getRealCompanyId()) + * .or() + * .in(IotGroupInfoEntity::getIsDefault,IsDefaultEnum.Y.getValue()) + * .in(IotGroupInfoEntity::getCode,code) + */ + return mapper.selectListByQuery( + QueryWrapper.create() + .in(IotGroupInfoEntity::getCode, code) + .in(IotGroupInfoEntity::getTenantCode, UserContext.getRealTenantCode()) + .in(IotGroupInfoEntity::getCompanyId, UserContext.getRealCompanyId()) + .or(IotGroupInfoEntity::getIsDefault).eq(IsDefaultEnum.Y.getValue()) + .and(IotGroupInfoEntity::getCode).in(code)); + } + + @Override + public List findByIdIn(Collection id) { + return mapper.selectListByIds(id); + } + + @Override + public void transferGroupInfo(Long tenantCode) { + List types = Lists.newArrayList(GroupTypeEnum.ATTR.getValue(), GroupTypeEnum.TAG.getValue(), GroupTypeEnum.THING_TYPE.getValue()); + long selectCount = mapper.selectCountByQuery(QueryWrapper.create().in(IotGroupInfoEntity::getType, types).eq(IotGroupInfoEntity::getTenantCode, tenantCode)); + if(selectCount > 0) { + throw new SysException("数据已存在"); + } + List iotGroupEntityList = mapper.selectListExt(QueryWrapper.create().in(IotGroupInfoEntity::getType, types)); + if(CollectionUtil.isEmpty(iotGroupEntityList)) { + throw new SysException("未找到可同步数据"); + } + //组 + for (IotGroupInfoEntity entity : iotGroupEntityList) { + long newId = new SnowFlakeIDKeyGenerator().nextId(); + groupRelationService.syncGroupRelation(entity.getId(), newId); + // dictService.syncGroupRelation(entity.getId(), newId); + entity.setId(newId); + entity.setTenantCode(tenantCode); + entity.setCompanyId(tenantCode); + entity.setDeptId(tenantCode); + save(entity); + } + } + + @Override + public Long getMaxSort(String type) { + Long sort = 1L; + QueryWrapper queryWrapper = QueryWrapper.create() + .eq(IotGroupInfoEntity::getType,type).eq(IotGroupInfoEntity::getTenantCode,UserContext.getRealTenantCode()) + .orderBy(IotGroupInfoEntity::getSort).desc().limit(1); + IotGroupInfoEntity groupInfoEntity = mapper.selectOneByQuery(queryWrapper); + if(ObjectUtil.isNotNull(groupInfoEntity)){ + return groupInfoEntity.getSort()+sort; + } + return sort; + } + + @Override + public void updateIotGroupInfoDTO(IotGroupInfoDTO dto) { + Long id = dto.getId(); + IotGroupInfoEntity groupInfoEntity = getById(id); + if (ObjectUtil.isNull(groupInfoEntity)) { + throw new SysException("组不存在,无法修改"); + } + String type = BizUtils.trimAll(dto.getType()); + if (GroupTypeEnum.noneMatch(type)) { + throw new SysException("组类型不正确"); + } + //如果组已经绑定字典&物实体&物模板,不让其修改组类型 + //List thingDictEntities = dictService.findAllByGroupId(id); + List groupRelationEntities = groupRelationService.findAllByGroupId(id); + if (/*(CollectionUtil.isNotEmpty(thingDictEntities) ||*/ CollectionUtil.isNotEmpty(groupRelationEntities)) + /*&& !StringUtils.equals(groupInfoEntity.getType(), dto.getType()))*/ { + throw new SysException("当前组关联了字典或者物实体,不能修改组类型"); + } + if (!UserContext.isAdmin() && ObjectUtil.equals(groupInfoEntity.getIsDefault(), IsDefaultEnum.Y.getValue())) { + throw new SysException("暂无修改系统默认组的权限"); + } + String name = BizUtils.trimAll(dto.getName()); + String code = BizUtils.trimAll(dto.getCode()); + String businessType = BizUtils.trimAll(dto.getBusinessType()); + //判断是否已经有重复的组了 + QueryWrapper wrapper = new QueryWrapper(); + //过滤条件 + appendSql(wrapper, type); + wrapper.eq("code", code, ObjectUtil.isNotEmpty(code)) + .eq("business_type", businessType, ObjectUtil.isNotEmpty(dto.getBusinessType())) + .ne("id", id); + if (mapper.selectCountByQuery(wrapper) > 0) { + throw new SysException("该组已存在,请勿重复添加!"); + } + groupInfoEntity.setCode(code); + groupInfoEntity.setName(name); + groupInfoEntity.setType(type); + groupInfoEntity.setDeptId(dto.getDeptId()); + groupInfoEntity.setTenantCode(UserContext.getRealTenantCode()); + groupInfoEntity.setCompanyId(UserContext.getRealCompanyId()); + groupInfoEntity.setBusinessType(businessType); + groupInfoEntity.setThumbnailUrl(dto.getThumbnailUrl()); + groupInfoEntity.setIsDefault(dto.getIsDefault()); + groupInfoEntity.setRemark(dto.getRemark()); + updateById(groupInfoEntity); + } + + @Override + public void batchDeleteByIds(Long[] ids) { + List groupRelationEntities = groupRelationService.findAllByGroupIdIn(List.of(ids)); + if(CollectionUtils.isNotEmpty(groupRelationEntities)){ + throw new SysException("当前组关联了物实体,无法删除"); + } + mapper.deleteBatchByIds(Arrays.asList(ids)); + } + + + /** + * 权限条件 查询类型thing=物/attr=属性/tag=标签/relation=关系/section=部件/material=素材 + * 权限过滤,目前是,admin查所有,租户查自己以及默认数据 + * 后期优化为切面统一处理 + */ + public void appendSql(QueryWrapper wrapper, String type) { + if(StringUtils.isBlank(type)){ + wrapper.notIn("type", Arrays.asList("material", "section")) + .and( + IOT_GROUP_INFO_ENTITY.IS_DEFAULT.eq(0) + .or(IOT_GROUP_INFO_ENTITY.TENANT_CODE.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + }else { + wrapper.eq("type", type); + if (Arrays.asList("material", "section").contains(type)){ + UserDetail userDetail = SecurityUser.getUser(); + if (!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(UserContext.getRealTenantCode(), userDetail.getTenantCode())) { + wrapper.and( + IOT_GROUP_INFO_ENTITY.IS_DEFAULT.eq(0) + .or(IOT_GROUP_INFO_ENTITY.TENANT_CODE.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + }else { + wrapper.and( + IOT_GROUP_INFO_ENTITY.IS_DEFAULT.eq(0) + .or(IOT_GROUP_INFO_ENTITY.TENANT_CODE.in(tenantSubsetUtil.paramsAddTenantCodeList(true)))); + } + } + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupRelationServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupRelationServiceImpl.java new file mode 100644 index 0000000..9e79696 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/group/service/impl/IotGroupRelationServiceImpl.java @@ -0,0 +1,250 @@ +package com.thing.thing.group.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.Lists; +import com.mybatisflex.core.paginate.Page; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.GroupTypeEnum; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.ConvertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.group.dto.IotGroupEntityDTO; +import com.thing.thing.group.dto.IotGroupEntityListDTO; +import com.thing.thing.group.dto.IotGroupRelationDTO; +import com.thing.thing.group.entity.IotGroupInfoEntity; +import com.thing.thing.group.entity.IotGroupRelationEntity; +import com.thing.thing.group.excel.IotGroupRelationExcel; +import com.thing.thing.group.mapper.IotGroupRelationMapper; +import com.thing.thing.group.service.IotGroupInfoService; +import com.thing.thing.group.service.IotGroupRelationService; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import static com.thing.thing.entity.entity.table.IotThingEntityTableDef.IOT_THING_ENTITY; +import static com.thing.thing.group.entity.table.IotGroupInfoEntityTableDef.IOT_GROUP_INFO_ENTITY; +import static com.thing.thing.group.entity.table.IotGroupRelationEntityTableDef.IOT_GROUP_RELATION_ENTITY; + + +/** + * 实体&组,实体&物关系 的关系表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Service +@RequiredArgsConstructor +public class IotGroupRelationServiceImpl extends BaseServiceImpl implements IotGroupRelationService { + + private final IotThingEntityService thingEntityService; + private final IotGroupInfoService groupInfoService; + + + + @Override + public QueryWrapper getWrapper(Map params) { + List groupList = Arrays.stream(StringUtils.split(MapUtils.getString(params, "groupIds"),",")).map(Long::parseLong).toList(); + String entityType = MapUtils.getString(params, "entityType"); + String groupName = MapUtils.getString(params, "groupName"); + String code = MapUtils.getString(params, "code"); + Long deptId = MapUtils.getLong(params, "deptId"); + + + Long tenantCode = UserContext.getRealTenantCode(); + + QueryWrapper wrapper = new QueryWrapper(); + wrapper.select( + IOT_THING_ENTITY.ID.as("entity_id"), + IOT_THING_ENTITY.CODE.as("entity_code"), + IOT_THING_ENTITY.NAME.as("entity_name"), + IOT_THING_ENTITY.REAL_TYPE.as("real_type"), + IOT_THING_ENTITY.TYPE.as("entity_type"), + IOT_THING_ENTITY.DEPT_IDS, + IOT_GROUP_INFO_ENTITY.ID.as("group_id"), + IOT_GROUP_RELATION_ENTITY.ID.as("id"), + IOT_GROUP_INFO_ENTITY.NAME.as("group_name"), + IOT_GROUP_INFO_ENTITY.CODE.as("group_code"), + IOT_GROUP_INFO_ENTITY.REMARK + ) + .from(IOT_THING_ENTITY.as("e")) + .join(IOT_GROUP_RELATION_ENTITY).as("m") + .on(IOT_THING_ENTITY.ID.eq(IOT_GROUP_RELATION_ENTITY.ENTITY_ID)) + .join(IOT_GROUP_INFO_ENTITY).as("g") + .on(IOT_GROUP_INFO_ENTITY.ID.eq(IOT_GROUP_RELATION_ENTITY.GROUP_ID)) + .like(IotGroupInfoEntity::getName, groupName, StrUtil.isNotBlank(groupName)) + .like(IotThingEntity::getCode, groupName, StrUtil.isNotBlank(groupName)) + .eq(IotThingEntity::getName, code, StrUtil.isNotBlank(code)) + .eq(IotThingEntity::getCode, code, StrUtil.isNotBlank(code)) + .eq(IotThingEntity::getType, entityType, StrUtil.isNotBlank(entityType)) + .in(IotGroupRelationEntity::getGroupId, groupList, CollectionUtil.isNotEmpty(groupList)) + .eq(IotThingEntity::getTemplateMark, "0") + // .eq(IotThingEntity::getDeptId, deptId, ObjectUtil.isNotNull(deptId)) + .eq(IotThingEntity::getTenantCode, tenantCode, ObjectUtil.isNotNull(tenantCode)) + .orderBy(IOT_GROUP_INFO_ENTITY.SORT.desc()); + + return wrapper; + } + + + @Override + public PageData pageList(Map params) { + //组的类型 + String type = MapUtils.getString(params, "type"); + if (GroupTypeEnum.noneMatch(type)) { + throw new SysException("组类型不匹配"); + } + QueryWrapper queryWrapper = getWrapper(params); + Page groupEntityListDTOPage = mapper.paginateAs(new Page<>(MapUtil.getInt(params, "page", 1), MapUtil.getInt(params, "limit", 10)), + queryWrapper, IotGroupEntityListDTO.class); + return new PageData<>(groupEntityListDTOPage.getRecords(), groupEntityListDTOPage.getTotalRow()); + } + + @Override + public List findAllList(Map params) { + //组的类型 + String type = MapUtils.getString(params, "type"); + if (GroupTypeEnum.noneMatch(type)) { + throw new SysException("组类型不匹配"); + } + String groupIds = MapUtils.getString(params, "groupIds"); + List groupList = Lists.newArrayList(); + if (StringUtils.isNotBlank(groupIds)) { + groupList.addAll(Arrays.stream(groupIds.split(",")).map(Long::parseLong).toList()); + } + params.put("tenantCode", UserContext.getRealTenantCode()); + params.put("companyId",UserContext.getRealCompanyId()); + + //组的类型是thing + if (CompareUtil.compare(type, GroupTypeEnum.THING.getValue()) == 0) { + //原因是物的type已经有相关查询 + List baseThingDTOIPage = mapper.thingPageList(params,groupList,null); + return baseThingDTOIPage; + } + //组的类型是标签或者属性 + if (CompareUtil.compare(type, GroupTypeEnum.ATTR.getValue()) == 0 || CompareUtil.compare(type, GroupTypeEnum.TAG.getValue()) == 0) { + List baseThingDTOS = mapper.dictPageList(params,groupList,null); + return baseThingDTOS; + } + return Lists.newArrayList(); + } + + + + @Override + public List export(Long[] ids, Map params, HttpServletResponse response) { + List idList = Lists.newArrayList(); + if(ArrayUtils.isNotEmpty(ids)){ + idList.addAll(Arrays.asList(ids)); + } + String type = MapUtils.getString(params, "type"); + if (GroupTypeEnum.noneMatch(type)) { + throw new SysException("组类型不匹配"); + } + String groupIds = MapUtils.getString(params, "groupIds"); + List groupIdList = Lists.newArrayList(); + if(StringUtils.isNotBlank(groupIds)){ + groupIdList.addAll(Arrays.stream(groupIds.split(",")).map(Long::parseLong).collect(Collectors.toList())); + } + if (CompareUtil.compare(type, GroupTypeEnum.THING.getValue()) == 0) { + List list = mapper.thingPageList(params,groupIdList,idList); + return ConvertUtils.sourceToTarget(list,IotGroupRelationExcel.class); + } + //组的类型是标签或者属性 + if (CompareUtil.compare(type, GroupTypeEnum.ATTR.getValue()) == 0 || CompareUtil.compare(type, GroupTypeEnum.TAG.getValue()) == 0) { + List list = mapper.dictPageList(params,groupIdList,idList); + return ConvertUtils.sourceToTarget(list,IotGroupRelationExcel.class); + } + return Lists.newArrayList(); + } + + + @Override + public Map getAttrExtendDataMapByTenantCode(Map params) { + getAttrExtendDataParams(params); + List attrExtendData = mapper.getAttrExtendDataByTenantCode(params); + Map map = new HashMap<>(); + attrExtendData.forEach(temp -> { + map.put(temp.getEntityCode(), temp.getUnit()); + }); + return map; + } + + @Override + public List findAllByGroupId(Long groupId) { + return mapper.selectListByQuery(QueryWrapper.create().eq(IotGroupRelationEntity::getGroupId, groupId)); + } + + @Override + public List findAllByGroupIdIn(List groupId) { + return mapper.selectListByQuery(QueryWrapper.create().in(IotGroupRelationEntity::getGroupId, groupId)); + } + + @Override + public void saveEntity(IotGroupEntityDTO dto) { + List groupIds = dto.getGroupIds().stream().distinct().collect(Collectors.toList()); + List groupInfoEntities = groupInfoService.findByIdIn(groupIds); + if(CollectionUtil.isEmpty(groupInfoEntities)){ + throw new SysException("组不存在"); + } + if(groupInfoEntities.stream().anyMatch(g-> !StringUtils.equals(g.getType(),dto.getEntityType()))){ + throw new SysException("组关系类型不匹配"); + } + List entityIds = dto.getEntityIds().stream().distinct().collect(Collectors.toList()); + List entities = thingEntityService.findEntityByIds(entityIds); + if(CollectionUtil.isEmpty(entities)){ + throw new SysException("实体不存在"); + } + //查询已经存在的关系 + List groupRelationEntities = mapper.selectListByQuery(QueryWrapper.create() + .in(IotGroupRelationEntity::getGroupId,groupIds) + .in(IotGroupRelationEntity::getEntityId,entityIds) + .eq(IotGroupRelationEntity::getEntityType,dto.getEntityType()) + ); + + for (Long groupId : groupIds) { + List insertList = entityIds.stream() + .filter(entityId -> groupRelationEntities.stream().noneMatch(r -> ObjectUtil.equals(r.getGroupId(), groupId) && ObjectUtil.equals(r.getEntityId(), entityId))) + .map(entityId -> { + IotGroupRelationEntity relationEntity = new IotGroupRelationEntity(); + relationEntity.setGroupId(groupId); + relationEntity.setEntityId(entityId); + relationEntity.setEntityType(dto.getEntityType()); + return relationEntity; + }).collect(Collectors.toList()); + if(CollectionUtil.isNotEmpty(insertList)){ + saveBatch(insertList); + } + } + } + + @Override + public void syncGroupRelation(Long id, long newId) { + List list = mapper.selectListByQuery(QueryWrapper.create().eq(IotGroupRelationEntity::getGroupId, id) + .select(IotGroupRelationEntity::getEntityId)); + if (CollectionUtil.isNotEmpty(list)) { + saveBatch(list.stream().peek(item -> item.setGroupId(newId)).collect(Collectors.toList())); + } + } + + private void getAttrExtendDataParams(Map params) { + params.put("groupType", GroupTypeEnum.ATTR.getValue()); + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/controller/IotThingModelController.java b/modules/thing/src/main/java/com/thing/thing/model/controller/IotThingModelController.java new file mode 100644 index 0000000..eee14fa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/controller/IotThingModelController.java @@ -0,0 +1,176 @@ +package com.thing.thing.model.controller; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.utils.excel.ExcelUtils; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.thing.model.dto.IotThingModelExcel; +import com.thing.thing.model.dto.ModelDetailDTO; +import com.thing.thing.model.param.DistributeModelParam; +import com.thing.thing.model.service.IotThingModelService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 物模型表 + * + * @author xunxueli + * @since 3.0 2024-03-18 + */ +@RestController +@RequestMapping("v2/model") +@Tag(name = "物模型表") +public class IotThingModelController { + + @Resource + private IotThingModelService service; + + @GetMapping("page") + @Operation(summary = "分页") + public Result> page( + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") @RequestParam Integer page, + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") @RequestParam Integer limit, + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "code", description = "物编码") @RequestParam(required = false) String code, + @Parameter(name = "token", description = "物令牌(无)") @RequestParam(required = false) String token, + @Parameter(name = "origin", description = "数据来源") @RequestParam(required = false) String origin, + @Parameter(name = "status", description = "在线离线状态,0离线 1在线 2错误 3未接入") @RequestParam(required = false) String status, + @Parameter(name = "realType", description = "真实/虚拟类型") @RequestParam(required = false) String realType, + @Parameter(name = "startTime", description = "开始时间") @RequestParam(required = false) Long startTime, + @Parameter(name = "endTime", description = "结束时间") @RequestParam(required = false) Long endTime) { + PageData pageList = service.pageList(page,limit,orderField,order,code,token,origin,status,realType,startTime,endTime); + return new Result>().ok(pageList); + } + + @GetMapping("list") + @Operation(summary = "列表") + public Result> list( + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "code", description = "物编码") @RequestParam(required = false) String code, + @Parameter(name = "token", description = "物令牌(无)") @RequestParam(required = false) String token, + @Parameter(name = "origin", description = "数据来源") @RequestParam(required = false) String origin, + @Parameter(name = "status", description = "在线离线状态,0离线 1在线 2错误 3未接入") @RequestParam(required = false) String status, + @Parameter(name = "realType", description = "在线离线状态,0离线 1在线 2错误 3未接入") @RequestParam(required = false) String realType, + @Parameter(name = "startTime", description = "开始时间") @RequestParam(required = false) Long startTime, + @Parameter(name = "endTime", description = "结束时间") @RequestParam(required = false) Long endTime) { + List list = service.findList(orderField, order, code, token, origin, status, realType,startTime, endTime); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary = "信息") + public Result get(@PathVariable("id") Long id) { + ModelDetailDTO data = service.findById(id); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary = "保存物模型,并分配给多个企业") + @LogOperation("保存物模型,并分配给多个企业") + public Result save(@RequestBody DistributeModelParam dto) { + //效验数据 + // ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.save(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary = "修改一个物模型实体,并分配给多个企业") + @LogOperation("修改") + public Result update(@RequestBody DistributeModelParam dto) { + //效验数据 + //ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.update(dto); + return new Result<>(); + } + + @PostMapping("distribute") + @Operation(summary = "将多个物分配给一个企业") + @LogOperation("将多个物分配给一个企业") + public Result distribute(@RequestBody DistributeModelParam dto) { + service.distribute(dto); + return new Result<>(); + } + + + @DeleteMapping + @Operation(summary = "删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids) { + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDelete(ids); + return new Result<>(); + } + + @PostMapping("export") + @Operation(summary="导出") + @LogOperation("导出") + public void export( @RequestBody Long[] ids, @RequestParam Map params, HttpServletResponse response) { + ExcelUtils.exportExcel(service.export(ids, params), "物模型列表", "物模型", IotThingModelExcel.class, "物模型列表.xls", response); + } + + @GetMapping("origins") + @Operation(summary="物模型数据来源") + public Result> originList() { + return new Result>().ok(service.findAllOrigin()); + } + + + @GetMapping("latestData") + @Operation(summary="获取code在时序数据库中的时序属性") + @Parameters({ + @Parameter(name = "entityCode",description ="物编码"), + @Parameter(name = "attrs",description ="属性编码集合") + }) + public Result> latestData(String entityCode,String attrs,Long startTime,Long endTime) { + return new Result>().ok(service.latestData(entityCode,attrs,startTime,endTime)); + } + + @GetMapping("getSequenceAttr") + @Operation(summary="获取code在时序数据库中的时序属性") + @Parameters({ + @Parameter(name = "entityCode",description ="物编码"), + @Parameter(name = "startTime",description ="开始时间"), + @Parameter(name = "endTime",description ="结束时间"), + @Parameter(name = "attrs",description ="属性编码集合") + }) + public Result> getSequenceAttr(String entityCode,String attrs,Long startTime,Long endTime) { + return new Result>().ok(service.getSequenceAttr(entityCode,attrs,startTime,endTime)); + } + + @PostMapping("saveSequenceAttr") + @Operation(summary="设备的时序属性保存和更新") + public Result saveSequenceAttr(@RequestBody List tsKvDTOS) { + service.saveSequenceAttr(tsKvDTOS); + return new Result<>(); + } + + @PostMapping("deleteSequenceAttr") + @Operation(summary="设备的时序属性删除") + public Result deleteSequenceAttr(@RequestBody List tsKvDTOS) { + service.deleteSequenceAttr(tsKvDTOS); + return new Result<>(); + } + + @GetMapping("status") + @Operation(summary="设备状态列表") + public Result> status() { + return new Result>().ok(service.status()); + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelDTO.java b/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelDTO.java new file mode 100644 index 0000000..701bbfd --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelDTO.java @@ -0,0 +1,51 @@ +package com.thing.thing.model.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 物模型表 + * + * @author mark + * @since 3.0 2024-03-18 + */ +@Data +@Schema(description = "物模型表") +public class IotThingModelDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "物编码") + private String code; + @Schema(description = "TB: token") + private String token; + @Schema(description = "是否网关: 0否1是") + private String gateway; + @Schema(description = "在线离线状态,0离线 1在线 2错误 3未接入") + private String status; + @Schema(description = "最新状态改变时间") + @JsonSerialize(using = ToStringSerializer.class) + private Long statusTs; + @Schema(description = "被分配到几个租户") + @JsonSerialize(using = ToStringSerializer.class) + private Long authNum; + @Schema(description = "数据来源") + private String origin; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "创建时间") + @JsonSerialize(using = ToStringSerializer.class) + private Long createDate; + @Schema(description = "租户内物存在类型,0虚拟 1真实") + private String realType; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelExcel.java b/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelExcel.java new file mode 100644 index 0000000..4001703 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/dto/IotThingModelExcel.java @@ -0,0 +1,34 @@ +package com.thing.thing.model.dto; + + +import cn.afterturn.easypoi.excel.annotation.Excel; +import lombok.Data; + +/** + * 物模型表 + * + * @author mark + * @since 3.0 2023-06-05 + */ +@Data +public class IotThingModelExcel { + + @Excel(name = "物编码",width= 10) + private String code; + + @Excel(name = "物令牌",width= 40) + private String token; + + @Excel(name = "是否网关: 0否1是") + private String gateway; + + @Excel(name = "被分配到几个租户") + private Long authNum; + + @Excel(name = "数据来源",width= 25) + private String origin; + + @Excel(name = "备注说明",width= 50) + private String remark; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/dto/ModelDetailDTO.java b/modules/thing/src/main/java/com/thing/thing/model/dto/ModelDetailDTO.java new file mode 100644 index 0000000..1bc59ef --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/dto/ModelDetailDTO.java @@ -0,0 +1,28 @@ +package com.thing.thing.model.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 物模型表 + * + * @author mark + * @since 3.0 2023-06-05 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@Schema( name= "物模型详情表") +public class ModelDetailDTO extends IotThingModelDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "分配租户编码(分配企业功能参数)") + private List tenantCodes; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/entity/IotThingModelEntity.java b/modules/thing/src/main/java/com/thing/thing/model/entity/IotThingModelEntity.java new file mode 100644 index 0000000..ffca7ff --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/entity/IotThingModelEntity.java @@ -0,0 +1,67 @@ +package com.thing.thing.model.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseDateEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 物模型表 + * + * @author mark + * @since 3.0 2024-03-18 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper = false) +@Table("iot_thing_model") +public class IotThingModelEntity extends BaseDateEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 物编码 + */ + private String code; + /** + * TB: token + */ + private String token; + /** + * 是否网关: 0否1是 + */ + private String gateway; + /** + * 在线离线状态,0离线 1在线 2错误 3未接入 + */ + private String status; + /** + * 最新状态改变时间 + */ + private Long statusTs; + /** + * 被分配到几个租户 + */ + private Long authNum; + /** + * 数据来源 + */ + private String origin; + /** + * 租户内物存在类型,0虚拟 1真实 + */ + private String realType; + /** + * 备注说明 + */ + private String remark; + /** + * 备注字段 + */ + private String extendData; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/mapper/IotThingModelMapper.java b/modules/thing/src/main/java/com/thing/thing/model/mapper/IotThingModelMapper.java new file mode 100644 index 0000000..dc54f51 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/mapper/IotThingModelMapper.java @@ -0,0 +1,16 @@ +package com.thing.thing.model.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.model.entity.IotThingModelEntity; +import org.apache.ibatis.annotations.Mapper; + +/** + * 物模型表 + * + * @author xc + * @since 3.0 2024-03-18 + */ +@Mapper +public interface IotThingModelMapper extends PowerBaseMapper { + +} diff --git a/modules/thing/src/main/java/com/thing/thing/model/param/DistributeModelParam.java b/modules/thing/src/main/java/com/thing/thing/model/param/DistributeModelParam.java new file mode 100644 index 0000000..0e45e3b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/param/DistributeModelParam.java @@ -0,0 +1,42 @@ +package com.thing.thing.model.param; + +import com.thing.thing.model.dto.IotThingModelDTO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 物模型表 + * + * @author xc/ls + * @since 3.0 2023-06-05 + */ +@Data +@Schema( name= "物模型表") +public class DistributeModelParam implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id") + private Long id; + @Schema(description = "物编码") + private String code; + @Schema(description = "是否网关: 0否1是") + private String gateway; + @Schema(description = "备注说明") + private String remark; + @Schema(description = "物模型:分配租户编码(分配企业功能参数)") + private List tenantCodes; + @Schema(description = "物模型:分配企业:属性组集合") + private List groupNames; + @Schema(description = "物模型:分配企业:属性信息列表") + private List attrs; + @Schema(description = "物模型:分配企业:物编码集合(分配企业功能参数)") + private List codes; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/model/service/IotThingModelService.java b/modules/thing/src/main/java/com/thing/thing/model/service/IotThingModelService.java new file mode 100644 index 0000000..ab20ca4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/service/IotThingModelService.java @@ -0,0 +1,58 @@ +package com.thing.thing.model.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.model.dto.IotThingModelDTO; +import com.thing.thing.model.dto.IotThingModelExcel; +import com.thing.thing.model.dto.ModelDetailDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.thing.model.param.DistributeModelParam; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public interface IotThingModelService extends IBaseService { + + PageData pageList(Integer page,Integer limit,String orderField,String order, + String code,String token,String origin,String status,String realType,Long startTime,Long endTime); + + List findList(String orderField, String order, + String code, String token,String origin,String status,String realType,Long startTime,Long endTime); + + ModelDetailDTO findById(Long id); + + Optional findByCode(String code); + + Optional> findByGateway(String gateway); + + List findByCodeIn(Collection codeList); + + List findAllOrigin(); + + Map status(); + + List getSequenceAttr(String entityCode, String attrs, Long startTime, Long endTime); + + List latestData(String entityCode, String attrs, Long startTime, Long endTime); + + void saveSequenceAttr(List tsKvDTOS); + + void deleteSequenceAttr(List tsKvDTOS); + + boolean save(DistributeModelParam dto); + + boolean update(DistributeModelParam dto); + + void distribute(DistributeModelParam dto); + + List export(Long[] ids, Map params); + + void updateModelAuthNumByCodes(List codes); + + void batchSaveOrUpdate(List entities); + +} diff --git a/modules/thing/src/main/java/com/thing/thing/model/service/impl/IotThingModelServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/model/service/impl/IotThingModelServiceImpl.java new file mode 100644 index 0000000..d95079b --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/model/service/impl/IotThingModelServiceImpl.java @@ -0,0 +1,445 @@ +package com.thing.thing.model.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.CaseFormat; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.*; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.*; +import com.thing.common.core.web.response.PageData; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.common.tskv.service.TsKvService; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.dictRelation.service.IotThingDictRelationService; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.entity.IotThingEntity; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.model.dto.IotThingModelDTO; +import com.thing.thing.model.dto.IotThingModelExcel; +import com.thing.thing.model.dto.ModelDetailDTO; +import com.thing.thing.model.entity.IotThingModelEntity; +import com.thing.thing.model.mapper.IotThingModelMapper; +import com.thing.thing.model.param.DistributeModelParam; +import com.thing.thing.model.service.IotThingModelService; +import com.thing.transport.api.adaptor.JsonConverter; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +public class IotThingModelServiceImpl extends BaseServiceImpl implements IotThingModelService { + + @Resource + private IotThingEntityService thingEntityService; + + @Resource + private IotThingDictRelationService dictRelationsService; + + @Resource + private TsKvService tsKvService; + + @Resource + private ThingCache cache; + + @Override + public QueryWrapper getWrapper(Map params) { + QueryWrapper wrapper = new QueryWrapper(); + String code = (String) params.get("code"); + String token = (String) params.get("token"); + String startTime = (String) params.get("startTime"); + String endTime = (String) params.get("endTime"); + String orderField = (String) params.get("orderField"); + String order = (String) params.get("order"); + wrapper + .like(IotThingModelEntity::getCode, code, StringUtils.isNotBlank(code)) + .like(IotThingModelEntity::getToken, token, StringUtils.isNotBlank(token)) + .between(IotThingModelEntity::getCreateDate, + DateTimeUtils.convertTimeToLong(startTime), + DateTimeUtils.convertTimeToLong(endTime), + StringUtils.isNotBlank(startTime) && StringUtils.isNotBlank(endTime)) + .orderBy(StringUtils.isNotBlank(orderField) ? orderField : "create_date", StringUtils.equals(order, Constant.ASC)) + ; + return wrapper; + } + + public QueryWrapper getWrapper(String orderField, + String order, + String code, + String token, + String origin, + String status, + String realType, + Long startTime, + Long endTime) { + + QueryWrapper wrapper = new QueryWrapper(); + wrapper + .like(IotThingModelEntity::getCode, code, StringUtils.isNotBlank(code)) + .like(IotThingModelEntity::getToken, token, StringUtils.isNotBlank(token)) + .eq(IotThingModelEntity::getOrigin, origin, StringUtils.isNotBlank(origin)) + .eq(IotThingModelEntity::getStatus, status, StringUtils.isNotBlank(status)) + .eq(IotThingModelEntity::getRealType, realType, StringUtils.isNotBlank(realType)) + .between(IotThingModelEntity::getCreateDate, + startTime, endTime, !Objects.isNull(startTime) && !Objects.isNull(endTime)) + .orderBy(StringUtils.isNotBlank(orderField) ? orderField : CacheNameEnum.ModelField.THING_MODEL_CREATE_DATE.getField() + , StringUtils.equalsIgnoreCase(order, Constant.ASC)) + ; + return wrapper; + } + + @Override + public PageData pageList(Integer page, Integer limit, String orderField, String order, + String code, + String token, + String origin, + String status, + String realType, + Long startTime, + Long endTime) { + List list = findList(orderField, order, code, token, origin, status, realType, startTime, endTime); + if (CollectionUtils.isEmpty(list)) { + return PageData.empty(); + } + List resList = PageUtils.startPage(list, page, limit); + return new PageData<>(resList, CollectionUtils.size(list)); + } + + + @Override + public List findList(String orderField, String order, + String code, + String token, + String origin, + String status, + String realType, + Long startTime, + Long endTime) { + List modelList = cache.getTopicMap(CacheNameEnum.THING_MODEL); + if (CollectionUtils.isEmpty(modelList)) { + List list = mapper.selectListByQueryAs(getWrapper(orderField, order, null, null, null, + null, null, null, null), IotThingModelDTO.class); + modelList = JsonConverter.convertToJsonObjectListObjectNode(list); + //更新缓存 + CacheInit.modelMap(modelList, cache); + } + if (StringUtils.isBlank(orderField)) { + orderField = CacheNameEnum.ModelField.THING_MODEL_CREATE_DATE.getField(); + } + String finalOrderField = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, orderField); + Comparator comparator = CompareUtils.getComparator(order, finalOrderField);//封装参数 + List> pairs = buildParam(code, token, origin, status, realType); + return modelList.stream().filter(jsonObject -> JacksonUtil.filter(jsonObject, pairs, finalOrderField, startTime, endTime)) + .sorted(comparator.thenComparing(obj -> obj.get(CacheNameEnum.ModelField.THING_MODEL_ID.getField()).asLong())).toList(); + } + + @Override + public ModelDetailDTO findById(Long id) { + ModelDetailDTO modelDetailDTO = mapper.selectOneByQueryAs(QueryWrapper.create().eq(IotThingModelEntity::getId, id), ModelDetailDTO.class); + if (ObjectUtil.isNull(modelDetailDTO)) { + throw new SysException("物模型不存在"); + } + // 处理物模型数据 + String code = modelDetailDTO.getCode(); + List entities = thingEntityService.findAllByCodeInAndTenantCode(Collections.singletonList(code), null); + List list = entities.stream().map(IotThingEntity::getTenantCode).distinct().toList(); + modelDetailDTO.setAuthNum((long) CollectionUtils.size(list)); + modelDetailDTO.setTenantCodes(list); + return modelDetailDTO; + } + + @Override + public Optional findByCode(String code) { + return Optional.ofNullable(mapper.selectOneByQueryAs(QueryWrapper.create().eq(IotThingModelEntity::getCode, code), ModelDetailDTO.class)); + } + + @Override + public Optional> findByGateway(String gateway) { + List topicMap = cache.getTopicMap(CacheNameEnum.THING_MODEL); + if (CollectionUtils.isEmpty(topicMap)) { + List modelDTOList = mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingModelEntity::getGateway, gateway), IotThingModelDTO.class); + return Optional.ofNullable(JsonConverter.convertToJsonObjectListObjectNode(modelDTOList)); + } + return Optional.of(topicMap.stream() + .filter(obj -> StringUtils.equals(obj.get(CacheNameEnum.ModelField.THING_MODEL_GATEWAY.getField()).asText(), gateway)) + .toList()); + } + + @Override + public List findByCodeIn(Collection codeList) { + List modelList = cache.findAllKeyMap(CacheNameEnum.THING_MODEL, codeList); + if (CollectionUtils.isEmpty(modelList)) { + List modelDTOList = mapper.selectListByQueryAs(QueryWrapper.create().in(IotThingModelEntity::getCode, codeList), IotThingModelDTO.class); + return JsonConverter.convertToJsonObjectListObjectNode(modelDTOList); + } + return modelList; + } + + @Override + public List findAllOrigin() { + List modelList = findList(CacheNameEnum.ModelField.THING_MODEL_CREATE_DATE.getField(), + Constant.DESC, null, null, null, null, null, null, null); + if (CollectionUtil.isEmpty(modelList)) { + return Lists.newArrayList(); + } + return modelList.stream().map(s -> s.get(CacheNameEnum.ModelField.THING_MODEL_ORIGIN.getField()).asText()).distinct().collect(Collectors.toList()); + } + + @Override + public Map status() { + return ThingStatus.getNameCodeMap(); + } + + @Override + public List getSequenceAttr(String entityCode, String attrs, Long startTime, Long endTime) { + Optional optional = findByCode(entityCode); + if (optional.isEmpty()) { + return null; + } + //获取时序属性 + List attrList = Lists.newArrayList(); + if (StringUtils.isNotBlank(attrs)) { + attrList.addAll(Arrays.stream(attrs.split(",")).distinct().toList()); + } + List tskvList; + // 有时间参数:查历史值 + if (!Objects.isNull(startTime) && !Objects.isNull(endTime) && CollectionUtils.isNotEmpty(attrList)) { + tskvList = tsKvService.findTsKvByCodeAndAttrs(entityCode, attrList, startTime, endTime, false); + } else { + // 没有时间参数:查最新值 + tskvList = tsKvService.findLatestByCodeAndAttrs(entityCode, attrList, false); + } + return tskvList; + } + + @Override + public List latestData(String entityCode, String attrs, Long startTime, Long endTime) { + Optional optional = findByCode(entityCode); + if (optional.isEmpty()) { + return null; + } + //获取时序属性 + List attrList = Lists.newArrayList(); + if (StringUtils.isNotBlank(attrs)) { + attrList.addAll(Arrays.stream(attrs.split(",")).distinct().toList()); + } + return tsKvService.findLatestByCodeAndAttrs(entityCode, attrList, false); + } + + + @Override + public void saveSequenceAttr(List tsKvDTOS) { + tsKvService.saveDTOTsKvAndLatest(tsKvDTOS); + tsKvService.saveDTOTsKv(tsKvDTOS); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void deleteSequenceAttr(List tsKvDTOS) { + for (TsKvDTO tsKvDTO : tsKvDTOS) { + tsKvService.deleteTskv(tsKvDTO); + tsKvService.deleteLastTskv(tsKvDTO); + } + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean save(DistributeModelParam dto) { + String code = BizUtils.trimAll(dto.getCode()); + if (StringUtils.isBlank(code)) { + throw new SysException("物模型编码不能为空"); + } + //分配属性信息 + List entitiesList = shareThingsToTenantCode(Collections.singletonList(code), dto.getTenantCodes()); + Optional optional = findByCode(code); + if (optional.isEmpty()) { + IotThingModelEntity modelEntity = buildIotThingModelEntity(code, dto.getRemark(), (long) CollectionUtils.size(entitiesList), null, dto.getGateway()); + mapper.insert(modelEntity); + } else { + updateModelAuthNumByCodes(Collections.singletonList(code)); + } + //更新物实体的缓存 + cache.clearTopic(CacheNameEnum.THING_MODEL); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + return true; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public boolean update(DistributeModelParam dto) { + String gateway = dto.getGateway(); + if (GateWayStatus.noneMatch(gateway)) { + throw new SysException("请输入正确的网关信息"); + } + ModelDetailDTO modelDTO = findById(dto.getId()); + //更新分配企业情况:如果是原先或者没有则不做分配相关工作,如果存在新增的分配情况则做分配 + List entitiesList = shareThingsToTenantCode(Collections.singletonList(modelDTO.getCode()), dto.getTenantCodes()); + IotThingModelEntity iotThingModelEntity = buildIotThingModelEntity(modelDTO.getCode(), dto.getRemark(), + (long) CollectionUtils.size(entitiesList), modelDTO.getToken(), gateway); + iotThingModelEntity.setId(modelDTO.getId()); + mapper.update(iotThingModelEntity, true); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_MODEL); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + return true; + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void distribute(DistributeModelParam dto) { + List codes = dto.getCodes(); + List tenantCodes = dto.getTenantCodes(); + if (CollectionUtil.isEmpty(codes) || CollectionUtil.isEmpty(tenantCodes)) { + throw new SysException("物编码/租户编码不能为空"); + } + List entitiesList = shareThingsToTenantCode(codes, tenantCodes); + updateModelAuthNumByCodes(entitiesList.stream().map(IotThingEntity::getCode).distinct().toList()); + //更新物属性信息:指标缓存就交给指标类处理了 + buildDictRelation(dto, entitiesList); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_MODEL); + cache.clearTopic(CacheNameEnum.THING_ENTITY); + } + + @Override + public List export(Long[] ids, Map params) { + params.put("ids", Arrays.asList(ids)); + return mapper.selectListByQueryAs(getWrapper(params), IotThingModelExcel.class); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void updateModelAuthNumByCodes(List codes) { + List entities = mapper.selectListByQuery(QueryWrapper.create() + .in(IotThingModelEntity::getCode, codes)); + entities.forEach(entity -> + { + List entityDTOS = thingEntityService.findEntityAllByCode(Collections.singleton(entity.getCode()), null, true); + entity.setAuthNum((long) CollectionUtils.size(entityDTOS)); + mapper.insertOrUpdateSelective(entity); + } + ); + cache.clearTopic(CacheNameEnum.THING_MODEL); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchSaveOrUpdate(List modelEntities) { + modelEntities.forEach(model -> mapper.insertOrUpdateSelective(model)); + cache.clearTopic(CacheNameEnum.THING_MODEL); + } + + //物模型新增和更新中分配物实体信息 + private List shareThingsToTenantCode(Collection codeList, Collection tenantCodes) { + if (CollectionUtils.isEmpty(tenantCodes)) { + return new ArrayList<>(); + } + //获取已经存在的物实体 + List entitiesList = thingEntityService.findAllByCodeInAndTenantCode(codeList, null); + //获取没有分配的企业进行分配 + List noexistList = tenantCodes.stream() + .filter(tenantCode -> + entitiesList.stream().noneMatch(entity -> Objects.equals(entity.getTenantCode(), tenantCode))) + .toList(); + if (CollectionUtils.isEmpty(noexistList)) { + return new ArrayList<>(); + } + List insertEntities = buildIotThingEntities(noexistList, codeList); + if (CollectionUtils.isNotEmpty(insertEntities)) { + thingEntityService.saveBatchEntity(insertEntities); + entitiesList.addAll(insertEntities); + } + return entitiesList; + } + + private List buildIotThingEntities(Collection noExistList, Collection codeList) { + return noExistList.parallelStream().flatMap(tenantCode -> + codeList.stream().map(code -> new IotThingEntity() + .setCode(code) + .setName(code) + .setTemplateMark(TemplateMark.NO.getValue()) + .setType(DefaultType.THING.getValue()) + .setEnableStatus(ThingEnableStatus.START.getValue()) + .setRealType(ThingRealType.REAL.getValue()) + .setTenantCode(tenantCode) + .setCompanyId(UserContext.getCompanyId()) + .setDeptIds(Objects.isNull(UserContext.getDeptId()) ? null : UserContext.getDeptId().toString()) + ) + ).collect(Collectors.toList()); + } + + + private IotThingModelEntity buildIotThingModelEntity(String code, String remark, Long authNum, String token, String gateWay) { + IotThingModelEntity iotThingModelEntity = new IotThingModelEntity(); + iotThingModelEntity.setCode(code); + iotThingModelEntity.setRemark(remark); + iotThingModelEntity.setAuthNum(authNum); + //TB已经去除,暂时自己生产Token + iotThingModelEntity.setToken(StringUtils.isBlank(token) ? TokenGenerator.generateValue() : token); + iotThingModelEntity.setStatus(ThingStatus.NOT_CONNECTED.getCode()); + iotThingModelEntity.setOrigin(QueueOriginType.MQTT_CLIENT.name()); + iotThingModelEntity.setStatusTs(DateTimeUtils.getCurrentTime()); + iotThingModelEntity.setRealType(ThingRealType.INVENTED.getValue()); + iotThingModelEntity.setGateway(gateWay); + return iotThingModelEntity; + } + + + private void buildDictRelation(DistributeModelParam dto, List entitiesList) { + List groupNames = dto.getGroupNames(); + List attrs = dto.getAttrs(); + if (CollectionUtils.isNotEmpty(entitiesList) && CollectionUtils.isNotEmpty(groupNames) && CollectionUtils.isNotEmpty(attrs)) { + IotThingDictRelationParamDTO dictRelationsDTO = new IotThingDictRelationParamDTO(); + dictRelationsDTO.setGroupNames(groupNames); + dictRelationsDTO.setDictIds(attrs); + dictRelationsDTO.setEntityIds(entitiesList.stream().map(IotThingEntity::getId).collect(Collectors.toList())); + dictRelationsService.save(dictRelationsDTO); + } + //更新字典缓存 + cache.clearTopic(CacheNameEnum.THING_DICT_RELATION); + } + + + private List> buildParam(String code, + String token, + String origin, + String status, + String realType) { + List> filterList = new ArrayList<>(); + if (StringUtils.isNotBlank(code)) { + filterList.add(Pair.of(CacheNameEnum.ModelField.THING_MODEL_CODE.getField(), code)); + } + if (StringUtils.isNotBlank(token)) { + filterList.add(Pair.of(CacheNameEnum.ModelField.THING_MODEL_TOKEN.getField(), token)); + } + if (StringUtils.isNotBlank(origin)) { + filterList.add(Pair.of(CacheNameEnum.ModelField.THING_MODEL_ORIGIN.getField(), origin)); + } + if (StringUtils.isNotBlank(status)) { + filterList.add(Pair.of(CacheNameEnum.ModelField.THING_MODEL_STATUS.getField(), status)); + } + if (StringUtils.isNotBlank(realType)) { + filterList.add(Pair.of(CacheNameEnum.ModelField.THING_MODEL_REAL_TYPE.getField(), realType)); + } + return filterList; + + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/controller/IotThingRelationDetailController.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/controller/IotThingRelationDetailController.java new file mode 100644 index 0000000..9df973d --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/controller/IotThingRelationDetailController.java @@ -0,0 +1,161 @@ +package com.thing.thing.relation.detail.controller; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.dto.RelationDetailBatchSaveDTO; +import com.thing.thing.relation.detail.param.IotThingRelationDetailParamDTO; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("v2/relation/detail") +@Tag(name="物关系详情信息表") +public class IotThingRelationDetailController { + + @Resource + private IotThingRelationDetailService service; + + @GetMapping("page") + @Operation(summary="关系列表分页") + public Result> page( + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") @RequestParam Integer page, + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") @RequestParam Integer limit, + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "rootIds", description = "根关系id") @RequestParam(required = false) String rootIds, + @Parameter(name = "name", description = "关系名称") @RequestParam(required = false) String name, + @Parameter(name = "entityName", description = "物名称或者编码") @RequestParam(required = false) String entityName, + @Parameter(name = "groupName", description = "组名称") @RequestParam(required = false) String groupName) + { + PageData pageList = service.pageList(page, limit, orderField, order, rootIds, name, entityName, groupName); + return new Result>().ok(pageList); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list( @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "rootIds", description = "根关系id") @RequestParam(required = false) String rootIds, + @Parameter(name = "name", description = "关系名称") @RequestParam(required = false) String name, + @Parameter(name = "entityName", description = "物名称或者编码") @RequestParam(required = false) String entityName, + @Parameter(name = "groupName", description = "组名称") @RequestParam(required = false) String groupName) + { + List list = service.findList(orderField,order,rootIds,name,entityName,groupName); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingRelationDetailDTO data = service.getByIdAs(id, IotThingRelationDetailDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingRelationDetailParamDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.save(dto); + return new Result<>(); + } + + @PostMapping("batch/save") + @Operation(summary="批量保存: 【限制】只能批量保存一个关系下的数据") + @LogOperation("批量保存") + public Result batchSave(@RequestBody RelationDetailBatchSaveDTO request){ + service.batchSave(request); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingRelationDetailParamDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.update(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDeleteByIds(ids); + return new Result<>(); + } + + @GetMapping("{id}/{toSort}") + @Operation(summary="拖拽修改顺序") + @Parameters({ + @Parameter(name = "id", description = "当前拖拽的关系id", required = true) , + @Parameter(name = "toId", description = "拖拽目标的关系id", required = true) , + @Parameter(name = "toSort", description = "拖拽目标位置", required = true) + }) + public Result updateSort(@PathVariable("id") Long id, @PathVariable("toSort") Long toSort){ + service.updateSort(id, toSort); + return new Result<>(); + } + + @PostMapping("tree") + @Operation(summary="查询树形结构") + public Result> nodeTree(@RequestBody List rootIds){ + List viewDTOS = service.findTreeList(rootIds); + return new Result>().ok(viewDTOS); + } + + @GetMapping("tree/{id}") + @Operation(summary="查询树形结构") + public Result> nodeTreeById(@PathVariable("id") Long id){ + List viewDTOS = service.nodeTreeById(id); + return new Result>().ok(viewDTOS); + } + + @GetMapping("childNodes/{id}") + @Operation(summary="查询还未添加的子节点(列表新增-子节点)") + public Result> findChildNodes(@PathVariable("id") Long id){ + List viewDTOS = service.findAllNotAddNodesById(id); + return new Result>().ok(viewDTOS); + } + + @PostMapping("sourceEntities") + @Operation(summary="配置中心物集合") + public Result> sourceEntities(@RequestBody IotThingRelationDetailParamDTO dto){ + List viewDTOS = service.findNodesTypeByRootId(dto); + return new Result>().ok(viewDTOS); + } + + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotThingRelationDetailService.listAs(params, IotThingRelationDetailDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "物关系详情信息表", list, IotThingRelationDetailExcel.class); + *} + */ + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/IotThingRelationDetailDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/IotThingRelationDetailDTO.java new file mode 100644 index 0000000..15f4657 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/IotThingRelationDetailDTO.java @@ -0,0 +1,58 @@ +package com.thing.thing.relation.detail.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 物关系详情信息表 +* +* @author Max +* @since 3.0 2024-03-25 +*/ +@Data +@Schema(description = "物关系详情信息表") +public class IotThingRelationDetailDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "关系id主键: 新增关系(必须值) 修改关系必须值 ") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "上级物实体id") + @JsonSerialize(using = ToStringSerializer.class) + private Long fromId; + @Schema(description = "上级物实体code") + private String fromCode; + @Schema(description = "上级节点名称") + private String fromName; + @Schema(description = "下级物实体id") + @JsonSerialize(using = ToStringSerializer.class) + private Long toId; + @Schema(description = "下级物实体code") + private String toCode; + @Schema(description = "下级物实体名称") + private String toName; + @Schema(description = "根主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long rootId; + @Schema(description = "其他信息(包含x、y、width、height、shape等)") + private String config; + @Schema(description = "备注") + private String remark; + @Schema(description = "图片信息") + private String url; + @Schema(description = "排序") + @JsonSerialize(using = ToStringSerializer.class) + private Long sort; + @Schema(description = "根设备id") + @JsonSerialize(using = ToStringSerializer.class) + private Long rootThingId; + @Schema(description = "标签") + private String tag; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/RelationDetailBatchSaveDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/RelationDetailBatchSaveDTO.java new file mode 100644 index 0000000..f3b5c22 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/RelationDetailBatchSaveDTO.java @@ -0,0 +1,33 @@ +package com.thing.thing.relation.detail.dto; + + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; + +/** + * Author: SiYang + * Date: 2023/12/27 10:59 + * Description: 批量新增关系参数 + */ +@Data +@Schema(description = "批量新增关系参数") +public class RelationDetailBatchSaveDTO { + @Schema(description = "关系根id") + private Long rootId; + + @Schema(description = "关系根的前端配置信息") + private String config; + + @Schema(description = "物关系存储详情") + private List details; + + @Data + @Schema(description = "根设备-子关系列表的组合") + public static class ThingRelationDetail { + private Long rootThingId; + + private List relationDetails; + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingRelationDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingRelationDTO.java new file mode 100644 index 0000000..736825a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingRelationDTO.java @@ -0,0 +1,68 @@ +package com.thing.thing.relation.detail.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import javax.validation.constraints.NotNull; +import java.util.List; + +/** + * @author ls. 2022-04-11 + **/ +@Data +@Schema(description = "物关系") +public class ThingRelationDTO { + + @Schema(description = "关系根主键") + @NotNull(message="关系根主键不能为空") + private Long rootId; + + @Schema(description = "顶级设备主键") + @NotNull(message="顶级设备主键不能为空") + private Long rootThingId; + + @Schema(description = "父节点id") + @NotNull(message="当前节点id不能为空") + private Long fromId; + + @Schema(description = "父节点code") + private String fromCode; + + @Schema(description = "父节点名称") + private String fromName; + + @Schema(description = "位置") + private String config; + + @Schema(description = "备注") + private String remark; + + @NotEmpty(message="子节点不能为空") + @Schema(description = "子节点列表") + private List relationList; + + @Data + @Schema(description = "子节点") + public static class RelationEntity { + + @Schema(description = "子节点id") + @NotNull(message="子节点不能为空") + private Long toId; + + @Schema(description = "子节点code") + private String toCode; + + @NotBlank(message="子节点名称不能为空") + @Schema(description = "子节点名称") + private String toName; + + @Schema(description = "其他信息(包含x、y、width、height、shape等)") +// @NotBlank(message="其他信息不能为空") + private String config; + + @Schema(description = "备注") + private String remark; + + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingTreeDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingTreeDTO.java new file mode 100644 index 0000000..9b44298 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/dto/ThingTreeDTO.java @@ -0,0 +1,113 @@ +package com.thing.thing.relation.detail.dto; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhenghh. 2022-04-08 + **/ +@EqualsAndHashCode(callSuper = false) +@Accessors(chain = true) +@Data +@Schema( name= "物关系树") +public class ThingTreeDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "id:当为根关系表记录的时候,是根关系的id;当为关系详情的时候,是to_id") + private Long id; + + @Schema(description = "上级ID:当为根关系表记录的时候,是0;当为关系详情的时候,是from_id") + private Long pid; + + //private List children = new ArrayList<>(); + + private List children = new ArrayList<>(); + + @Schema(description = "关系根ID:能够唯一确定一颗树的顶级节点id") + private Long rootId; + + @Schema(description = "关系根名称,toName有点重复或者是当前节点的实体名称") + private String name; + + @Schema(description = "关系根名称") + private String rootName; + + @Schema(description = "图片的url") + private String url; + + @Schema(description = "顶级物id") + private Long rootThingId; + + @Schema(description = "物关系主键ID:当此记录为 关系 的时候,根的id(根表中的主键)就是关系id; 当物关系节点的时候,节点id(关系详情表的主键)就是关系id") + private Long relationId; + + @Schema(description = "关系类型") + private String type; + + @Schema(description = "父节点名称(或者叫标签)") + private String fromName; + + @Schema(description = "父节点id") + private Long fromId; + + @Schema(description = "父节点设备编号") + private String fromCode; + + @Schema(description = "当前节点设备编号") + private String toCode; + + @Schema(description = "当前节点id") + private Long toId; + + @Schema(description = "当前节点设备名称") + private String toName; + + @Schema(description = "备注信息") + private String remark; + + @Schema(description = "当前节点设备类型") + private String entityType; + + @Schema(description = "物实体主键") + private Long entityId; + + @Schema(description = "实体code") + private String entityCode; + + @Schema(description = "实体名称") + private String entityName; + + @Schema(description = "组名称") + private String groupName; + + @Schema(description = "排序") + private Long sort; + + @Schema(description = "物状态") + private String status; + + @Schema(description = "启用停用状态") + private String enableStatus; + + @Schema(description = "关系配置:位置信息") + private String config; + + @Schema(description = "是否在物关系显示 0:不展示 1.展示") + private String relationShow; + + @Schema(description = "是否在物关系显示 0:不展示 1.展示") + private String relationSort; + + + +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/entity/IotThingRelationDetailEntity.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/entity/IotThingRelationDetailEntity.java new file mode 100644 index 0000000..82dc3e2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/entity/IotThingRelationDetailEntity.java @@ -0,0 +1,79 @@ +package com.thing.thing.relation.detail.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 物关系详情信息表 + * + * @author xc + * @since 3.0 2024-03-25 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_relation_detail") +public class IotThingRelationDetailEntity extends BaseInfoEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 上级物实体id + */ + private Long fromId; + /** + * 上级物实体code + */ + private String fromCode; + /** + * 上级节点名称 + */ + private String fromName; + /** + * 下级物实体id + */ + private Long toId; + /** + * 下级物实体code + */ + private String toCode; + /** + * 下级物实体名称 + */ + private String toName; + /** + * 根主键 + */ + private Long rootId; + /** + * 其他信息(包含x、y、width、height、shape等) + */ + private String config; + /** + * 备注 + */ + private String remark; + /** + * 排序 + */ + private Long sort; + /** + * 根设备id + */ + private Long rootThingId; + + /** + * 图片信息 + */ + private String url; + /** + * 图片信息 + */ + private String tag; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/mapper/IotThingRelationDetailMapper.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/mapper/IotThingRelationDetailMapper.java new file mode 100644 index 0000000..9c37f6c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/mapper/IotThingRelationDetailMapper.java @@ -0,0 +1,26 @@ +package com.thing.thing.relation.detail.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** +* 物关系详情信息表 +* +* @author xc +* @since 3.0 2024-03-25 +*/ +@Mapper +public interface IotThingRelationDetailMapper extends PowerBaseMapper { + + IotThingRelationDetailEntity getThingsRelation(@Param("relationTypeId") Long relationTypeId, @Param("thingId") Long thingId); + + Long findMaxSortByFromIdAndRootId(@Param("fromId") Long fromId, @Param("rootId") Long rootId); + + IotThingRelationDetailEntity getThingsRelations(@Param("relationTypeId") Long relationTypeId, @Param("toId") Long toId); + + List getToid(Long[] longArray); +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/param/IotThingRelationDetailParamDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/param/IotThingRelationDetailParamDTO.java new file mode 100644 index 0000000..f5091ba --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/param/IotThingRelationDetailParamDTO.java @@ -0,0 +1,69 @@ +package com.thing.thing.relation.detail.param; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** +* 物关系详情信息表 +* +* @author man +* @since 3.0 2024-03-25 +*/ +@Data +@Schema(description = "物关系详情信息表") +public class IotThingRelationDetailParamDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "关系id主键: 新增关系(必须值) 修改关系必须值 ") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @JsonSerialize(using = ToStringSerializer.class) + private Long pid; + @Schema(description = "上级物实体id") + @JsonSerialize(using = ToStringSerializer.class) + private Long fromId; + @Schema(description = "上级物实体code") + private String fromCode; + @Schema(description = "上级节点名称") + private String fromName; + @Schema(description = "下级物实体id") + @JsonSerialize(using = ToStringSerializer.class) + private Long toId; + @Schema(description = "下级物实体code") + private String toCode; + @Schema(description = "下级物实体名称") + private String toName; + @Schema(description = "根主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long rootId; + @Schema(description = "其他信息(包含x、y、width、height、shape等)") + private String config; + @Schema(description = "备注") + private String remark; + @Schema(description = "图片信息") + private String url; + @Schema(description = "排序") + private Long sort; + @Schema(description = "根设备id") + @JsonSerialize(using = ToStringSerializer.class) + private Long rootThingId; + @Schema(description = "标签") + private String tag; + + @NotEmpty(message="子节点不能为空") + @Schema(description = "子节点列表:新增关系(必须值) 修改关系必须值 ") + private List relationList; + @Schema(description = "物类型集合") + private List entityTypes; + + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/service/IotThingRelationDetailService.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/service/IotThingRelationDetailService.java new file mode 100644 index 0000000..b4ada2c --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/service/IotThingRelationDetailService.java @@ -0,0 +1,76 @@ +package com.thing.thing.relation.detail.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.dto.RelationDetailBatchSaveDTO; +import com.thing.thing.relation.detail.dto.ThingRelationDTO; +import com.thing.thing.relation.detail.dto.ThingTreeDTO; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import com.thing.thing.relation.detail.param.IotThingRelationDetailParamDTO; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public interface IotThingRelationDetailService extends IBaseService { + + PageData pageList(Integer page, Integer limit,String orderField,String order,String rootIds,String name,String entityName,String groupName); + + List findList(String orderField,String order,String rootIds,String name,String entityName,String groupName); + + IotThingRelationDetailEntity findByRootIdAndRootThingId(Long rootId,Long rootThingId); + + List findByRootId(Long rootId); + + Long findByMaxSort(Long rootId); + + List findByRootIds(Collection rootId); + + void save(IotThingRelationDetailParamDTO dto); + + void batchSave(RelationDetailBatchSaveDTO request); + + void update(IotThingRelationDetailParamDTO dto); + + void deleteByRootId(Long rootId); + + void deleteById(Long id); + + void updateSort(Long id, Long toSort); + + void batchDeleteByIds(Long[] ids); + + List findAllNodesByRootIdAndToId(Long rootId,Long toId); + + List findRelationAllByFromIds(Collection fromIds); + + List findAllNotAddNodesById(Long id); + + List findNodesTypeByRootId(IotThingRelationDetailParamDTO dto); + + List findTreeList(List rootIds); + + List nodeTreeById(Long id); + + List findRootDetailSortTreeList(Long rootId, Long rootThingId); + /** 超级API物关系检索*/ + List findEntityByCondition(List thingRelation); + + List findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId); + + List findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(Long rootId, Long fromId, Long rootThingId); + + IotThingRelationDetailDTO findRootDetailParentNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId); + + IotThingRelationDetailEntity getThingsRelation(Long relationTypeId, Long thingId); + + IotThingRelationDetailEntity getThingsRelations(Long relationTypeId, Long toId); + + void saveRelationNodes(ThingRelationDTO relationDTO); + +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/detail/service/impl/IotThingRelationDetailServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/relation/detail/service/impl/IotThingRelationDetailServiceImpl.java new file mode 100644 index 0000000..c8de5f2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/detail/service/impl/IotThingRelationDetailServiceImpl.java @@ -0,0 +1,927 @@ +package com.thing.thing.relation.detail.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.comparator.CompareUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.CaseFormat; +import com.google.common.collect.Lists; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.ContainsCondition; +import com.thing.common.core.enumeration.DatasetThingRelationStatus; +import com.thing.common.core.enumeration.TreeNodeStatus; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.*; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.event.ThingRelationDetailSaveEvent; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.api.dto.ApiEntityRelationDTO; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.entity.dto.IotThingEntityDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.dto.RelationDetailBatchSaveDTO; +import com.thing.thing.relation.detail.dto.ThingRelationDTO; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import com.thing.thing.relation.detail.mapper.IotThingRelationDetailMapper; +import com.thing.thing.relation.detail.param.IotThingRelationDetailParamDTO; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; +import com.thing.thing.relation.root.entity.IotThingRelationRootEntity; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import com.thing.transport.api.adaptor.JsonConverter; +import jakarta.annotation.Resource; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.mybatisflex.core.query.QueryMethods.max; +import static com.thing.thing.relation.detail.entity.table.IotThingRelationDetailEntityTableDef.IOT_THING_RELATION_DETAIL_ENTITY; +import static java.util.Comparator.comparing; + + +@Service +public class IotThingRelationDetailServiceImpl extends BaseServiceImpl implements IotThingRelationDetailService { + + @Resource + private IotThingEntityService thingEntitiesService; + + @Autowired + private ApplicationEventPublisher eventPublisher; + + @Resource + private IotThingRelationRootService relationRootsService; + + @Resource + private ThingCache cache; + + @Override + public QueryWrapper getWrapper(Map params) { + return new QueryWrapper(); + } + + public QueryWrapper getWrapper(String orderField, String order, String entityName, List rootIdList) { + QueryWrapper wrapper = new QueryWrapper(); + wrapper.where(IOT_THING_RELATION_DETAIL_ENTITY.TO_CODE.like(entityName) + .or(IOT_THING_RELATION_DETAIL_ENTITY.TO_NAME.like(entityName)) + .or(IOT_THING_RELATION_DETAIL_ENTITY.FROM_CODE.like(entityName)) + .or(IOT_THING_RELATION_DETAIL_ENTITY.FROM_NAME.like(entityName)) + ); + if (StringUtils.isNotBlank(orderField)) { + wrapper.orderBy(orderField, StringUtils.equalsIgnoreCase(order, Constant.DESC)); + } else { + wrapper.orderBy(IOT_THING_RELATION_DETAIL_ENTITY.SORT, StringUtils.equalsIgnoreCase(order, Constant.DESC)); + } + wrapper.in(IotThingRelationDetailEntity::getRootId, rootIdList, CollectionUtils.isNotEmpty(rootIdList)); + return wrapper; + } + + @Override + public PageData pageList(Integer page, Integer limit, String orderField, String order, String rootIds, String name, String entityName, String groupName) { + //查询根关系列表, + List detailList = findList(orderField, order, rootIds, name, entityName, groupName); + if (CollectionUtils.isEmpty(detailList)) { + return new PageData<>(new ArrayList<>(), 0); + } + //root的所有信息 + List rootList = relationRootsService.findList(orderField, order, rootIds, name, groupName, UserContext.getRealTenantCode()); + List relationDetailList = new ArrayList<>(detailList); + recursionChildrenAllChild(relationDetailList,rootList); + relationDetailList.removeIf(s-> { + boolean has = s.has("flag"); + if(has){ + s.remove("flag"); + } + return has; + }); + //分页 + List resPageList = PageUtils.startPage(relationDetailList, page, limit); + /* //找到第一节点 :先保留这个注释代码,可能会用到 + List resList = detailList.stream() + .filter(d -> StringUtils.equals( + d.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asText(), + d.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_ID.getField()).asText())) + .sorted(Comparator.comparingLong(node -> node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField()).asLong())) + .toList(); + //分页第一节点 + List resPageList = PageUtils.startPage(resList, page, limit); + //root的所有信息 + List list = relationRootsService.findList(orderField, order, rootIds, name, groupName, UserContext.getRealTenantCode()); + //递归子集合 + recursionChildren(resPageList, detailList, list);*/ + return new PageData<>(resPageList, CollectionUtils.size(relationDetailList)); + } + + + @Override + public List findList(String orderField, String order, String rootIds, String name, String entityName, String groupName) { + List rootIdList = Lists.newArrayList(); + if (StringUtils.isNotBlank(rootIds)) { + rootIdList = Arrays.stream(rootIds.split(",")).map(Long::parseLong).toList(); + } + //查询关系详情列表 + List relationDetaillist = cache.getTopicMap(CacheNameEnum.THING_DETAIL_RELATION); + if (CollectionUtils.isEmpty(relationDetaillist)) { + List list = mapper.selectListByQueryAs(getWrapper(orderField, order, null, null), IotThingRelationDetailDTO.class); + relationDetaillist = JsonConverter.convertToJsonObjectListObjectNode(list); + if (CollectionUtils.isEmpty(relationDetaillist)) { + return new ArrayList<>(); + } + //更新缓存 + CacheInit.relationDetailMap(relationDetaillist, cache); + } + //默认用sort排序 + if (StringUtils.isBlank(orderField)) { + orderField = CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField(); + } + String finalOrderField = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, orderField); + Comparator comparator = CompareUtils.getComparator(order, finalOrderField); //封装参数 + List finalRootIdList = rootIdList; + return relationDetaillist.stream() + .filter(jsonObject -> { + if (CollectionUtils.isNotEmpty(finalRootIdList)) { + long rootId = jsonObject.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asLong(); + return finalRootIdList.contains(rootId); + } + return true; + }) + .filter(jsonObject -> JacksonUtil.filterOr(jsonObject, entityName, + CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_CODE.getField(), + CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_NAME.getField(), + CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_NAME.getField(), + CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_CODE.getField() + )).sorted(comparator.thenComparing(obj -> obj.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ID.getField()).asLong())).toList(); + } + + @Override + public IotThingRelationDetailEntity findByRootIdAndRootThingId(Long rootId, Long rootThingId) { + return mapper.selectOneByQuery(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getRootThingId, rootThingId) + .eq(IotThingRelationDetailEntity::getToId, rootThingId) + .eq(IotThingRelationDetailEntity::getFromId, rootId)); + } + + @Override + public List findByRootId(Long rootId) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId), IotThingRelationDetailDTO.class); + } + + @Override + public Long findByMaxSort(Long rootId) { + QueryWrapper queryWrapper = QueryWrapper.create().select(max(IOT_THING_RELATION_DETAIL_ENTITY.SORT)) + .eq(IotThingRelationDetailEntity::getRootId, rootId); + return mapper.selectOneByQueryAs(queryWrapper, Long.class); + } + + @Override + public List findByRootIds(Collection rootId) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingRelationDetailEntity::getRootId, rootId), IotThingRelationDetailDTO.class); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(IotThingRelationDetailParamDTO dto) { + List subRelationList = dto.getRelationList(); + if (CollectionUtils.isEmpty(subRelationList)) { + throw new SysException("子节点不存在"); + } + boolean isMatchMyself = subRelationList.stream().anyMatch(item -> Objects.equals(item.getToId(), dto.getFromId())); + if (isMatchMyself) { + throw new SysException("当前物实体不能添加为子节点"); + } + //查询当前节点下所有的子节点集合 + List relationDetailEntities = mapper.selectListByQuery( + QueryWrapper.create().eq(IotThingRelationDetailEntity::getRootId, dto.getRootId()).orderBy(IotThingRelationDetailEntity::getSort, true) + ); + //将已经添加的物关系移除 + if (CollectionUtil.isNotEmpty(relationDetailEntities)) { + subRelationList.removeIf(sub -> relationDetailEntities.stream() + .anyMatch(r -> ObjectUtil.equals(sub.getToId(), r.getToId()) + && ObjectUtil.equals(dto.getRootId(), r.getRootId()) + && ObjectUtil.equals(dto.getFromId(), r.getFromId()) + && ObjectUtil.equals(dto.getRootThingId(), r.getRootThingId())) + ); + } + if (CollectionUtil.isEmpty(subRelationList)) { + throw new SysException("节点关系已经存在,无需重复添加"); + } + List resList = new ArrayList<>(); + //获取当前节点 + IotThingRelationDetailEntity currentNode = relationDetailEntities.stream().filter(item -> Objects.equals(item.getId(), dto.getId())) + .findFirst().orElseThrow(() -> new SysException("当前节点不存在")); + List childNodes = relationDetailEntities.stream().filter(item -> Objects.equals(item.getFromId(), currentNode.getToId())).toList(); + if (CollectionUtils.isNotEmpty(childNodes)) { + IotThingRelationDetailEntity maxEntity = childNodes.stream() + .max(Comparator.comparing(IotThingRelationDetailEntity::getSort)).orElseThrow(() -> new SysException("最大排序节点不存在")); + resList.addAll(relationDetailEntities.stream().filter(item -> item.getSort() <= maxEntity.getSort()).toList()); + List iotThingRelationDetailEntities = buildDetails(dto, subRelationList); + resList.addAll(iotThingRelationDetailEntities); + resList.addAll(relationDetailEntities.stream().filter(item -> item.getSort() > maxEntity.getSort()).toList()); + } else { + resList.addAll(relationDetailEntities.stream().filter(item -> item.getSort() <= currentNode.getSort()).toList()); + List iotThingRelationDetailEntities = buildDetails(dto, subRelationList); + resList.addAll(iotThingRelationDetailEntities); + resList.addAll(relationDetailEntities.stream().filter(item -> item.getSort() > currentNode.getSort()).toList()); + } + AtomicLong sort = new AtomicLong(0); + resList.forEach(item -> { + item.setSort(sort.incrementAndGet()); + if (Objects.isNull(item.getId())) { + mapper.insert(item); + } else { + mapper.updateByQuery(item, QueryWrapper.create().eq(IotThingRelationDetailEntity::getId, item.getId())); + } + }); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + private List buildDetails(IotThingRelationDetailParamDTO dto, List subRelationList) { + return subRelationList.stream().map(item -> { + //iotThingRelationDetailEntity.setId(IdUtil.getSnowflake().nextId()); + return new IotThingRelationDetailEntity() + .setFromId(dto.getFromId()) + .setFromName(dto.getFromName()) + .setFromCode(dto.getFromCode()) + .setToId(item.getToId()) + .setToCode(item.getToCode()) + .setToName(item.getToName()) + .setRootId(dto.getRootId()) + .setConfig(item.getConfig()) + .setRemark(item.getRemark()) + // .setSort(sort.incrementAndGet()) + .setTag(StringUtils.isBlank(item.getTag()) ? item.getToName() : item.getTag()) + .setRootThingId(dto.getRootThingId()); + }).toList(); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchSave(RelationDetailBatchSaveDTO request) { + List detailList = request.getDetails(); + if (CollectionUtils.isEmpty(detailList)) { + return; + } + // 修改关系配置 + IotThingRelationRootEntity root = new IotThingRelationRootEntity(); + root.setId(request.getRootId()); + root.setConfig(request.getConfig()); + relationRootsService.updateById(root); + + //查询当前rootId下的所有关系列表 + List iotThingRelationDetailEntities = mapper.selectListByQuery(QueryWrapper.create().eq(IotThingRelationDetailEntity::getRootId, request.getRootId())); + if (CollectionUtils.isNotEmpty(iotThingRelationDetailEntities)) { + //删除全部关系 + mapper.deleteByQuery(QueryWrapper.create().eq(IotThingRelationDetailEntity::getRootId, request.getRootId())); + } + //重构关系入库 + List list = detailList.stream().flatMap(item -> { + Long rootThingId = item.getRootThingId(); + List relationDetails = item.getRelationDetails(); + return relationDetails.stream().map(detail -> new IotThingRelationDetailEntity() + .setRootThingId(rootThingId) + .setFromId(detail.getFromId()) + .setFromName(detail.getFromName()) + .setFromCode(detail.getFromCode()) + .setToId(detail.getToId()) + .setToCode(detail.getToCode()) + .setToName(detail.getToName()) + .setRootId(request.getRootId()) + .setSort(detail.getSort()) + .setConfig(detail.getConfig()) + .setUrl(detail.getUrl()) + .setRemark(detail.getRemark())); + }).toList(); + mapper.insertBatch(list); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void update(IotThingRelationDetailParamDTO dto) { + Long id = dto.getId(); + IotThingRelationDetailEntity sourceEntity = mapper.selectOneById(id); + if (Objects.isNull(sourceEntity)) { + throw new SysException("当前移动节点关系不存在"); + } + IotThingRelationDetailEntity targetEntity = mapper.selectOneById(dto.getPid()); + if (Objects.isNull(targetEntity)) { + throw new SysException("当前目标节点关系不存在"); + } + if(!Objects.equals(sourceEntity.getRootThingId(), targetEntity.getRootThingId())){ + throw new SysException("当前移动节点和目标节点不在同一个根节点下,无法移动"); + } + //查询当前关系下所有节点信息,但是不包括当前节点 + QueryWrapper queryWrapper = QueryWrapper.create().eq(IotThingRelationDetailEntity::getRootId, dto.getRootId()); + //这里肯定有数据 + List iotThingRelationDetailEntities = mapper.selectListByQuery(queryWrapper); + if (CollectionUtils.isEmpty(iotThingRelationDetailEntities)) { + throw new SysException("当前根关系下节点信息列表异常"); + } + //找出当前移动节点的 所有子节点,即所有需要移动的节点集合 + List sourceChildList = Lists.newArrayList(); + findAllChild(iotThingRelationDetailEntities, sourceEntity, sourceChildList); + sourceChildList.add(sourceEntity); + + //获取前端的参数中 父节点的所有子节点的最大顺序的节点 + Optional optionalMax = iotThingRelationDetailEntities.stream() + .filter(item -> Objects.equals(item.getFromId(), targetEntity.getToId()) && Objects.equals(item.getRootThingId(), targetEntity.getRootThingId())) + .max(comparing(IotThingRelationDetailEntity::getSort)); + if(optionalMax.isEmpty()){ + //若没有子节点,那就自己为节点 + optionalMax = iotThingRelationDetailEntities.stream().filter(item -> + Objects.equals(item.getId(), dto.getPid()) + ).findFirst(); + } + //父节点上面的加入到list中 + Optional finalOptionalMax = optionalMax; + //找出目标节点上面的所有节点,但是不包含 移动节点和子节点 + List resList = new ArrayList<>(iotThingRelationDetailEntities.stream() + .filter(item -> item.getSort() <= finalOptionalMax.get().getSort() + && !sourceChildList.stream().map(IotThingRelationDetailEntity::getId).toList().contains(item.getId()) + ).sorted(Comparator.comparing(IotThingRelationDetailEntity::getSort)).toList()); + + sourceChildList.stream().sorted(Comparator.comparing(IotThingRelationDetailEntity::getSort)).forEach(item -> { + if(Objects.equals(item.getId(), sourceEntity.getId())){ + item.setFromId(targetEntity.getFromId()) + .setFromName(targetEntity.getFromName()) + .setFromCode(targetEntity.getFromCode()) + .setRootId(dto.getRootId()) + .setRootThingId(targetEntity.getRootThingId()) + .setToName(sourceEntity.getToName()) + .setTag(dto.getTag()); + }else{ + item.setRootThingId(targetEntity.getRootThingId()); + } + resList.add(item); + }); + //剩余的节点添加到list中 + iotThingRelationDetailEntities.stream() + .filter(item -> item.getSort() > finalOptionalMax.get().getSort() && !sourceChildList.stream().map(IotThingRelationDetailEntity::getId).toList().contains(item.getId())) + .sorted(Comparator.comparing(IotThingRelationDetailEntity::getSort)) + .forEach(resList::add); + //更新排序 + AtomicLong sort = new AtomicLong(0); + resList.forEach(item -> + { + item.setSort(sort.incrementAndGet()); + mapper.updateByQuery(item, QueryWrapper.create().eq(IotThingRelationDetailEntity::getId, item.getId())); + } + ); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + private void findAllChild(List allRelation, + IotThingRelationDetailEntity currentNode, + List list){ + List childList = allRelation.stream() + .filter(item -> Objects.equals(item.getFromId(), currentNode.getToId()) && Objects.equals(item.getRootThingId(), currentNode.getRootThingId())) + .toList(); + if(CollectionUtils.isNotEmpty(childList)){ + list.addAll(childList); + childList.forEach(item -> findAllChild(allRelation,item,list)); + } + } + + @Override + public void deleteByRootId(Long rootId) { + mapper.deleteByQuery(QueryWrapper.create().eq(IotThingRelationDetailEntity::getRootId, rootId)); + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Override + public void deleteById(Long id) { + mapper.deleteByQuery(QueryWrapper.create().eq(IotThingRelationDetailEntity::getId, id)); + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Override + public void updateSort(Long id, Long toSort) { + //拖拽关系 + IotThingRelationDetailEntity sourceEntity = mapper.selectOneById(id); + if (Objects.isNull(sourceEntity)) { + throw new SysException("当前拖拽的关系不存在,请重新选择"); + } + //查询所有同级别的关系记录 + List sourceList = mapper.selectListByQuery(new QueryWrapper() + .eq(IotThingRelationDetailEntity::getRootId, sourceEntity.getRootId()) + .eq(IotThingRelationDetailEntity::getRootThingId, sourceEntity.getRootThingId()) + .eq(IotThingRelationDetailEntity::getFromId, sourceEntity.getFromId()) + .ne(IotThingRelationDetailEntity::getId, sourceEntity.getId()) + .orderBy(IotThingRelationDetailEntity::getSort, true)); + List resultList = Lists.newArrayList(); + //向上:这里的条件判断是利用list的元素顺序重新给整个节点赋sort值 + if (sourceEntity.getSort() > toSort) { + List upList = sourceList.stream().filter(item -> item.getSort() < toSort).toList(); + resultList.addAll(upList); + sourceEntity.setSort(toSort); + resultList.add(sourceEntity); + List downList = sourceList.stream().filter(item -> item.getSort() >= toSort).toList(); + resultList.addAll(downList); + } else { + List upList = sourceList.stream().filter(item -> item.getSort() <= toSort).toList(); + resultList.addAll(upList); + sourceEntity.setSort(toSort); + resultList.add(sourceEntity); + List downList = sourceList.stream().filter(item -> item.getSort() > toSort).toList(); + resultList.addAll(downList); + } + AtomicLong sort = new AtomicLong(1); + resultList.forEach(item -> item.setSort(sort.getAndIncrement())); + this.updateBatch(resultList); + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Override + public void batchDeleteByIds(Long[] ids) { + //当前节点 + List iotThingRelationDetailEntities = mapper.selectListByQuery(QueryWrapper.create() + .in(IotThingRelationDetailEntity::getId, Arrays.asList(ids))); + if (CollectionUtils.isEmpty(iotThingRelationDetailEntities)) { + throw new SysException("当前节点不存在,请重新选择"); + } + List rootIds = iotThingRelationDetailEntities.stream().map(IotThingRelationDetailEntity::getRootId).distinct().toList(); + if (CollectionUtils.size(rootIds) > 1) { + throw new SysException("不支持多个关系下的节点删除"); + } + QueryWrapper queryWrapper = QueryWrapper.create() + .notIn(IotThingRelationDetailEntity::getId, Arrays.asList(ids)) + .in(IotThingRelationDetailEntity::getRootId, rootIds) + .orderBy(IotThingRelationDetailEntity::getSort, true); + List otherRelationList = mapper.selectListByQuery(queryWrapper); + mapper.deleteBatchByIds(Arrays.asList(ids)); + //更新序号 + AtomicLong sort = new AtomicLong(1); + otherRelationList.forEach(s -> { + s.setSort(sort.getAndIncrement()); + mapper.insertOrUpdateSelective(s); + }); + //更新缓存 + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Override + public List findAllNodesByRootIdAndToId(Long rootId, Long toId) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getToId, toId, Objects.nonNull(toId)) + , IotThingRelationDetailDTO.class); + } + + @Override + public List findRelationAllByFromIds(Collection fromIds) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .in(IotThingRelationDetailEntity::getFromId, fromIds) + , IotThingRelationDetailDTO.class); + } + + @Override + public List findAllNotAddNodesById(Long id) { + List relationList = cache.findKeyMap(CacheNameEnum.THING_DETAIL_RELATION, id.toString()); + if (CollectionUtils.isEmpty(relationList)) { + throw new SysException("当前物关系不存在"); + } + List entityDTOList = thingEntitiesService.findEntityAllByCode(null, UserContext.getRealTenantCode(), true); + if (CollectionUtils.isEmpty(entityDTOList)) { + return entityDTOList; + } + Long rootId = relationList.get(0).get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asLong(); + Long rootThingId = relationList.get(0).get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asLong(); + List iotThingRelationDetailDTOS = findAllNodesByRootIdAndToId(rootId, null); + if (CollectionUtils.isNotEmpty(iotThingRelationDetailDTOS)) { + List entityIds = iotThingRelationDetailDTOS.stream() + .filter(s -> Objects.equals(s.getRootThingId(), rootThingId)) + .flatMap(item -> Stream.of(item.getFromId(), item.getToId())).distinct().toList(); + entityDTOList.removeIf(item -> entityIds.contains(item.getId())); + } + return entityDTOList; + } + + @Override + public List findNodesTypeByRootId(IotThingRelationDetailParamDTO dto) { + List detailDTOList = findByRootId(dto.getRootId()); + if (CollectionUtils.isEmpty(detailDTOList)) { + return Lists.newArrayList(); + } + List ids = detailDTOList.stream().map(IotThingRelationDetailDTO::getToId).toList(); + List entityDTOList = thingEntitiesService.findEntityByIds(ids); + if (CollectionUtils.isNotEmpty(dto.getEntityTypes())) { + entityDTOList = entityDTOList.stream().filter(e -> CollectionUtils.containsAny(dto.getEntityTypes(), e.getType())).toList(); + } + if (CollectionUtils.isNotEmpty(entityDTOList)) { + entityDTOList = entityDTOList.stream() + .collect(Collectors.toMap(IotThingEntityDTO::getId, Function.identity(), (existing, replacement) -> existing)) + .values() + .stream() + .toList(); + } + return entityDTOList; + } + + @Override + public List findTreeList(List rootIds) { + //关系节点 + List rootList = cache.findAllKeyMap(CacheNameEnum.THING_ROOT_RELATION, rootIds.stream().map(String::valueOf).toList()); + if (CollectionUtils.isEmpty(rootList)) { + return Lists.newArrayList(); + } + List detailList = cache.findAllKeyMap(CacheNameEnum.THING_DETAIL_RELATION, rootIds.stream().map(String::valueOf).toList()); + + if (CollectionUtils.isEmpty(detailList)) { + return rootList; + } + List firstList = detailList.stream() + .filter(d -> StringUtils.equals( + d.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asText(), + d.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_ID.getField()).asText())) + .sorted(Comparator.comparingLong(node -> node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField()).asLong())) + .toList(); + recursionChildren(firstList, detailList, rootList); + //顶级节点添加 + rootList.forEach(node -> { + List childList = firstList.stream().filter(d -> + StringUtils.equals(d.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_ID.getField()).asText(), + node.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asText())).toList(); + if (CollectionUtils.isNotEmpty(childList)) { + node.put(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField(), JacksonUtil.convertValue(childList, ArrayNode.class)); + } + }); + return rootList; + } + + @Override + public List nodeTreeById(Long id) { + List treeList = findTreeList(Collections.singletonList(id)); + if (CollectionUtils.isEmpty(treeList)) { + return treeList; + } + return treeList.stream() + .filter(item -> item.has(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()) && + item.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()) != null) + .flatMap(node -> { + JsonNode jsonNode = node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()); + List queryList = jsonNode == null || jsonNode.isEmpty() ? Lists.newArrayList() : JacksonUtil.convertValue(jsonNode, new TypeReference<>() { + }); + return queryList.stream(); + }).toList(); + + //当前物关系的顶级节点删除 +// return treeList.stream() +// .filter(item -> item.has(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()) && +// item.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()) != null) +// .map(item -> { +// String text = item.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()).asText(); +// return JacksonUtil.convertValue(text, ObjectNode.class); +// } +// ).toList(); + } + + @Override + public List findRootDetailSortTreeList(Long rootId, Long rootThingId) { + List res = new ArrayList<>(); + QueryWrapper eq = QueryWrapper.create().select( + IOT_THING_RELATION_DETAIL_ENTITY.TO_ID.as("id"), + IOT_THING_RELATION_DETAIL_ENTITY.FROM_ID.as("pid"), + IOT_THING_RELATION_DETAIL_ENTITY.TO_ID.as("entity_id"), + IOT_THING_RELATION_DETAIL_ENTITY.TO_CODE.as("entity_code"), + IOT_THING_RELATION_DETAIL_ENTITY.TO_NAME.as("entity_name"), + IOT_THING_RELATION_DETAIL_ENTITY.ROOT_THING_ID, + IOT_THING_RELATION_DETAIL_ENTITY.SORT + ).eq(IotThingRelationDetailEntity::getRootId, rootId); + + List thingSortTreeDTOS = mapper.selectListByQueryAs(eq, ThingSortTreeDTO.class); + + Map> rootThingTreeGroup = + thingSortTreeDTOS.stream().collect(Collectors.groupingBy(ThingSortTreeDTO::getRootThingId)); + rootThingTreeGroup.forEach( + (thingId, treeList) -> { + if (Objects.nonNull(rootThingId) && !Objects.equals(rootThingId, thingId)) { + return; + } + List rootThingTree = TreeUtils.buildSortTree(treeList, rootId); + res.addAll(rootThingTree); + }); + return res; + } + + @Override + public List findEntityByCondition(List thingRelation) { + List result = Lists.newArrayList(); + for (ApiEntityRelationDTO dto : thingRelation) { + if (Objects.isNull(dto.getEntity())) { + continue; + } + //检索物关系的树级结构数据 + IotThingRelationDetailEntity relationDetail = mapper.selectOneById(dto.getEntity()); + List relationDetailDTOS = findByRootId(relationDetail.getRootId()); + if (CollectionUtil.isEmpty(relationDetailDTOS)) { + continue; + } + if (StringUtils.isBlank(dto.getLevel()) || CompareUtil.compare(TreeNodeStatus.ALL.getValue(), dto.getLevel()) == 0) { + result.addAll(relationDetailDTOS); + } else { + Optional currentNode = relationDetailDTOS.stream().filter(r -> Objects.equals(r.getId(), dto.getEntity())).findFirst(); + if (currentNode.isEmpty()) { + throw new SysException("关系点不存在"); + } + //获取层级上或者下的物实体集合 + recurrenceNodes(relationDetailDTOS, currentNode.get(), Integer.parseInt(dto.getLevel()) + 1, dto.getTo(), result); + if (CompareUtil.compare(ContainsCondition.REMOVE.getValue(), dto.getContainCurrent()) != 0) { + result = result.stream().filter(r -> !Objects.equals(r.getId(), dto.getEntity())).collect(Collectors.toList()); + } + } + } + return result; + } + + @Override + public List findRootDetailSiblingNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId) { + IotThingRelationDetailEntity iotThingRelationDetailEntity = mapper.selectOneByQuery(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getToId, toId) + .eq(IotThingRelationDetailEntity::getRootThingId, rootThingId, ObjectUtil.isNotNull(rootThingId)) + ); + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getFromId, iotThingRelationDetailEntity.getFromId()) + .eq(IotThingRelationDetailEntity::getRootThingId, rootThingId, ObjectUtil.isNotNull(rootThingId)) + , IotThingRelationDetailDTO.class + ); + } + + @Override + public List findRootDetailChildNodeByRootIdAndFromIdAndRootThingId(Long rootId, Long fromId, Long rootThingId) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getFromId, fromId) + .eq(IotThingRelationDetailEntity::getRootThingId, rootThingId, ObjectUtil.isNotNull(rootThingId)) + , IotThingRelationDetailDTO.class); + } + + @Override + public IotThingRelationDetailDTO findRootDetailParentNodeByRootIdAndToIdAndRootThingId(Long rootId, Long toId, Long rootThingId) { + return mapper.selectOneByQueryAs(QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getToId, toId) + .eq(IotThingRelationDetailEntity::getRootThingId, rootThingId, ObjectUtil.isNotNull(rootThingId)), + IotThingRelationDetailDTO.class + ); + } + + @Override + public IotThingRelationDetailEntity getThingsRelation(Long relationTypeId, Long thingId) { + return mapper.getThingsRelation(relationTypeId, thingId); + } + + @Override + public IotThingRelationDetailEntity getThingsRelations(Long relationTypeId, Long toId) { + return mapper.getThingsRelations(relationTypeId, toId); + } + + @Override + public void saveRelationNodes(ThingRelationDTO dto) { + //子节点集合 + List subRelationList = new ArrayList<>(dto.getRelationList()); + Long rootId = dto.getRootId(); + //校验节点不能添加自己 + if (subRelationList.stream().anyMatch(s -> ObjectUtil.equals(s.getToId(), dto.getFromId()))) { + throw new SysException("当前物实体是本身,不能添加为子节点"); + } + //查询当前节点下已经关联的子节点集合 + List relationDetailEntities = mapper.selectListExt( + QueryWrapper.create() + .eq(IotThingRelationDetailEntity::getRootId, rootId) + .eq(IotThingRelationDetailEntity::getFromId, dto.getFromId()) + ); + //将已经添加的物关系移除 + if (CollectionUtil.isNotEmpty(relationDetailEntities)) { + subRelationList.removeIf(sub -> relationDetailEntities.stream().anyMatch(r -> ObjectUtil.equals(sub.getToId(), r.getToId()) + && ObjectUtil.equals(dto.getRootId(), r.getRootId()) + && ObjectUtil.equals(dto.getFromId(), r.getFromId()))); + } + if (CollectionUtil.isEmpty(subRelationList)) { + throw new SysException("节点关系已经存在,无需重复添加"); + } + //获取子节点下的最大排序号 + Long maxSort = mapper.findMaxSortByFromIdAndRootId(dto.getFromId(), dto.getRootId()); + AtomicLong sort = new AtomicLong(ObjectUtil.isNull(maxSort) ? 1L : maxSort); + List insertList = subRelationList.stream().map(item -> { + IotThingRelationDetailEntity entity = new IotThingRelationDetailEntity(); + entity.setFromId(dto.getFromId()); + entity.setRootId(dto.getRootId()); + entity.setRootThingId(dto.getRootThingId()); + //为什么这里不去查询数据库:原因是为了更加灵活处理页面的节点名字和code + entity.setFromCode(BizUtils.trimAll(dto.getFromCode())); + entity.setFromName(BizUtils.trimAll(dto.getFromName())); + entity.setToId(item.getToId()); + entity.setToCode(BizUtils.trimAll(item.getToCode())); + entity.setToName(BizUtils.trimAll(item.getToName())); + entity.setConfig(BizUtils.trimAll(item.getConfig())); + entity.setSort(sort.getAndIncrement()); + entity.setRemark(dto.getRemark()); + return entity; + }).collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(insertList)) { + mapper.insertBatch(insertList); + + List relationList = dto.getRelationList(); + List thingIds = relationList.stream().map(ThingRelationDTO.RelationEntity::getToId).collect(Collectors.toList()); + eventPublisher.publishEvent( + new ThingRelationDetailSaveEvent( + this, + dto.getRootId(), + thingIds, + UserContext.getTenantCode())); + } + } + + /** + * 获取某个节点上面的按层级划分的父节点或者所有子节点 + * + * @param relationDetailDTOS 所有物关系 + * @param currentNode 节点对象 + * @param layers 层级 + */ + private void recurrenceNodes(List relationDetailDTOS, + IotThingRelationDetailDTO currentNode, + int layers, + String upOrDown, + List result) { + if (layers > 0) { + //若不包含这里就注释了 + result.add(currentNode); + List parentOrChildNodes; + if (StringUtils.endsWithIgnoreCase(DatasetThingRelationStatus.PD.name(), upOrDown)) { + //找出父节点 + parentOrChildNodes = relationDetailDTOS.stream().filter(r -> + Objects.equals(currentNode.getToId(), r.getFromId()) +// && Objects.equals(currentNode.getRootThingId(), r.getRootThingId()) + && Objects.equals(currentNode.getRootId(), r.getRootId())).toList(); + } else { + //找出子节点 + parentOrChildNodes = relationDetailDTOS.stream().filter(r -> + Objects.equals(currentNode.getFromId(), r.getToId()) +// && Objects.equals(currentNode.getRootThingId(), r.getRootThingId()) + && Objects.equals(currentNode.getRootId(), r.getRootId()) + ).toList(); + } + if (CollectionUtils.isEmpty(parentOrChildNodes)) { + return; + } +// if (StringUtils.endsWithIgnoreCase(DatasetThingRelationStatus.PU.name(),upOrDown)) { +// +// } + //若当前父节点已经是顶级了,则直接添加result中 + List topList = parentOrChildNodes.stream().filter(r -> Objects.equals(r.getRootId(), r.getFromId())).toList(); + if (CollectionUtils.isNotEmpty(topList)) { + result.addAll(topList); + } + List list; + if (StringUtils.endsWithIgnoreCase(DatasetThingRelationStatus.PD.name(), upOrDown)) { + //若子集或者父集不是顶级物关系,继续处理 + list = parentOrChildNodes.stream().filter(r -> + Objects.equals(currentNode.getRootThingId(), r.getRootThingId()) + && !Objects.equals(r.getRootId(), r.getFromId()) + ).toList(); + if (CollectionUtils.isEmpty(list)) { + return; + } + } else { + //若父集不是顶级物关系,继续处理 + list = relationDetailDTOS.stream().filter(r -> + Objects.equals(currentNode.getFromId(), r.getToId()) + && !Objects.equals(r.getRootId(), r.getFromId())).toList(); + if (CollectionUtils.isEmpty(list)) { + return; + } /*else { + //若需要将父集的同级查询,将这里放开 + List fjList = list.stream().map(IotThingRelationDetailDTO::getFromId).toList(); + list = relationDetailDTOS.stream().filter(r -> + fjList.contains(r.getFromId()) + && !Objects.equals(r.getRootId(), r.getFromId())).toList(); + }*/ + } + layers -= 1; + for (IotThingRelationDetailDTO tempNode : list) { + recurrenceNodes(relationDetailDTOS, tempNode, layers, upOrDown, result); + } + } + } + + private void recursionChildrenAllChild(List allDetailNodes, List rootNodes) { + for (ObjectNode rootChildNode : allDetailNodes) { + String toId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_ID.getField()).asText(); + String rootThingId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asText(); + String rootId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asText(); + //过滤对应的根关系信息 + ObjectNode rootNode = rootNodes.stream().filter(node -> node.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asText() + .equals(rootId)).findFirst().orElseThrow(() -> new RuntimeException("根节点不存在")); + //关系名称和组名称 + if (rootNode.has(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField())) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField(), + rootNode.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField()).asText()); + } + + if (rootNode.has(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField())) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField(), + rootNode.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField()).asText()); + } + //物实体信息 + List entityNode = cache.findKeyMap(CacheNameEnum.THING_ENTITY, String.valueOf(UserContext.getRealTenantCode()) + + ":" + rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_CODE.getField()).asText()); + if (CollectionUtils.isNotEmpty(entityNode)) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ENTITY_TYPE.getField(), entityNode.get(0).get(CacheNameEnum.EntityField.THING_ENTITY_TYPE.getField()).asText()); + } + List childList = new ArrayList<>(allDetailNodes.stream().filter(node -> + node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_ID.getField()).asText().equals(toId) + && node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asText().equals(rootThingId)).toList()); + if (CollectionUtils.isNotEmpty(childList)) { + childList.sort(Comparator.comparingLong(node -> node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField()).asLong())); + //这个标识很重要 + childList.forEach(s-> s.put("flag","1")); + recursionChildren(childList, allDetailNodes, rootNodes); + rootChildNode.put(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField(), JacksonUtil.convertValue(childList, ArrayNode.class)); + } else { + //没有子节点的时候,要将子节点删除 + if (rootChildNode.has(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField())) { + rootChildNode.remove(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()); + } + } + } + } + + + private void recursionChildren(List firstNodes, List allDetailNodes, List rootNodes) { + for (ObjectNode rootChildNode : firstNodes) { + String toId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_ID.getField()).asText(); + String rootThingId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asText(); + String rootId = rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_ID.getField()).asText(); + //过滤对应的根关系信息 + ObjectNode rootNode = rootNodes.stream().filter(node -> node.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asText() + .equals(rootId)).findFirst().orElseThrow(() -> new RuntimeException("根节点不存在")); + //关系名称和组名称 + if (rootNode.has(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField())) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField(), + rootNode.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField()).asText()); + } + + if (rootNode.has(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField())) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField(), + rootNode.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField()).asText()); + } + //物实体信息 + List entityNode = cache.findKeyMap(CacheNameEnum.THING_ENTITY, String.valueOf(UserContext.getRealTenantCode()) + + ":" + rootChildNode.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_CODE.getField()).asText()); + if (CollectionUtils.isNotEmpty(entityNode)) { + rootChildNode.put(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ENTITY_TYPE.getField(), entityNode.get(0).get(CacheNameEnum.EntityField.THING_ENTITY_TYPE.getField()).asText()); + } + List childList = new ArrayList<>(allDetailNodes.stream().filter(node -> + node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_ID.getField()).asText().equals(toId) + && node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_ROOT_THING_ID.getField()).asText().equals(rootThingId)).toList()); + if (CollectionUtils.isNotEmpty(childList)) { + childList.sort(Comparator.comparingLong(node -> node.get(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField()).asLong())); + recursionChildren(childList, allDetailNodes, rootNodes); + rootChildNode.put(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField(), JacksonUtil.convertValue(childList, ArrayNode.class)); + } else { + //没有子节点的时候,要将子节点删除 + if (rootChildNode.has(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField())) { + rootChildNode.remove(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_CHILDREN.getField()); + } + } + } + } + + private List> buildParam(String entityName) { + List> filterList = new ArrayList<>(); + if (StringUtils.isNotBlank(entityName)) { + filterList.add(Pair.of(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_NAME.getField(), entityName)); + filterList.add(Pair.of(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_TO_CODE.getField(), entityName)); + filterList.add(Pair.of(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_NAME.getField(), entityName)); + filterList.add(Pair.of(CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_FROM_CODE.getField(), entityName)); + } + return filterList; + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/controller/IotThingRelationRootController.java b/modules/thing/src/main/java/com/thing/thing/relation/root/controller/IotThingRelationRootController.java new file mode 100644 index 0000000..2fbb7ff --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/controller/IotThingRelationRootController.java @@ -0,0 +1,121 @@ +package com.thing.thing.relation.root.controller; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.annotation.LogOperation; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.validator.AssertUtils; +import com.thing.common.core.validator.ValidatorUtils; +import com.thing.common.core.validator.group.AddGroup; +import com.thing.common.core.validator.group.DefaultGroup; +import com.thing.common.core.validator.group.UpdateGroup; +import com.thing.common.core.web.response.PageData; +import com.thing.common.core.web.response.Result; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.relation.root.dto.IotThingRelationRootDTO; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("v2/relation/root") +@Tag(name="物关系根表") +public class IotThingRelationRootController { + + @Resource + private IotThingRelationRootService service; + + @GetMapping("page") + @Operation(summary="视图分页") + public Result> pageList( + @Parameter(name = Constant.PAGE, description = "当前页码,从1开始") @RequestParam Integer page, + @Parameter(name = Constant.LIMIT, description = "每页显示记录数") @RequestParam Integer limit, + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "entityName", description = "物令牌(无)") @RequestParam(required = false) String ids, + @Parameter(name = "name", description = "数据来源") @RequestParam(required = false) String name, + @Parameter(name = "groupName", description = "在线离线状态,0离线 1在线 2错误 3未接入") @RequestParam(required = false) String groupName) + { + PageData pageList = service.pageList(page,limit,orderField,order,ids,name,groupName); + return new Result>().ok(pageList); + } + + @GetMapping("list") + @Operation(summary="列表") + public Result> list( + @Parameter(name = Constant.ORDER_FIELD, description = "排序字段") @RequestParam(required = false) String orderField, + @Parameter(name = Constant.ORDER, description = "排序方式,可选值(asc、desc)") @RequestParam(required = false) String order, + @Parameter(name = "name", description = "数据来源") @RequestParam(required = false) String name, + @Parameter(name = "entityName", description = "物令牌(无)") @RequestParam(required = false) String ids, + @Parameter(name = "groupName", description = "在线离线状态,0离线 1在线 2错误 3未接入") @RequestParam(required = false) String groupName){ + List list = service.findList(orderField,order,ids,name,groupName, UserContext.getRealTenantCode()); + return new Result>().ok(list); + } + + @GetMapping("{id}") + @Operation(summary="信息") + public Result get(@PathVariable("id") Long id){ + IotThingRelationRootDTO data = service.getByIdAs(id, IotThingRelationRootDTO.class); + return new Result().ok(data); + } + + @PostMapping + @Operation(summary="保存") + @LogOperation("保存") + public Result save(@RequestBody IotThingRelationRootDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, AddGroup.class, DefaultGroup.class); + service.save(dto); + return new Result<>(); + } + + @PutMapping + @Operation(summary="修改") + @LogOperation("修改") + public Result update(@RequestBody IotThingRelationRootDTO dto){ + //效验数据 + ValidatorUtils.validateEntity(dto, UpdateGroup.class, DefaultGroup.class); + service.update(dto); + return new Result<>(); + } + + @DeleteMapping + @Operation(summary="删除") + @LogOperation("删除") + public Result delete(@RequestBody Long[] ids){ + //效验数据 + AssertUtils.isArrayEmpty(ids, "id"); + service.batchDeleteByIds(ids); + return new Result<>(); + } + + /** + *@GetMapping("export") + *@Operation(summary="导出") + *@LogOperation("导出") + *public void export(@Parameter(hidden = true) @RequestParam Map params, HttpServletResponse response) throws Exception { + * List list = iotThingRelationRootService.listAs(params, IotThingRelationRootDTO.class); + * //ExcelUtils.exportExcelToTarget(response, null, "物关系根表", list, IotThingRelationRootExcel.class); + *} + */ + + + @GetMapping("group") + @Operation(summary="组信息") + public Result> group(){ + return new Result>().ok(service.findAllGroup()); + } + + +// @GetMapping("type") +// @Operation(summary = "物关系类型") +// public Result> findAllType(String groupName) { +// List list = service.findAllType(groupName); +// return new Result>().ok(list); +// } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootDTO.java new file mode 100644 index 0000000..5006ad9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootDTO.java @@ -0,0 +1,66 @@ +package com.thing.thing.relation.root.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 物关系根表 +* +* @author xc +* @since 3.0 2024-03-25 +*/ +@Data +@Schema(description = "物关系根表") +public class IotThingRelationRootDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "关系名称") + @NotBlank(message = "关系名称不能为空") + private String name; + @Schema(description = "描述") + private String remark; + @Schema(description = "图片地址") + private String url; + @Schema(description = "关系配置项") + private String config; + //@Schema(description = "关系类型") + // @NotBlank(message = "关系类型不能为空") + // private String type; + @Schema(description = "排序") + private Long sort; + @Schema(description = "是否在物关系显示 0:不展示 1.展示") + private Integer relationShow; + @Schema(description = "物关系显示排序") + private Integer relationSort; + @Schema(description = "所属企业(租户)") + private Long tenantCode; + @Schema(description = "企业详情id") + private Long companyId; + @Schema(description = "部门id") + private Long deptId; + @Schema(description = "创建者") + private Long creator; + @Schema(description = "创建时间") + private Long createDate; + @Schema(description = "更新者") + private Long updater; + @Schema(description = "更新时间") + private Long updateDate; + @Schema(description = "组名称") + @NotBlank(message = "组名称不能为空") + private String groupName; + @Schema(description = "物实体的id") + @NotBlank(message = "物实体的id不能为空") + private Long entityId; + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootListDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootListDTO.java new file mode 100644 index 0000000..3a79a52 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/IotThingRelationRootListDTO.java @@ -0,0 +1,54 @@ +package com.thing.thing.relation.root.dto; + +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** +* 物关系根表 +* +* @author Make +* @since 3.0 2024-03-25 +*/ +@Data +@Schema(description = "物关系根表") +public class IotThingRelationRootListDTO implements Serializable { + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + @JsonSerialize(using = ToStringSerializer.class) + private Long id; + @Schema(description = "关系名称") + @NotBlank(message = "关系名称不能为空") + private String name; + @Schema(description = "描述") + private String remark; + @Schema(description = "图片地址") + private String url; + @Schema(description = "关系配置项") + private String config; + @Schema(description = "排序") + @JsonSerialize(using = ToStringSerializer.class) + private Long sort; + @Schema(description = "是否在物关系显示 0:不展示 1.展示") + @JsonSerialize(using = ToStringSerializer.class) + private Integer relationShow; + @Schema(description = "物关系显示排序") + @JsonSerialize(using = ToStringSerializer.class) + private Integer relationSort; + @Schema(description = "组名称") + @NotBlank(message = "组名称不能为空") + private String groupName; + @Schema(description = "租户编码") + @JsonSerialize(using = ToStringSerializer.class) + private Long tenantCode; + private Long createDate; + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/dto/ThingSortTreeDTO.java b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/ThingSortTreeDTO.java new file mode 100644 index 0000000..9066822 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/dto/ThingSortTreeDTO.java @@ -0,0 +1,40 @@ +package com.thing.thing.relation.root.dto; + +import com.thing.common.core.utils.params.SortTreeNode; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; +import java.io.Serializable; + +/** + * Author: SiYang + * Date: 2023/10/23 10:14 + * Description: 物的排序树,给出每个元素在整棵树中的序号 + */ +@Data +@EqualsAndHashCode(callSuper = true) +public class ThingSortTreeDTO extends SortTreeNode implements Serializable{ + @Serial + private static final long serialVersionUID = 1L; + + @Schema(description = "物实体主键") + private Long entityId; + + @Schema(description = "实体code") + private String entityCode; + + @Schema(description = "实体名称") + private String entityName; + + @Schema(description = "顶级物实体主键") + private Long rootThingId; + + @Schema(description = "排序号") + private String sort; + + @Schema(description = "节点深度") + private int depth; + +} diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/entity/IotThingRelationRootEntity.java b/modules/thing/src/main/java/com/thing/thing/relation/root/entity/IotThingRelationRootEntity.java new file mode 100644 index 0000000..8a4f09a --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/entity/IotThingRelationRootEntity.java @@ -0,0 +1,63 @@ +package com.thing.thing.relation.root.entity; + +import com.mybatisflex.annotation.Table; +import com.thing.common.orm.entity.BaseInfoEntity; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.experimental.Accessors; + +import java.io.Serial; + +/** + * 物关系根表 + * + * @author xc + * @since 3.0 2024-03-25 + */ +@Data +@Accessors(chain = true) +@EqualsAndHashCode(callSuper=false) +@Table("iot_thing_relation_root") +public class IotThingRelationRootEntity extends BaseInfoEntity { + @Serial + private static final long serialVersionUID = 1L; + + /** + * 关系名称 + */ + private String name; + /** + * 描述 + */ + private String remark; + /** + * 图片地址 + */ + private String url; + /** + * 关系配置项 + */ + private String config; + /** + * 关系类型 + */ + //private String type; + /** + * 排序 + */ + private Long sort; + /** + * 是否在物关系显示 0:不展示 1.展示 + */ + private Integer relationShow; + /** + * 物关系显示排序 + */ + private Integer relationSort; + + + /** + * 组名称 + */ + private String groupName; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/mapper/IotThingRelationRootMapper.java b/modules/thing/src/main/java/com/thing/thing/relation/root/mapper/IotThingRelationRootMapper.java new file mode 100644 index 0000000..5e80faa --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/mapper/IotThingRelationRootMapper.java @@ -0,0 +1,16 @@ +package com.thing.thing.relation.root.mapper; + +import com.thing.common.orm.mapper.PowerBaseMapper; +import com.thing.thing.relation.root.entity.IotThingRelationRootEntity; +import org.apache.ibatis.annotations.Mapper; + +/** +* 物关系根表 +* +* @author xc +* @since 3.0 2024-03-25 +*/ +@Mapper +public interface IotThingRelationRootMapper extends PowerBaseMapper { + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/service/IotThingRelationRootService.java b/modules/thing/src/main/java/com/thing/thing/relation/root/service/IotThingRelationRootService.java new file mode 100644 index 0000000..cd6fc34 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/service/IotThingRelationRootService.java @@ -0,0 +1,69 @@ +package com.thing.thing.relation.root.service; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.IBaseService; +import com.thing.thing.relation.root.dto.IotThingRelationRootDTO; +import com.thing.thing.relation.root.entity.IotThingRelationRootEntity; + +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +public interface IotThingRelationRootService extends IBaseService { + + + PageData pageList(Integer page,Integer limit,String orderField,String order,String ids,String name,String groupName); + + List findList(String orderField, String order,String ids,String name, String groupName,Long tenantCode); + + List findTreeListByRootIdAndGroup(Long rootId, String group); + + List findAllGroup(); + + IotThingRelationRootDTO findById(Long id); + + List findByIds(List id,boolean isAsc); + + Long findMaxSort(); + + void save(IotThingRelationRootDTO dto); + + void update(IotThingRelationRootDTO dto); + + void batchDeleteByIds(Long[] ids); + + /** + * 对"相同树"中的数据进行深度优先排序 + * @param toSortThingIds 待排序的数据所关联的物的id列表 + * @param rootId 关系id + * @param rootThingId 当前关系中的顶级物id + * @param toSortDataList 待排序的数据列表 + * @param identityFun 将数据转为 Map> 格式的函数 + * @return 排序好的数据列表 + */ + List doSort( + Long rootId, + Long rootThingId, + Set toSortThingIds, + List toSortDataList, + Function, Map>> identityFun); + + /** + * 对"不同树"中的数据进行深度优先排序 + * @param toSortThingIds 待排序的数据所关联的物的id列表 + * @param rootId 关系id + * @param rootThingIds 当前关系中的顶级物id列表 + * @param toSortDataList 待排序的数据列表 + * @param identityFun 将数据转为 Map> 格式的函数 + * @return 排序好的数据列表 + */ + List sortInDiffRootThingIds( + Long rootId, + List rootThingIds, + Set toSortThingIds, + List toSortDataList, + Function, Map>> identityFun); + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/relation/root/service/impl/IotThingRelationRootServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/relation/root/service/impl/IotThingRelationRootServiceImpl.java new file mode 100644 index 0000000..56e85c5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/relation/root/service/impl/IotThingRelationRootServiceImpl.java @@ -0,0 +1,378 @@ +package com.thing.thing.relation.root.service.impl; + +import cn.hutool.core.map.MapUtil; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.CaseFormat; +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.cache.constants.CacheNameEnum; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.enumeration.ThingSortType; +import com.thing.common.core.exception.SysException; +import com.thing.common.core.utils.BizUtils; +import com.thing.common.core.utils.CompareUtils; +import com.thing.common.core.utils.JacksonUtil; +import com.thing.common.core.utils.PageUtils; +import com.thing.common.core.web.response.PageData; +import com.thing.common.orm.service.impl.BaseServiceImpl; +import com.thing.sys.oss.cloud.OSSFactory; +import com.thing.sys.security.context.UserContext; +import com.thing.thing.cache.service.CacheInit; +import com.thing.thing.cache.service.ThingCache; +import com.thing.thing.entity.dto.IotThingEntityInfoDTO; +import com.thing.thing.entity.service.IotThingEntityService; +import com.thing.thing.relation.detail.dto.IotThingRelationDetailDTO; +import com.thing.thing.relation.detail.entity.IotThingRelationDetailEntity; +import com.thing.thing.relation.detail.service.IotThingRelationDetailService; +import com.thing.thing.relation.root.dto.IotThingRelationRootDTO; +import com.thing.thing.relation.root.dto.IotThingRelationRootListDTO; +import com.thing.thing.relation.root.dto.ThingSortTreeDTO; +import com.thing.thing.relation.root.entity.IotThingRelationRootEntity; +import com.thing.thing.relation.root.mapper.IotThingRelationRootMapper; +import com.thing.thing.relation.root.service.IotThingRelationRootService; +import com.thing.transport.api.adaptor.JsonConverter; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.compress.utils.Lists; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; +import org.apache.commons.lang3.tuple.Triple; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.mybatisflex.core.query.QueryMethods.max; +import static com.thing.thing.relation.root.entity.table.IotThingRelationRootEntityTableDef.IOT_THING_RELATION_ROOT_ENTITY; + +@Service +public class IotThingRelationRootServiceImpl extends BaseServiceImpl implements IotThingRelationRootService { + + @Resource + private IotThingRelationDetailService relationDetailsService; + + @Resource + private IotThingEntityService entitiesService; + + @Resource + private ThingCache cache; + + private final static String ROOT_CONFIG = "{\"width\":120,\"height\":40,\"shape\":\"rect\"}"; + + @Override + public QueryWrapper getWrapper(Map params){ + QueryWrapper wrapper = new QueryWrapper(); + String name = (String) params.get("name"); + String groupName = (String) params.get("groupName"); + Long tenantCode = UserContext.getRealTenantCode(); + String ids = MapUtil.getStr(params, "ids"); + List idList = Lists.newArrayList(); + if(StringUtils.isNotBlank(ids)){ + idList = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList()); + } + wrapper.like(IotThingRelationRootEntity::getName,name,Objects.nonNull(name)) + .like(IotThingRelationRootEntity::getGroupName,groupName,Objects.nonNull(groupName)) + .in(IotThingRelationRootEntity::getId,idList,CollectionUtils.isNotEmpty(idList)) + .eq(IotThingRelationRootEntity::getTenantCode,tenantCode,Objects.nonNull(tenantCode)) + ; + return wrapper; + } + + public QueryWrapper getWrapper(String orderField,String order,String name,String groupName,String ids,Long tenantCode){ + QueryWrapper wrapper = new QueryWrapper(); + if(StringUtils.isNotBlank(ids)){ + List idList = Arrays.stream(ids.split(",")).map(Long::parseLong).collect(Collectors.toList()); + wrapper .in(IotThingRelationRootEntity::getId,idList); + } + wrapper.like(IotThingRelationRootEntity::getName,name,Objects.nonNull(name)) + .like(IotThingRelationRootEntity::getGroupName,groupName,Objects.nonNull(groupName)) + .eq(IotThingRelationRootEntity::getTenantCode,tenantCode,Objects.nonNull(tenantCode)) + ; + if(StringUtils.isNotBlank(orderField)){ + wrapper.orderBy(orderField,StringUtils.equalsIgnoreCase(order,Constant.ASC)); + }else{ + wrapper.orderBy(IOT_THING_RELATION_ROOT_ENTITY.SORT,StringUtils.equalsIgnoreCase(order,Constant.DESC)); + } + return wrapper; + } + + @Override + public PageData pageList(Integer page,Integer limit,String orderField,String order,String ids,String name,String groupName){ + List list = findList(orderField,order,ids,name,groupName,UserContext.getRealTenantCode()); + if (CollectionUtils.isEmpty(list)) { + return PageData.empty(); + } + List resList = PageUtils.startPage(list, page, limit); + return new PageData<>(resList, CollectionUtils.size(list)); + } + + @Override + public List findList(String orderField, String order,String ids,String name, String groupName,Long tenantCode) { + List rootList = cache.getTopicMap(CacheNameEnum.THING_ROOT_RELATION); + if(CollectionUtils.isEmpty(rootList)){ + List list = mapper.selectListByQueryAs(getWrapper(orderField, order, null, null,null,null) + , IotThingRelationRootListDTO.class); + rootList = JsonConverter.convertToJsonObjectListObjectNode(list); + if(CollectionUtils.isEmpty(rootList)){ + return new ArrayList<>(); + } + CacheInit.relationRootMap(rootList,cache); + } + if(StringUtils.isBlank(orderField)){ + orderField = CacheNameEnum.RelationDetailField.THING_RELATION_DETAIL_SORT.getField(); + } + String finalOrderField = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, orderField); + Comparator comparator = CompareUtils.getComparator(order, finalOrderField); //封装参数 + List> pairs = buildParam(name, groupName,tenantCode); + return rootList.stream() + .filter(jsonObject -> { + boolean passesFilter = JacksonUtil.filter(jsonObject, pairs); + if (passesFilter && StringUtils.isNotBlank(ids)) { + String[] idList = ids.split(","); + long entityId = jsonObject.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asLong(); + passesFilter = ArrayUtils.contains(idList, String.valueOf(entityId)); + } + return passesFilter; + }).sorted(comparator.thenComparing(obj -> obj.get(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_ID.getField()).asLong())).toList(); + } + + @Override + public List findTreeListByRootIdAndGroup(Long rootId, String group) { + return mapper.selectListByQueryAs(QueryWrapper.create() + .eq(IotThingRelationRootEntity::getId,rootId) + .eq(IotThingRelationRootEntity::getGroupName,group) + .orderBy(IotThingRelationRootEntity::getSort,true),IotThingRelationRootDTO.class); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void save(IotThingRelationRootDTO dto) { + String groupName = BizUtils.trimAll(dto.getGroupName()); + String name = BizUtils.trimAll(dto.getName()); + //查询当前组下是否已经存在该类型的关系 + IotThingRelationRootEntity iotThingRelationRootEntity = mapper.selectOneByQuery(QueryWrapper.create() + .eq(IotThingRelationRootEntity::getName, name) + .eq(IotThingRelationRootEntity::getGroupName, groupName)); + //若是关系存在,校验根关系是否已经添加?若已添加,则抛出异常 + if(Objects.nonNull(iotThingRelationRootEntity)){ + IotThingRelationDetailEntity detailEntity = relationDetailsService.findByRootIdAndRootThingId(iotThingRelationRootEntity.getId(), dto.getEntityId()); + if(Objects.nonNull(detailEntity)){ + throw new SysException("当前关系的根已存在"); + } + //查询根实体信息 + IotThingEntityInfoDTO entityDTO = entitiesService.findEntityById(dto.getEntityId()); + if(!Objects.isNull(entityDTO)){ + buildRelationDetail(iotThingRelationRootEntity, entityDTO); + } + cache.clearTopic(CacheNameEnum.THING_ROOT_RELATION); + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + return; + } + Long maxSort = findMaxSort(); + IotThingRelationRootEntity iotThingRelationRootInsert = new IotThingRelationRootEntity() + .setName(name) + .setRemark(dto.getRemark()) + .setUrl(StringUtils.isNotBlank(dto.getUrl()) ? OSSFactory.cutOut(dto.getUrl()) : dto.getUrl()) + .setConfig(ROOT_CONFIG) + .setSort(Objects.isNull(maxSort) ? 1 : maxSort+1) + .setRelationShow(1) + .setGroupName(groupName); + mapper.insert(iotThingRelationRootInsert); + IotThingEntityInfoDTO entityDTO = entitiesService.findEntityById(dto.getEntityId()); + if(!Objects.isNull(entityDTO)){ + buildRelationDetail(iotThingRelationRootInsert, entityDTO); + } + cache.clearTopic(CacheNameEnum.THING_ROOT_RELATION); + cache.clearTopic(CacheNameEnum.THING_DETAIL_RELATION); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void update(IotThingRelationRootDTO dto) { + Long id = dto.getId(); + IotThingRelationRootEntity iotThingRelationRootEntity = mapper.selectOneById(id); + iotThingRelationRootEntity.setName(dto.getName()); + String url = BizUtils.trimAll(dto.getUrl()); + if(StringUtils.isNotBlank(url)){ + url = OSSFactory.cutOut(dto.getUrl()); + dto.setUrl(url); + } + iotThingRelationRootEntity.setUrl(url); + iotThingRelationRootEntity.setRemark(dto.getRemark()); + iotThingRelationRootEntity.setGroupName(dto.getGroupName()); + mapper.insertOrUpdateSelective(iotThingRelationRootEntity); + cache.clearTopic(CacheNameEnum.THING_ROOT_RELATION); + } + + @Override + public List findAllGroup() { + Optional> optional = Optional.ofNullable(mapper.selectListByQuery(QueryWrapper.create() + .select(IOT_THING_RELATION_ROOT_ENTITY.GROUP_NAME) + .orderBy(IOT_THING_RELATION_ROOT_ENTITY.CREATE_DATE.desc()))); + + return optional.map(iotThingRelationRootEntities -> iotThingRelationRootEntities.stream() + .map(IotThingRelationRootEntity::getGroupName) + .filter(Objects::nonNull).distinct().collect(Collectors.toList())).orElse(null); + } + + @Override + public IotThingRelationRootDTO findById(Long id) { + return mapper.selectOneByQueryAs(QueryWrapper.create().eq(IotThingRelationRootEntity::getId,id),IotThingRelationRootDTO.class); + } + + @Override + public List findByIds(List ids,boolean isAsc) { + return mapper.selectListByQueryAs(QueryWrapper.create().in(IotThingRelationRootEntity::getId,ids).orderBy(IotThingRelationRootEntity::getSort,isAsc),IotThingRelationRootDTO.class); + } + + @Override + public Long findMaxSort() { + QueryWrapper queryWrapper = QueryWrapper + .create() + .select(max(IOT_THING_RELATION_ROOT_ENTITY.SORT)); + return mapper.selectOneByQueryAs(queryWrapper,Long.class); + } + + @Transactional(rollbackFor = Exception.class) + @Override + public void batchDeleteByIds(Long[] ids) { + List detailDTOList = relationDetailsService.findByRootIds(Arrays.asList(ids)); + if(CollectionUtils.isNotEmpty(detailDTOList)){ + throw new SysException("存在子节点,请先删除子节点"); + } + //删除根关系 + mapper.deleteBatchByIds(Arrays.asList(ids)); + cache.clearTopic(CacheNameEnum.THING_ROOT_RELATION); + } + + /** + * 对数据进行深度优先排序 + * + * @param toSortThingIds 待排序的数据所关联的物的id列表 + * @param rootId 关系id + * @param rootThingId 当前关系中的顶级物id + * @param toSortDataList 待排序的数据列表 + * @param identityFun 将数据转为 Map> 格式的函数 + * @return 排序好的数据列表 + */ + @Override + public List doSort( + Long rootId, + Long rootThingId, + Set toSortThingIds, + List toSortDataList, + Function, Map>> identityFun) { + List> sortThings = getSortThings(rootId, rootThingId); + List> validSortThings = + sortThings.stream().filter(e -> toSortThingIds.contains(e.getLeft())).toList(); + List sortedResult = new ArrayList<>(); + Map> thingDataMap = identityFun.apply(toSortDataList); + for (Triple sortThing : validSortThings) { + Long thingId = sortThing.getLeft(); + List currentThingList = thingDataMap.get(thingId); + if (!org.springframework.util.CollectionUtils.isEmpty(currentThingList)) { + sortedResult.addAll(currentThingList); + } + } + return sortedResult; + } + + @Override + public List sortInDiffRootThingIds( + Long rootId, + List rootThingIds, + Set toSortThingIds, + List toSortDataList, + Function, Map>> identityFun) { + if (!org.springframework.util.CollectionUtils.isEmpty(rootThingIds)) { + return rootThingIds.stream() + .map( + rootThingId -> + doSort( + rootId, + Long.parseLong(rootThingId), + toSortThingIds, + toSortDataList, + identityFun)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + return doSort(rootId, null, toSortThingIds, toSortDataList, identityFun); + } + + + public List> getSortThings(Long rootId, Long rootThingId) { + return getSortThings(rootId, rootThingId, ThingSortType.DEPTH); + } + + /** + * 对关系树中的物进行排序 + * + * @param rootId 关系的根id + * @param rootThingId 顶级设备id + * @param sortType 排序类型 + * @return triple + */ + public List> getSortThings(Long rootId, Long rootThingId, ThingSortType sortType) { + List sortTree = relationDetailsService.findRootDetailSortTreeList(rootId,rootThingId); + List> triples = new ArrayList<>(); + flatTree(sortTree, triples); + return triples.stream() + .sorted( + (t1, t2) -> { + if (Objects.requireNonNull(sortType) == ThingSortType.BREADTH) { + Long l1 = Long.parseLong(t1.getRight()); + Long l2 = Long.parseLong(t2.getRight()); + return l1.compareTo(l2); + } + return t1.getRight().compareTo(t2.getRight()); + }) + .collect(Collectors.toList()); + } + private void flatTree(List trees, List> triples){ + triples = Objects.isNull(triples) ? new ArrayList<>() : triples; + for (ThingSortTreeDTO tree : trees) { + Triple triple = Triple.of(tree.getEntityId(), tree.getEntityCode(), tree.getSort()); + triples.add(triple); + List subTrees = tree.getChildren(); + if(!subTrees.isEmpty()){ + flatTree(subTrees, triples); + } + } + } + + private void buildRelationDetail(IotThingRelationRootEntity rootEntity, IotThingEntityInfoDTO entityDTO) { + Long id = rootEntity.getId(); + Long maxSort = relationDetailsService.findByMaxSort(id); + IotThingRelationDetailEntity detailEntityInsert = new IotThingRelationDetailEntity() + .setFromId(rootEntity.getId()) + .setFromName(rootEntity.getName()) + .setToId(entityDTO.getId()) + .setToCode(entityDTO.getCode()) + .setToName(entityDTO.getName()) + .setRootId(rootEntity.getId()) + .setConfig(ROOT_CONFIG) + .setTag(entityDTO.getName()) + .setSort(Objects.isNull(maxSort) ? 1L : maxSort + 1L) + .setRootThingId(entityDTO.getId()); + relationDetailsService.save(detailEntityInsert); + } + + private List> buildParam(String name, String groupName,Long tenantCode){ + List> filterList = new ArrayList<>(); + if (StringUtils.isNotBlank(name)) { + filterList.add(Pair.of(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_NAME.getField(), name)); + } + if (StringUtils.isNotBlank(groupName)) { + filterList.add(Pair.of(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_GROUP_NAME.getField(), groupName)); + } + if (Objects.nonNull(tenantCode)) { + filterList.add(Pair.of(CacheNameEnum.RelationRootField.THING_RELATION_ROOT_TENANT_CODE.getField(), tenantCode.toString())); + } + return filterList; + } + + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/controller/TsKvController.java b/modules/thing/src/main/java/com/thing/thing/tskv/controller/TsKvController.java new file mode 100644 index 0000000..35198f6 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/controller/TsKvController.java @@ -0,0 +1,58 @@ +package com.thing.thing.tskv.controller; + +import com.thing.common.core.web.response.Result; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Parameter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author zhenghh. 2022-11-02 + **/ +@RestController +@RequestMapping("tsKv") +@Tag(name = "遥测数据") +public class TsKvController { + + @Autowired + private TskvService tsKvService; + + @PostMapping("list/one/{startTime}/{endTime}") + @Operation(summary="单个区间") + public Result> getTimeSeries(@RequestBody ThingAttrDTO thingAttrDTO, @PathVariable Long startTime, @PathVariable Long endTime) { + List timeSeries = tsKvService.getTimeSeries(thingAttrDTO, startTime, endTime); + return new Result>().ok(timeSeries); + } + + @PostMapping("list/{startTime}/{endTime}") + @Operation(summary="多个区间") + public Result> getTimeSeries(@RequestBody List thingAttrList, @PathVariable Long startTime, @PathVariable Long endTime) { + List timeSeries = tsKvService.getTimeSeries(thingAttrList, startTime, endTime); + return new Result>().ok(timeSeries); + + } + + @PostMapping("list/last/one") + @Operation(summary="单个最新") + public Result> getLastTimeSeries(@RequestBody ThingAttrDTO thingAttrDTO) { + List timeSeries = tsKvService.getLastTimeSeries(thingAttrDTO); + return new Result>().ok(timeSeries); + + } + + @PostMapping("list/last") + @Operation(summary="多个最新") + public Result> getLastTimeSeries(@RequestBody List thingAttrList) { + List timeSeries = tsKvService.getLastTimeSeries(thingAttrList); + return new Result>().ok(timeSeries); + + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttr.java b/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttr.java new file mode 100644 index 0000000..5eb28e4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttr.java @@ -0,0 +1,75 @@ +package com.thing.thing.tskv.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.io.Serializable; +import java.util.List; + +/** + * @author zhenghh. 2022-11-02 + **/ +@Data +public class ThingAttr implements Serializable { + private static final long serialVersionUID = -2077243440277863622L; + /** + * 物实体表主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 物属性及关联物 + */ + private List attributes; + + @Data + public static class Attribute { + /** + * 物属性 + */ + private String attrCode; + /** + * 计算规则 + */ + private String rule; + /** + * 计算物列表 + */ + private List thingChildList; + + public Attribute(String attrCode) { + this.attrCode = attrCode; + } + + public Attribute(String rule, List thingChildList) { + this.rule = rule; + this.thingChildList = thingChildList; + } + } + + @Data + @AllArgsConstructor + public static class ChildThing { + /** + * AA/BB + */ + private String thingSerial; + /** + * 物编码 + */ + private String thingCode; + /** + * 物属性 + */ + private String thingAttrCode; + + public ChildThing(String thingCode, String thingAttrCode) { + this.thingCode = thingCode; + this.thingAttrCode = thingAttrCode; + } + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttrDTO.java b/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttrDTO.java new file mode 100644 index 0000000..b0b7705 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/dto/ThingAttrDTO.java @@ -0,0 +1,51 @@ +package com.thing.thing.tskv.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +/** + * @author zhenghh. 2022-11-02 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ThingAttrDTO implements Serializable { + + private static final long serialVersionUID = 7235106781977132645L; + /** + * 物实体表主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 物属性 + */ + private List attrList; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ThingAttrDTO that = (ThingAttrDTO) o; + return thingId.equals(that.thingId) && thingCode.equals(that.thingCode); + } + + @Override + public int hashCode() { + return Objects.hash(thingId, thingCode); + } + + public ThingAttrDTO(Long thingId, String thingCode) { + this.thingId = thingId; + this.thingCode = thingCode; + } + +} diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/dto/TsKvDTO.java b/modules/thing/src/main/java/com/thing/thing/tskv/dto/TsKvDTO.java new file mode 100644 index 0000000..e21de32 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/dto/TsKvDTO.java @@ -0,0 +1,48 @@ +package com.thing.thing.tskv.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; + +/** + * @author zhenghh. 2022-11-02 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TsKvDTO implements Serializable { + + @Serial + private static final long serialVersionUID = -1091864856002341905L; + + /** + * 物实体表主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 时间 + */ + private String thingAttr; + /** + * 时间 + */ + private Long ts; + /** + * 值 + */ + private String val; + + public TsKvDTO(String thingCode, String thingAttr, Long ts, String val) { + this.thingCode = thingCode; + this.thingAttr = thingAttr; + this.ts = ts; + this.val = val; + } +} diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/service/TskvService.java b/modules/thing/src/main/java/com/thing/thing/tskv/service/TskvService.java new file mode 100644 index 0000000..e21d7bf --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/service/TskvService.java @@ -0,0 +1,342 @@ +package com.thing.thing.tskv.service; + +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.TimeType; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; + +import java.util.List; +import java.util.Map; + +/** + * @author zhenghh. 2022-11-02 + **/ +public interface TskvService { + + /** + * 单设备最新遥测值 + * + * @param thingAttrDTO param + * @return list + */ + List getLastTimeSeries(ThingAttrDTO thingAttrDTO); + + /** + * 多设备最新遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @return list + */ + List getLastTimeSeries(Map> thingMap); + + /** + * 多设备最新遥测值 + * + * @param thingAttrList param + * @return list + */ + List getLastTimeSeries(List thingAttrList); + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime); + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc); + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param timeType 查询粒度 + * @return list + */ + List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, TimeType timeType); + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc, TimeType granularity); + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(List thingAttrList, Long startTime, Long endTime); + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(List thingAttrList, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(List thingAttrList, Long startTime, Long endTime, TimeType granularity,AggType agg); + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(List thingAttrList, Long startTime, Long endTime, Boolean isAsc, TimeType granularity); + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(Map> thingMap, Long startTime, Long endTime); + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(Map> thingMap, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Map> thingMap, Long startTime, Long endTime, TimeType granularity); + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Map> thingMap, Long startTime, Long endTime, Boolean isAsc, TimeType granularity); + + /** + * 单设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @return list + */ + List getLastTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO); + + /** + * 多设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @return list + */ + List getLastTimeSeries(Boolean isSelf, List thingAttrList); + + /** + * 多设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @return list + */ + List getLastTimeSeries(Boolean isSelf, Map> thingMap); + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime); + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc); + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, TimeType granularity); + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc, TimeType granularity); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime, TimeType granularity, AggType agg); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime, Boolean isAsc, TimeType granularity,AggType agg); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, Boolean isAsc); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, TimeType granularity); + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, Boolean isAsc, TimeType granularity); +} diff --git a/modules/thing/src/main/java/com/thing/thing/tskv/service/impl/TskvServiceImpl.java b/modules/thing/src/main/java/com/thing/thing/tskv/service/impl/TskvServiceImpl.java new file mode 100644 index 0000000..ea1d4f5 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/thing/tskv/service/impl/TskvServiceImpl.java @@ -0,0 +1,651 @@ +package com.thing.thing.tskv.service.impl; + +import cn.hutool.core.collection.CollectionUtil; +import com.alibaba.fastjson.JSONObject; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.thing.api.FormulaInvokeService; +import com.thing.common.core.enumeration.AggType; +import com.thing.common.core.enumeration.DataTreatingMarkEnum; +import com.thing.common.core.enumeration.TimeType; +import com.thing.common.tskv.service.TsKvService; +import com.thing.thing.context.service.ThingManageContextService; +import com.thing.thing.dictRelation.dto.IotThingDictRelationDTO; +import com.thing.thing.dictRelation.param.IotThingDictRelationParamDTO; +import com.thing.thing.tskv.dto.ThingAttr; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.MapUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author zhenghh. 2022-11-02 + **/ +@Slf4j +@Service +@RequiredArgsConstructor +public class TskvServiceImpl implements TskvService { + + private final ThingManageContextService thingManageContextService; + + private final TsKvService tskvService; + private final FormulaInvokeService formulaInvokeService; + + /** + * 单设备最新遥测值 + * + * @param thingAttrDTO param + * @return list + */ + @Override + public List getLastTimeSeries(ThingAttrDTO thingAttrDTO) { + return getLastTimeSeries(Boolean.FALSE, thingAttrDTO); + } + + /** + * 多设备最新遥测值 + * + * @param thingAttrList param + * @return list + */ + @Override + public List getLastTimeSeries(List thingAttrList) { + return getLastTimeSeries(Boolean.FALSE, thingAttrList); + } + + /** + * 多设备最新遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @return list + */ + @Override + public List getLastTimeSeries(Map> thingMap) { + return getLastTimeSeries(Boolean.FALSE, thingMap); + } + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime) { + return getTimeSeries(Boolean.FALSE, thingAttrDTO, startTime, endTime); + } + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(Boolean.FALSE, thingAttrDTO, startTime, endTime, isAsc); + } + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param timeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, TimeType timeType) { + return getTimeSeries(Boolean.FALSE, thingAttrDTO, startTime, endTime, timeType); + } + + /** + * 单设备区间遥测值 + * + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc, TimeType granularity) { + return getTimeSeries(Boolean.FALSE, thingAttrDTO, startTime, endTime, isAsc, granularity); + } + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(List thingAttrList, Long startTime, Long endTime) { + return getTimeSeries(Boolean.FALSE, thingAttrList, startTime, endTime); + } + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(List thingAttrList, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(Boolean.FALSE, thingAttrList, startTime, endTime, isAsc); + } + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(List thingAttrList, Long startTime, Long endTime, TimeType granularity, AggType agg) { + return getTimeSeries(Boolean.FALSE, thingAttrList, startTime, endTime, granularity,agg); + } + + /** + * 多设备区间遥测值 + * + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param granularity 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(List thingAttrList, Long startTime, Long endTime, Boolean isAsc, TimeType granularity) { + return getTimeSeries(Boolean.FALSE, thingAttrList, startTime, endTime, isAsc, granularity,null); + } + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(Map> thingMap, Long startTime, Long endTime) { + return getTimeSeries(Boolean.FALSE, thingMap, startTime, endTime); + } + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(Map> thingMap, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(Boolean.FALSE, thingMap, startTime, endTime, isAsc); + } + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param granularity 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Map> thingMap, Long startTime, Long endTime, TimeType granularity) { + return getTimeSeries(Boolean.FALSE, thingMap, startTime, endTime, granularity); + } + + /** + * 多设备区间遥测值 + * + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Map> thingMap, Long startTime, Long endTime, Boolean isAsc, TimeType TimeType) { + return getTimeSeries(Boolean.FALSE, thingMap, startTime, endTime, isAsc, TimeType); + } + + /** + * 单设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @return list + */ + @Override + public List getLastTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO) { + return getLastTimeSeries(isSelf, Lists.newArrayList(thingAttrDTO)); + } + + /** + * 多设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @return list + */ + @Override + public List getLastTimeSeries(Boolean isSelf, Map> thingMap) { + return getLastTimeSeries(isSelf, convertMap(thingMap)); + } + + /** + * 多设备最新遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTOList param + * @return list + */ + @Override + public List getLastTimeSeries(Boolean isSelf, List thingAttrDTOList) { + //设备数据组装 + List thingAttrList = getRelationList(thingAttrDTOList, isSelf); + Map> dataParams = convertDeviceDataParam(thingAttrList); + if (CollectionUtil.isEmpty(dataParams)) { + return Lists.newArrayList(); + } + //获取最新值 + List lastList = tskvService.findLatestByMultiMap(dataParams, false); + if (CollectionUtil.isEmpty(lastList)) { + return Lists.newArrayList(); + } + //数据处理 + return thingAttrList.parallelStream() + .flatMap(thingAttr -> thingAttr.getAttributes().stream() + .map(attribute -> { + //只查自己 不存在数据绑定 直接返回 + if (isSelf || CollectionUtil.isEmpty(attribute.getThingChildList())) { + Optional first = lastList.parallelStream() + .filter(last -> StringUtils.equals(last.getThingCode(), thingAttr.getThingCode()) && StringUtils.equals(last.getAttrKey(), attribute.getAttrCode())) + .findFirst(); + return first.map(cacheTsKvDTO -> new TsKvDTO(thingAttr.getThingId(), thingAttr.getThingCode(), cacheTsKvDTO.getAttrKey(), cacheTsKvDTO.getTs(), cacheTsKvDTO.getVal())).orElse(null); + } + //存在数据绑定 进行最新值计算 + //过滤数据 + OptionalLong optionalLong = lastList.parallelStream() + .filter(last -> attribute.getThingChildList().stream() + .anyMatch(child -> StringUtils.equals(last.getThingCode(), child.getThingCode()) && StringUtils.equals(last.getAttrKey(), child.getThingAttrCode()))) + .mapToLong(com.thing.common.data.tskv.TsKvDTO::getTs).max(); + if (optionalLong.isEmpty()) { + return null; + } + //时间相同 直接计算 + //时间不同,取最新时间 补0计算 + BigDecimal result = BigDecimal.ZERO; + try { + Map env = Maps.newHashMapWithExpectedSize(attribute.getThingChildList().size()); + for (ThingAttr.ChildThing child : attribute.getThingChildList()) { + env.put(child.getThingSerial(), lastList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), child.getThingCode()) + && StringUtils.equals(tsKv.getAttrKey(), child.getThingAttrCode()) + && Objects.equals(tsKv.getTs(), optionalLong.getAsLong())) + .findFirst().map(item -> new BigDecimal(item.getVal())).orElse(BigDecimal.ZERO)); + } + result = formulaInvokeService.invokeFormula(attribute.getRule(), env, 4); + } catch (Exception e) { + log.error("{}-{} 公式: {}, 计算最新值失败: {}", thingAttr.getThingCode(), attribute.getAttrCode(), attribute.getRule(), e.getMessage()); + } + return new TsKvDTO(thingAttr.getThingId(), thingAttr.getThingCode(), attribute.getAttrCode(), optionalLong.getAsLong(), result.toPlainString()); + }).filter(Objects::nonNull)).collect(Collectors.toList()); + } + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime) { + return getTimeSeries(isSelf, thingAttrDTO, startTime, endTime, Boolean.FALSE); + } + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(isSelf, thingAttrDTO, startTime, endTime, isAsc, null); + } + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, TimeType TimeType) { + return getTimeSeries(isSelf, thingAttrDTO, startTime, endTime, Boolean.FALSE, TimeType); + } + + /** + * 单设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTO param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, ThingAttrDTO thingAttrDTO, Long startTime, Long endTime, Boolean isAsc, TimeType TimeType) { + return getTimeSeries(isSelf, Lists.newArrayList(thingAttrDTO), startTime, endTime, isAsc, TimeType,null); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime) { + return getTimeSeries(isSelf, thingAttrList, startTime, endTime, Boolean.FALSE); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(isSelf, thingAttrList, startTime, endTime, isAsc, null,null); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, List thingAttrList, Long startTime, Long endTime, TimeType TimeType,AggType agg) { + return getTimeSeries(isSelf, thingAttrList, startTime, endTime, Boolean.FALSE, TimeType,agg); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingAttrDTOList param + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, List thingAttrDTOList, Long startTime, Long endTime, Boolean isAsc, TimeType TimeType, AggType agg) { + //设备数据组装 + List thingAttrList = getRelationList(thingAttrDTOList, isSelf); + Map> dataParams = convertDeviceDataParam(thingAttrList); + if (MapUtils.isEmpty(dataParams)) { + return Lists.newArrayList(); + } + //获取区间值 AggType agg, Integer interval, TimeType timeType, Boolean isAsc + List intervalList = tskvService.findTsKvByMultiMap(dataParams,startTime,endTime,isAsc); + if (CollectionUtil.isEmpty(intervalList)) { + return Lists.newArrayList(); + } + return thingAttrList.parallelStream() + .flatMap(thingAttr -> thingAttr.getAttributes().stream() + .flatMap(attribute -> { + //只查询自己 不存在数据绑定 直接返回 + if (isSelf || CollectionUtil.isEmpty(attribute.getThingChildList())) { + return intervalList.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), thingAttr.getThingCode()) && StringUtils.equals(tsKv.getAttrKey(), attribute.getAttrCode())) + .map(tsKv -> new TsKvDTO(thingAttr.getThingId(), thingAttr.getThingCode(), attribute.getAttrCode(), tsKv.getTs(), tsKv.getVal())); + } + //存在数据绑定 进行最新值计算 + //过滤数据 + Map> tsListMap = intervalList.parallelStream() + .filter(tsKv -> attribute.getThingChildList().stream() + .anyMatch(child -> StringUtils.equals(tsKv.getThingCode(), child.getThingCode()) && StringUtils.equals(tsKv.getAttrKey(), child.getThingAttrCode()))) + .collect(Collectors.groupingBy(com.thing.common.data.tskv.TsKvDTO::getTs)); + if (CollectionUtil.isEmpty(tsListMap)) { + return Stream.empty(); + } + List result = Lists.newArrayListWithExpectedSize(tsListMap.size()); + tsListMap.forEach((ts, list) -> { + //数据不全不进行计算,直接返回0 + if (attribute.getThingChildList().size() != list.size()) { + log.warn("{}-{}-{}计算数据不全,结果置0", thingAttr.getThingCode(), attribute.getAttrCode(), ts); + result.add(new TsKvDTO(thingAttr.getThingId(), thingAttr.getThingCode(), attribute.getAttrCode(), ts, "0")); + } else { + BigDecimal value = BigDecimal.ZERO; + try { + Map env = Maps.newHashMapWithExpectedSize(attribute.getThingChildList().size()); + for (ThingAttr.ChildThing child : attribute.getThingChildList()) { + env.put(child.getThingSerial(), list.parallelStream() + .filter(tsKv -> StringUtils.equals(tsKv.getThingCode(), child.getThingCode()) + && StringUtils.equals(tsKv.getAttrKey(), child.getThingAttrCode())) + .findFirst().map(item -> new BigDecimal(item.getVal())).orElse(BigDecimal.ZERO)); + } + value = formulaInvokeService.invokeFormula(attribute.getRule(), env, 4); + } catch (Exception e) { + log.error("{}-{} 公式:{}, 计算区间值失败: {}", thingAttr.getThingCode(), attribute.getAttrCode(), attribute.getRule(), e.getMessage()); + } + result.add(new TsKvDTO(thingAttr.getThingId(), thingAttr.getThingCode(), attribute.getAttrCode(), ts, value.toPlainString())); + } + }); + return result.stream(); + })).collect(Collectors.toList()); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime) { + return getTimeSeries(isSelf, thingMap, startTime, endTime, Boolean.FALSE); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, Boolean isAsc) { + return getTimeSeries(isSelf, thingMap, startTime, endTime, isAsc, null); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, TimeType TimeType) { + return getTimeSeries(isSelf, thingMap, startTime, endTime, Boolean.FALSE, TimeType); + } + + /** + * 多设备区间遥测值 + * + * @param isSelf 是否只查询自己 + * @param thingMap <'thingId,thingCode',attrList> + * @param startTime 开始时间 + * @param endTime 结束时间 + * @param isAsc 是否正序排序 + * @param TimeType 查询粒度 + * @return list + */ + @Override + public List getTimeSeries(Boolean isSelf, Map> thingMap, Long startTime, Long endTime, Boolean isAsc, TimeType TimeType) { + return getTimeSeries(isSelf, convertMap(thingMap), startTime, endTime, isAsc, TimeType,null); + } + + /** + * 获取物属性关联物 + * + * @param thingAttrs params + * @param isSelf 是否查询自己 + * @return list + */ + private List getRelationList(List thingAttrs, Boolean isSelf) { + if (CollectionUtil.isEmpty(thingAttrs)) { + return Lists.newArrayList(); + } + //去重 + List thingAttrList = new ArrayList<>(); + thingAttrs.stream().collect(Collectors.groupingBy(item -> new ThingAttrDTO(item.getThingId(), item.getThingCode()), + Collectors.flatMapping(item -> item.getAttrList().stream(), Collectors.toList()))) + .forEach((dto, list) -> { + dto.setAttrList(list.stream().distinct().collect(Collectors.toList())); + thingAttrList.add(dto); + }); + List fromIds = thingAttrList.stream().map(ThingAttrDTO::getThingId).distinct().collect(Collectors.toList()); + List attrList = thingAttrList.stream().flatMap(item -> item.getAttrList().stream()).distinct().collect(Collectors.toList()); + + Optional> optional = thingManageContextService.findDictRelationAllByEntityIdsAndCodes(fromIds, attrList); + return optional.map(iotThingDictRelationDTOS -> thingAttrList.parallelStream().map(item -> { + ThingAttr thingAttr = new ThingAttr(); + thingAttr.setThingId(item.getThingId()); + thingAttr.setThingCode(item.getThingCode()); + List attributes = item.getAttrList().stream().map(attr -> { + Optional first = iotThingDictRelationDTOS.parallelStream() + .filter(dict -> Objects.equals(item.getThingId(), dict.getEntityId()) && StringUtils.equals(attr, dict.getCode())).findFirst(); + if (first.isEmpty()) { + return null; + } + IotThingDictRelationParamDTO dictRelation = first.get(); + //只查自己,直接返回 + if (isSelf) { + return new ThingAttr.Attribute(attr); + } + //数据不处理 或 不存在返回 + if (StringUtils.equals(dictRelation.getDataTreatingMark(), DataTreatingMarkEnum.UNTREATED.getValue()) || StringUtils.isBlank(dictRelation.getChildConfig())) { + return new ThingAttr.Attribute(attr); + } + ThingAttr.Attribute attribute = JSONObject.parseObject(dictRelation.getChildConfig(), ThingAttr.Attribute.class); + attribute.setAttrCode(attr); + return attribute; + }).filter(Objects::nonNull).collect(Collectors.toList()); + if (CollectionUtil.isEmpty(attributes)) { + return null; + } + thingAttr.setAttributes(attributes); + return thingAttr; + }).filter(Objects::nonNull).distinct().collect(Collectors.toList())).orElseGet(Lists::newArrayList); + + } + + /** + * 按物编码分组 + * + * @param thingAttrList 物及物属性列表 + * @return map + */ + private Map> convertDeviceDataParam(List thingAttrList) { + if (CollectionUtil.isEmpty(thingAttrList)) { + return Maps.newHashMapWithExpectedSize(1); + } + return thingAttrList.parallelStream() + .flatMap(item -> item.getAttributes().parallelStream() + .flatMap(attribute -> CollectionUtil.isEmpty(attribute.getThingChildList()) + ? Stream.of(new ThingAttr.ChildThing(item.getThingCode(), attribute.getAttrCode())) + : attribute.getThingChildList().stream()) + ).collect(Collectors.groupingBy(ThingAttr.ChildThing::getThingCode, Collectors.mapping(ThingAttr.ChildThing::getThingAttrCode, Collectors.toCollection(LinkedList::new)))); + } + + /** + * map转为list + * + * @param thingMap <'thingId,thingCode',attrList> + * @return list + */ + private List convertMap(Map> thingMap) { + if (CollectionUtil.isEmpty(thingMap)) { + return Lists.newArrayList(); + } + List thingAttrList = new ArrayList<>(); + thingMap.forEach((key, attrList) -> { + String[] split = key.split(","); + thingAttrList.add(new ThingAttrDTO(Long.parseLong(split[0]), split[1], attrList.stream().distinct().collect(Collectors.toList()))); + }); + return thingAttrList; + } +} diff --git a/modules/thing/src/main/java/com/thing/util/BeanUtil.java b/modules/thing/src/main/java/com/thing/util/BeanUtil.java new file mode 100644 index 0000000..eebb3f3 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/util/BeanUtil.java @@ -0,0 +1,63 @@ +package com.thing.util; + +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.util.ArrayUtil; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.TypeReference; +import com.thing.common.core.exception.SysException; + +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BeanUtil { + + /** + * 将params的k-v参数转换成bean对象 + */ + public static T jsonConvertBean(String params, Class clazz) { + Map paramMap = jsonConvertMap(params); + return cn.hutool.core.bean.BeanUtil.mapToBean(paramMap, clazz, false, new CopyOptions()); + } + + /** + * 将params的k-v参数转为map对象 + */ + public static Map jsonConvertMap(String params){ + if(StringUtils.isBlank(params)){ + throw new SysException("参数不能为空"); + } + return JSON.parseObject(params, new TypeReference>() {}); + } + + /** + * 将params的k-v参数转为map对象 + */ + public static List jsonConvertList(String params){ + if(StringUtils.isBlank(params)){ + throw new SysException("参数不能为空"); + } + return JSON.parseObject(params, new TypeReference<>() {}); + } + + /** + * 将params的k-v参数转为map对象 + */ + public static Map jsonConvertMaps(String ... params){ + if(ArrayUtil.isEmpty(params)){ + throw new SysException("参数不能为空"); + } + //合并每个生成的map + return Arrays.stream(params) + .map(BeanUtil::jsonConvertMap) + .reduce(new HashMap<>(), (map1, map2) -> { + map1.putAll(map2); + return map1; + }); + } + +} diff --git a/modules/thing/src/main/java/com/thing/util/TenantSubsetUtil.java b/modules/thing/src/main/java/com/thing/util/TenantSubsetUtil.java new file mode 100644 index 0000000..6e7b5b4 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/util/TenantSubsetUtil.java @@ -0,0 +1,72 @@ +package com.thing.util; + +import cn.hutool.core.collection.CollectionUtil; + +import com.mybatisflex.core.query.QueryWrapper; +import com.thing.common.core.enumeration.SuperAdminEnum; +import com.thing.sys.security.context.TenantContext; +import com.thing.sys.security.domain.SecurityUser; +import com.thing.sys.security.domain.UserDetail; +import com.thing.sys.tenant.entity.SysTenantGroupEntity; +import com.thing.sys.tenant.mapper.SysTenantGroupMapper; + +import lombok.RequiredArgsConstructor; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Component +@RequiredArgsConstructor +public class TenantSubsetUtil { + private final SysTenantGroupMapper baseDao; + + /** + * 父租户code获取子列表 + * + * @param parentCode 父租户code + * @return 子租户列表 + */ + public List getChildren(Long parentCode) { + if (parentCode == null) { + return new ArrayList<>(); + } + QueryWrapper queryWrapper = new QueryWrapper() + .eq(SysTenantGroupEntity::getParentCode, parentCode); + List list = baseDao.selectListByQuery(queryWrapper); + if (CollectionUtil.isEmpty(list)) { + return new ArrayList<>(); + } + return list.parallelStream().map(SysTenantGroupEntity::getCode).distinct().collect(Collectors.toList()); + } + + + /** + * 不为超管 或 超管切换租户时,查询自己及下属 + * @param includeTenant 是否包含当前租户 + */ + public List paramsAddTenantCodeList( Boolean includeTenant) { + List tenantCodeList = new ArrayList<>(); + UserDetail userDetail = SecurityUser.getUser(); + Long tenantCode = TenantContext.getTenantCode(userDetail); + if(!Objects.equals(userDetail.getSuperAdmin(), SuperAdminEnum.YES.value()) + || !Objects.equals(tenantCode, userDetail.getTenantCode())) { + //tenantCodeList = getChildren(tenantCode); + if(includeTenant) { + tenantCodeList.add(tenantCode); + } + //保证查不到数据 + if(CollectionUtil.isEmpty(tenantCodeList)) { + tenantCodeList.add(-1L); + } + }else { + tenantCodeList.add(tenantCode); + } + return tenantCodeList; + } + + +} diff --git a/modules/thing/src/main/java/com/thing/util/ToolUtil.java b/modules/thing/src/main/java/com/thing/util/ToolUtil.java new file mode 100644 index 0000000..2d4a9ad --- /dev/null +++ b/modules/thing/src/main/java/com/thing/util/ToolUtil.java @@ -0,0 +1,169 @@ +package com.thing.util; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; + +import java.text.NumberFormat; +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +public class ToolUtil { + + /** + * + * @param divisor + * @param dividend + * @return divisor/dividend + */ + public static double getRate(Integer divisor,Integer dividend){ + // 创建一个数值格式化对象 + NumberFormat numberFormat = NumberFormat.getInstance(); + numberFormat.setMaximumFractionDigits(2); + if(null==dividend||0==dividend){ + return 0.00; + } + if(null==divisor ||0==divisor){ + return 0.00; + } + String result = null; + try { + result = numberFormat.format((double)divisor/(double)dividend * 100); + } catch (Exception e) { + result="0.00"; + } + try { + return Double.valueOf(result); + } catch (NumberFormatException e) { + return 100.00; + } + } + + /** + * 输入时分秒,转换为毫秒戳值 时分秒以英文逗号分割 + * @param timeStr + * @return + */ + public static Long formartMilliSeconds (String timeStr){ + List intList = Arrays.asList(timeStr.split(",")).stream() + .map(Integer::valueOf) + .collect(Collectors.toList()); + return Duration.ofDays(intList.get(0)).plusHours(intList.get(1)).plusMinutes(intList.get(2)).toMillis(); + } + + + /** + * 获取数据集中,最大值的数据,jsonArray入参 json回参 + * @param dataArray jsonArray入参 + * @param handleKey 计算字段 val=数值最大 ts=最新时间的 + * @return + */ + public static JSONObject getMaxVal(JSONArray dataArray,String handleKey){ + + return dataArray.parallelStream() + .map(JSONObject.class::cast) + .max(Comparator.comparingDouble(obj -> obj.getDouble(handleKey))) + .orElse(new JSONObject()); + } + + /** + * 获取数据集中,最小值的数据,jsonArray入参 json回参 + * @param dataArray jsonArray入参 + * @param handleKey 计算字段 + * @return + */ + public static JSONObject getMimVal(JSONArray dataArray,String handleKey){ + return dataArray.parallelStream() + .map(JSONObject.class::cast) + .min(Comparator.comparingDouble(obj -> obj.getDouble(handleKey))) + .orElse(new JSONObject()); + } + + + /** + * 获取数据集中,平均值,jsonArray入参 json回参, + * 并且封装一个新的json返回,里面包含入参的所有字段,方便后续业务处理 + * @param dataArray jsonArray入参 + * @param handleKey 计算字段 + * @return + */ + public static JSONObject getAvgVal(JSONArray dataArray,String handleKey){ + + double avgVal = dataArray.parallelStream() + .mapToDouble(data -> Double.parseDouble(((JSONObject) data).getString(handleKey))) + .average() + .orElse(Double.NaN); + NumberFormat nf = NumberFormat.getNumberInstance(); + //保留小数点后两位 + nf.setMaximumFractionDigits(2); + String avgValStr = nf.format(avgVal); + + JSONObject object = dataArray.getJSONObject(0); + object.put("val",avgValStr); + return object; + } + + + /** + * 获取数据集中,总和,jsonArray入参 json回参, + * 并且封装一个新的json返回,里面包含入参的所有字段,方便后续业务处理 + * @param dataArray jsonArray入参 + * @param handleKey 计算字段 + * @return + */ + public static JSONObject getSumVal(JSONArray dataArray,String handleKey){ + double sumVal = dataArray.parallelStream() + .mapToDouble(data -> Double.parseDouble(((JSONObject) data).getString(handleKey))) + .sum(); + NumberFormat nf = NumberFormat.getNumberInstance(); + //保留小数点后两位 + nf.setMaximumFractionDigits(2); + String sumValStr = nf.format(sumVal); + JSONObject object = dataArray.getJSONObject(0); + object.put("val",sumValStr); + return object; + } + + /** + * 获取数据集中 符合时间范围的数据 + * @param dataArray 数据集 + * @param handleKey 计算比较字段的key + * @param startTime 开始时间,毫秒戳 + * @param endTime 结束时间,毫秒戳 + * @return + */ + public static List filteredData(JSONArray dataArray,String handleKey,Long startTime,Long endTime){ + return dataArray.parallelStream() + .map(data -> (JSONObject) data) + .filter(data -> { + long ts = data.getLongValue(handleKey); + return ts >= startTime && ts <= endTime; + }) + .collect(Collectors.toList()); + } + + + /** + * 根据类型 和处理字段,对数据集合进行聚合处理 + * @param dataArray + * @param handleKey + * @param type + * @return + */ + public static JSONObject handleByPolymerizationType(JSONArray dataArray,String handleKey,String type) { + switch (type) { + case "1": + return ToolUtil.getMaxVal(dataArray, handleKey); + case "2": + return ToolUtil.getMimVal(dataArray, handleKey); + case "3": + return ToolUtil.getAvgVal(dataArray, handleKey); + case "4": + return ToolUtil.getSumVal(dataArray, handleKey); + default: + return ToolUtil.getMaxVal(dataArray, "ts"); + + } + } + +} diff --git a/modules/thing/src/main/java/com/thing/websocket/ExtensionSocketEventListener.java b/modules/thing/src/main/java/com/thing/websocket/ExtensionSocketEventListener.java new file mode 100644 index 0000000..1dba3eb --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/ExtensionSocketEventListener.java @@ -0,0 +1,42 @@ +package com.thing.websocket; + +import cn.hutool.core.collection.CollectionUtil; + +import com.alibaba.fastjson.JSONObject; +import com.thing.common.core.event.ExtensionSocketEvent; +import com.thing.common.core.message.MessageData; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-12-08 + **/ +@Slf4j +@Component +@RequiredArgsConstructor +public class ExtensionSocketEventListener { + private final WebSocketServer webSocketServer; + + @EventListener(ExtensionSocketEvent.class) + public void onSocketServerEvent(ExtensionSocketEvent event) { + List msgList = event.getMsgList(); + if (CollectionUtil.isEmpty(msgList)) { + return; + } + WebSocketServer.TRANSPORT_EXTEND_MAP.forEach((sessionId, list) -> { + List collect = msgList.parallelStream() + .filter(msg -> list.contains(msg.getLong("id"))) + .collect(Collectors.toList()); + if (CollectionUtil.isNotEmpty(collect)) { + webSocketServer.sendMessage(sessionId, new MessageData<>().data(collect)); + } + }); + } +} diff --git a/modules/thing/src/main/java/com/thing/websocket/QueueSocketEventListener.java b/modules/thing/src/main/java/com/thing/websocket/QueueSocketEventListener.java new file mode 100644 index 0000000..9a14574 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/QueueSocketEventListener.java @@ -0,0 +1,152 @@ +package com.thing.websocket; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; + +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import com.thing.common.core.event.QueueSocketEvent; +import com.thing.common.core.message.MessageData; +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import com.thing.websocket.data.SocketDataCache; + +import lombok.extern.slf4j.Slf4j; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * @author zhenghh. 2022-10-21 + **/ +@Slf4j +@Component +public class QueueSocketEventListener { + + @Autowired + private WebSocketServer webSocketServer; + + @Autowired + private WebSocketDashboardServer webSocketDashboardServer; + + @Autowired + private TskvService tsKvService; + + @EventListener(QueueSocketEvent.class) + public void onBoardEvent(QueueSocketEvent event) { + List list = event.getList(); + //socket推送的最新值 + Map> lastAttrValMap = list.parallelStream().collect(Collectors.groupingBy(QueueMsgDTO::getThingCode)); + //超级API + if (MapUtil.isNotEmpty(webSocketServer.API_SOCKET_MAP)) { + webSocketServer.API_SOCKET_MAP.forEach((sessionId, thingList) -> { + JsonObject jsonObject = getApiMap(lastAttrValMap, thingList); + if (jsonObject != null && !jsonObject.isJsonNull() && !jsonObject.entrySet().isEmpty()) { + webSocketServer.sendMessage(sessionId, new MessageData<>().data(jsonObject)); + } + }); + } + //看板 + if (MapUtil.isNotEmpty(webSocketServer.DASHBOARD_MAP)) { + webSocketServer.DASHBOARD_MAP.forEach((sessionId, thingList) -> { + if(CollectionUtil.isNotEmpty(thingList)){ + List dtoList = new ArrayList<>(); + thingList.forEach(thing -> { + //获取设备对应数据 + List msgList = lastAttrValMap.get(thing.getThingCode()); + if(CollectionUtil.isNotEmpty(msgList)){ + dtoList.addAll(msgList.stream().filter(m -> thing.getThingAttrList().contains(m.getAttrKey())).collect(Collectors.toList())); + } + }); + if (CollectionUtil.isNotEmpty(dtoList)) { + webSocketServer.sendMessage(sessionId, new MessageData<>().data(dtoList)); + } + } + }); + } + + //匿名看板 + if (CollectionUtil.isNotEmpty(webSocketDashboardServer.DASHBOARD_MAP)) { + //数据组装 + webSocketDashboardServer.DASHBOARD_MAP.forEach((sessionId, thingList) -> { + List finalList = getFinalList(lastAttrValMap, thingList); + if (CollectionUtil.isNotEmpty(finalList)) { + webSocketDashboardServer.sendMessage(sessionId, new MessageData<>().data(finalList)); + } + }); + } + //} catch (Exception e) { + // log.error("看板websocket推送失败: {}", e.getMessage()); + //} + } + + private List getFinalList(Map> lastAttrValMap, List thingList) { + /*return thingList.stream().flatMap(item -> { + List queueMsgDTOList = lastAttrValMap.get(item.getThingCode()); + if (CollectionUtil.isNotEmpty(queueMsgDTOList)) { + List attrList = item.getThingAttrList(); + return queueMsgDTOList.stream().filter(dto -> attrList.contains(dto.getAttrKey())); + } + return Stream.empty(); + }).collect(Collectors.toList());*/ + List param = new ArrayList<>(); + thingList.forEach(temp -> { + ThingAttrDTO dto = new ThingAttrDTO(temp.getThingId(), temp.getThingCode(), temp.getThingAttrList()); + param.add(dto); + }); + List tsKvDTOS = tsKvService.getLastTimeSeries(param); + List dtos = new ArrayList<>(); + tsKvDTOS.forEach(temp -> { + QueueMsgDTO dto = new QueueMsgDTO(temp.getThingCode(), temp.getThingAttr(), temp.getTs(), temp.getVal(), null, null, null, null); + dtos.add(dto); + }); + return dtos; + } + + private final Gson gson = new Gson(); + private final Type listType = new TypeToken>() {}.getType(); + + private JsonObject getApiMap(Map> lastAttrValMap, JsonObject jsonObject) { + // 使用 Gson 将第一个 JsonObject 转换为 JsonElement + JsonElement jsonElement = gson.toJsonTree(jsonObject); + // 将 JsonElement 转换为第二个 JsonObject + JsonObject dataJson = jsonElement.getAsJsonObject(); + JsonObject result = dataJson.get("result").getAsJsonObject(); + JsonObject info = result.get("info").getAsJsonObject(); + List queueMsgDTOList = Lists.newArrayList(); + for (String thingCode : info.keySet()) { + JsonObject object = info.get(thingCode).getAsJsonObject(); + Set thingAttrList = object.get("attrs").getAsJsonObject().keySet(); + List queueMsgDTOS = lastAttrValMap.get(thingCode); + if(CollectionUtil.isNotEmpty(thingAttrList) && CollectionUtil.isNotEmpty(queueMsgDTOS)){ + List collect = queueMsgDTOS.stream() + .filter(s -> thingAttrList.contains(s.getAttrKey()) && StringUtils.equals(thingCode, s.getThingCode())) + .collect(Collectors.toList()); + queueMsgDTOList.addAll(collect); + } + } + if(CollectionUtil.isEmpty(queueMsgDTOList)){ + return new JsonObject(); + } + // 使用 TypeToken 获取 List 的类型 + // Type listType = new TypeToken>() {}.getType(); + // 将 List 转换为 JsonElement + JsonElement jsonElement1 = gson.toJsonTree(queueMsgDTOList, listType); + result.add("values",jsonElement1); + return dataJson; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/websocket/WebSocketDashboardServer.java b/modules/thing/src/main/java/com/thing/websocket/WebSocketDashboardServer.java new file mode 100644 index 0000000..b532b77 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/WebSocketDashboardServer.java @@ -0,0 +1,116 @@ +package com.thing.websocket; + +import cn.hutool.core.util.ObjectUtil; +import com.alibaba.fastjson.JSON; +import com.google.common.collect.Lists; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.message.MessageData; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.data.tskv.TsKvDTO; +import com.thing.common.tskv.service.TsKvService; +import com.thing.dashboard.dto.IotDashboardElementDTO; +import com.thing.dashboard.service.IotDashboardElementService; +import com.thing.websocket.config.WebSocketConfig; +import com.thing.websocket.data.SocketDataCache; +import com.thing.websocket.data.WebSocketDashboardData; +import jakarta.websocket.*; +import jakarta.websocket.server.ServerEndpoint; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * WebSocket服务 + * + * @author Mark sunlightcs@gmail.com + */ +@Slf4j +@Component +@ServerEndpoint(value = "/dashboard/websocket", configurator = WebSocketConfig.class) +public class WebSocketDashboardServer { + /** + * 客户端连接信息 + */ + private static Map servers = new ConcurrentHashMap<>(); + + public static final Map> DASHBOARD_MAP = new ConcurrentHashMap<>(); + + @OnOpen + public void open(Session session) { + String dashboardId = (String) session.getUserProperties().get(Constant.DASHBOARD_KEY); + servers.put(session.getId(), new WebSocketDashboardData(dashboardId, session)); + //看板全局变量存储 + if (StringUtils.isNotBlank(dashboardId)) { + //加载节点配置 + IotDashboardElementService dashboardElementService = SpringContextUtils.getBean(IotDashboardElementService.class); + List elementList = dashboardElementService.getElementByDashboardId(Long.parseLong(dashboardId)); + if (ObjectUtil.isNull(elementList)) { + sendMessage(session.getId(), new MessageData<>().data("看板配置为空,请检查看板配置")); + return; + } + List list = Lists.newArrayList(); + elementList.stream() + .collect(Collectors.groupingBy(IotDashboardElementDTO::getThingCode, Collectors.mapping(IotDashboardElementDTO::getAttrCode, Collectors.toList()))) + .forEach((thingCode, attList) -> list.add(new SocketDataCache(thingCode, attList))); + DASHBOARD_MAP.put(session.getId(), list); + //推送一次最新数据 + TsKvService tsKvService = SpringContextUtils.getBean(TsKvService.class); + Map> thingAttrParams = elementList.stream() + .collect(Collectors.groupingBy(IotDashboardElementDTO::getThingCode, + Collectors.mapping(IotDashboardElementDTO::getAttrCode, Collectors.toCollection(ArrayList::new)))); + List lastList = tsKvService.findLatestByMultiMap(thingAttrParams, false); + sendMessage(session.getId(), new MessageData<>().data(lastList)); + log.debug("预览看板websocket连接:dashboardId---" + dashboardId + ",-----sessionId:" + session.getId()); + } + } + + @OnClose + public void onClose(Session session) { + //客户端断开连接 + servers.remove(session.getId()); + DASHBOARD_MAP.remove(session.getId()); + log.debug("websocket close, session id:" + session.getId()); + } + + @OnError + public void onError(Session session, Throwable throwable) { + servers.remove(session.getId()); + DASHBOARD_MAP.remove(session.getId()); + log.error(throwable.getMessage(), throwable); + log.debug("看板websocket连接:sessionId:" + session.getId()); + } + + @OnMessage + public void onMessage(Session session, String msg) { + log.info("session id: " + session.getId() + ", message:" + msg); + } + + /** + * 发送信息 + * + * @param sessionId sessionId + * @param message 消息内容 + */ + public void sendMessage(String sessionId, MessageData message) { + WebSocketDashboardData webSocketDashboardData = servers.get(sessionId); + if (webSocketDashboardData != null && webSocketDashboardData.getSession() != null) { + sendMessage(webSocketDashboardData.getSession(), message); + } + } + + public void sendMessage(Session session, MessageData message) { + try { + session.getBasicRemote().sendText(JSON.toJSONString(message)); + } catch (IOException e) { + log.error("send message error," + e.getMessage(), e); + } + } +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/websocket/WebSocketServer.java b/modules/thing/src/main/java/com/thing/websocket/WebSocketServer.java new file mode 100644 index 0000000..0ae00b9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/WebSocketServer.java @@ -0,0 +1,298 @@ +package com.thing.websocket; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.Lists; +import com.google.gson.Gson; +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.thing.common.core.constants.Constant; +import com.thing.common.core.message.MessageData; +import com.thing.common.core.utils.SpringContextUtils; +import com.thing.common.data.dto.QueueMsgDTO; +import com.thing.dashboard.dto.IotDashboardElementDTO; +import com.thing.dashboard.service.IotDashboardElementService; +import com.thing.extend.dto.TransportExtendCalculationDTO; +import com.thing.extend.service.TransportExtendCalculationService; +import com.thing.thing.api.service.IotThingApiService; +import com.thing.thing.tskv.dto.ThingAttrDTO; +import com.thing.thing.tskv.dto.TsKvDTO; +import com.thing.thing.tskv.service.TskvService; +import com.thing.websocket.config.WebSocketConfig; +import com.thing.websocket.data.SocketDataCache; +import com.thing.websocket.data.WebSocketData; +import jakarta.websocket.*; +import jakarta.websocket.server.ServerEndpoint; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * WebSocket服务 + * + * @author Mark sunlightcs@gmail.com + */ +@Slf4j +@Component +@ServerEndpoint(value = "/websocket", configurator = WebSocketConfig.class) +public class WebSocketServer { + /** + * 客户端连接信息 + */ + private static Map servers = new ConcurrentHashMap<>(); + + public static final Map> TRANSPORT_EXTEND_MAP = new ConcurrentHashMap<>(); + + public static final Map> DASHBOARD_MAP = new ConcurrentHashMap<>(); + + public static final Map API_SOCKET_MAP = new ConcurrentHashMap<>(); + + private static final ByteBuffer PING_MSG = ByteBuffer.wrap(new byte[]{}); + + private TransportExtendCalculationService extendCalculationService; + + private IotThingApiService iotThingApiService; + + private TskvService tsKvService; + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @OnOpen + public void open(Session session) { + + Object auth = session.getUserProperties().get(Constant.AUTH); + //登录 + if(ObjectUtil.equals(auth,"0")){ + Long userId = (Long) session.getUserProperties().get(Constant.USER_KEY); + servers.put(session.getId(), new WebSocketData(userId, session,session.getId())); + }else{ + servers.put(session.getId(), new WebSocketData(0L, session,session.getId())); + } + //通讯配置 + String transportExtendId = (String) session.getUserProperties().get(Constant.TRANSPORT_EXTEND_ID); + if (StringUtils.isNotBlank(transportExtendId)) { + extendCalculationService = SpringContextUtils.getBean(TransportExtendCalculationService.class); + loadTransportExtendList(session.getId(), transportExtendId); + } + //api全局变量存储 + String apiId = (String) session.getUserProperties().get(Constant.API_ID); + if (StringUtils.isNotBlank(apiId)) { + //加载bean + iotThingApiService = SpringContextUtils.getBean(IotThingApiService.class); + //加载节点配置 + loadApiList(session, apiId); + + } + + //看板全局变量存储 + String dashboardId = (String) session.getUserProperties().get(Constant.DASHBOARD_KEY); + if (StringUtils.isNotBlank(dashboardId)) { + log.info("初始化看板全局变量存储:" + dashboardId); + tsKvService= SpringContextUtils.getBean(TskvService.class); + //加载节点配置 + IotDashboardElementService dashboardElementService = SpringContextUtils.getBean(IotDashboardElementService.class); + List elementList = dashboardElementService.getElementByDashboardId(Long.parseLong(dashboardId)); + if (ObjectUtil.isNull(elementList)) { + sendMessage(session.getId(), new MessageData<>().data("看板配置为空,请检查看板配置")); + return; + } + List list = Lists.newArrayList(); + + Map> groupedElements = elementList.stream() + .collect(Collectors.groupingBy(IotDashboardElementDTO::getThingCode)); + List attrCodeList = new ArrayList<>(); + groupedElements.forEach((thingCode, elements) -> { + SocketDataCache cache = new SocketDataCache(); + cache.setThingCode(thingCode); + //提取 集合中attrcode 为新的集合 + for (IotDashboardElementDTO element : elements) { + attrCodeList.add(element.getAttrCode()); + cache.setThingId(element.getThingId()); + } + cache.setThingAttrList(attrCodeList); + list.add(cache); + }); + + DASHBOARD_MAP.put(session.getId(), list); + //推送一次最新数据 + //IDeviceDataFacade deviceDataFacade = SpringContextUtils.getBean(IDeviceDataFacade.class); + + List param = new ArrayList<>(); + list.forEach(temp->{ + ThingAttrDTO dto = new ThingAttrDTO(temp.getThingId(),temp.getThingCode(),temp.getThingAttrList()); + param.add(dto); + }); + List tsKvDTOS = tsKvService.getLastTimeSeries(param); + List dtos= new ArrayList<>(); + tsKvDTOS.forEach(temp->{ + QueueMsgDTO dto = new QueueMsgDTO(temp.getThingCode(),temp.getThingAttr(),temp.getTs(),temp.getVal(),null,null,null,null); + dtos.add(dto); + }); + + //List lastList = deviceDataFacade.getLastTimeseries(thingAttrParams); + sendMessage(session.getId(), new MessageData<>().data(dtos)); + log.info("----websocket连接成功----看板dashboardId:{}--------sessionId:{}", dashboardId, session.getId()); + } + } + + @OnClose + public void onClose(Session session) { + if (session != null) { + //客户端断开连接 + servers.remove(session.getId()); + API_SOCKET_MAP.remove(session.getId()); + TRANSPORT_EXTEND_MAP.remove(session.getId()); + DASHBOARD_MAP.remove(session.getId()); + try { + session.close(); + log.error("------websocket---连接断开------关闭-----sessionId:{}", session.getId()); + } catch (IOException e) { + log.error("------websocket-------连接关闭异常---------sessionId:{}-----{}", session.getId(), e.getMessage()); + throw new RuntimeException(e); + } + } + } + + @OnError + public void onError(Session session, Throwable throwable) { + if (session != null) { + servers.remove(session.getId()); + API_SOCKET_MAP.remove(session.getId()); + TRANSPORT_EXTEND_MAP.remove(session.getId()); + DASHBOARD_MAP.remove(session.getId()); + try { + session.close(); + log.error("------websocket---通讯异常-------关闭-----sessionId:{}", session.getId()); + } catch (IOException e) { + log.error("------websocket-------通讯关闭异常---------sessionId:{}-----{}", session.getId(), e.getMessage()); + } + } + } + + @OnMessage + public void onMessage(Session session, String msg) { + log.info("------websocket---接收消息---sessionId:{}-----{}", session.getId(),msg); + } + + /** + * 发送信息 + * + * @param userIdList 用户ID列表 + * @param message 消息内容 + */ + public void sendMessage(List userIdList, MessageData message) { + userIdList.forEach(userId -> sendMessage(userId, message)); + } + + /** + * 发送信息 + * + * @param sessionId sessionId + * @param message 消息内容 + */ + public void sendMessage(String sessionId, MessageData message) { + WebSocketData webSocketData = servers.get(sessionId); + if (webSocketData != null && webSocketData.getSession() != null) { + sendMessage(webSocketData.getSession(), message); + } + } + + /** + * 发送信息 + * + * @param userId 用户ID + * @param message 消息内容 + */ + public void sendMessage(Long userId, MessageData message) { + servers.values().forEach(info -> { + if (userId.equals(info.getUserId())) { + sendMessage(info.getSession(), message); + } + }); + } + + /** + * 发送信息给全部用户 + * + * @param message 消息内容 + */ + public void sendMessageAll(MessageData message) { + servers.values().forEach(info -> sendMessage(info.getSession(), message)); + } + + public void sendMessage(Session session, MessageData message) { + if (session == null || !session.isOpen()) { + return; + } + try { + session.getAsyncRemote().sendPing(PING_MSG); + } catch (IOException e) { + log.error("----sessionID:{}---------WebSocket---网络异常----->{}", session.getId(), e.getMessage()); + return; + } + session.getAsyncRemote().sendText(new Gson().toJson(message)); + } + + + private void loadApiList(Session session, String apiId) { + Map apiMap = iotThingApiService.websocketApi(Long.parseLong(apiId)); + if (MapUtil.isNotEmpty(apiMap)) { + try { + String jsonString = objectMapper.writeValueAsString(apiMap); + JsonNode jsonNode = objectMapper.readTree(jsonString); + convertValuesToString(jsonNode); + + JsonObject jsonObject = JsonParser.parseString(jsonNode.toString()).getAsJsonObject(); + //推送一次最新数据 + sendMessage(session,new MessageData<>().data(jsonObject)); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + } + Map result = MapUtil.get(apiMap, "result", Map.class); + result.remove("values"); + apiMap.put("result",result); + // 创建一个 Gson 对象 + Gson gson = new Gson(); + // 将 Map 转换为 JsonObject + JsonObject jsonObject = gson.toJsonTree(apiMap).getAsJsonObject(); + //存储一个超级API需要哪些物和相应的属性 + API_SOCKET_MAP.put(session.getId(),jsonObject); + } + + private void loadTransportExtendList(String sessionId, String transportExtendId) { + List list = extendCalculationService.selectListByConfigId(Long.parseLong(transportExtendId)); + List collect = list.parallelStream().map(TransportExtendCalculationDTO::getId).collect(Collectors.toList()); + TRANSPORT_EXTEND_MAP.put(sessionId, collect); + } + + public static void convertValuesToString(JsonNode jsonNode) { + if (jsonNode.isObject()) { + ObjectNode objectNode = (ObjectNode) jsonNode; + Iterator> iterator = objectNode.fields(); + while (iterator.hasNext()) { + Map.Entry entry = iterator.next(); + convertValuesToString(entry.getValue()); + if (entry.getValue().isValueNode()) { + objectNode.put(entry.getKey(), entry.getValue().asText()); + } + } + } else if (jsonNode.isArray()) { + jsonNode.forEach(element -> convertValuesToString(element)); + } + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/websocket/config/WebSocketConfig.java b/modules/thing/src/main/java/com/thing/websocket/config/WebSocketConfig.java new file mode 100644 index 0000000..3e0b6e9 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/config/WebSocketConfig.java @@ -0,0 +1,52 @@ +package com.thing.websocket.config; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; + +import com.thing.common.core.constants.Constant; +import com.thing.sys.security.domain.SecurityUser; + +import jakarta.websocket.HandshakeResponse; +import jakarta.websocket.server.HandshakeRequest; +import jakarta.websocket.server.ServerEndpointConfig; +import jakarta.websocket.server.ServerEndpointConfig.Configurator; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +import java.util.List; +import java.util.Map; + +/** + * WebSocket配置 + * + * @author Mark sunlightcs@gmail.com + */ +@Configuration +public class WebSocketConfig extends Configurator { + @Bean + public ServerEndpointExporter serverEndpointExporter() { + return new ServerEndpointExporter(); + } + + @Override + public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { + if (ObjectUtil.isNotNull(SecurityUser.getUserId())) { + sec.getUserProperties().put(Constant.USER_KEY, SecurityUser.getUserId()); + } + Map> parameterMap = request.getParameterMap(); + + if (CollectionUtil.isNotEmpty(parameterMap.get(Constant.API_ID))) { + sec.getUserProperties().put(Constant.API_ID, parameterMap.get(Constant.API_ID).get(0)); + } + + if (CollectionUtil.isNotEmpty(parameterMap.get(Constant.TRANSPORT_EXTEND_ID))) { + sec.getUserProperties().put(Constant.TRANSPORT_EXTEND_ID, parameterMap.get(Constant.TRANSPORT_EXTEND_ID).get(0)); + } + if (CollectionUtil.isNotEmpty(parameterMap.get(Constant.DASHBOARD_KEY))) { + sec.getUserProperties().put(Constant.DASHBOARD_KEY, parameterMap.get(Constant.DASHBOARD_KEY).get(0)); + } + } + +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/websocket/data/SocketDataCache.java b/modules/thing/src/main/java/com/thing/websocket/data/SocketDataCache.java new file mode 100644 index 0000000..d2fd49f --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/data/SocketDataCache.java @@ -0,0 +1,34 @@ +package com.thing.websocket.data; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * @author zhenghh. 2023-03-14 + **/ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class SocketDataCache { + /** + * 物主键 + */ + private Long thingId; + /** + * 物编码 + */ + private String thingCode; + /** + * 属性编码 + */ + private List thingAttrList; + + + public SocketDataCache(String thingCode, List thingAttrList) { + this.thingCode = thingCode; + this.thingAttrList = thingAttrList; + } +} diff --git a/modules/thing/src/main/java/com/thing/websocket/data/WebSocketDashboardData.java b/modules/thing/src/main/java/com/thing/websocket/data/WebSocketDashboardData.java new file mode 100644 index 0000000..1560bc2 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/data/WebSocketDashboardData.java @@ -0,0 +1,18 @@ +package com.thing.websocket.data; + +import jakarta.websocket.Session; + +import lombok.AllArgsConstructor; +import lombok.Data; + +/** + * WebSocket连接数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@AllArgsConstructor +public class WebSocketDashboardData { + private String dashboardId; + private Session session; +} \ No newline at end of file diff --git a/modules/thing/src/main/java/com/thing/websocket/data/WebSocketData.java b/modules/thing/src/main/java/com/thing/websocket/data/WebSocketData.java new file mode 100644 index 0000000..16a1d82 --- /dev/null +++ b/modules/thing/src/main/java/com/thing/websocket/data/WebSocketData.java @@ -0,0 +1,34 @@ +package com.thing.websocket.data; + +import jakarta.websocket.Session; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * WebSocket连接数据 + * + * @author Mark sunlightcs@gmail.com + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class WebSocketData { + + private Long userId; + + private Session session; + + private String sessionId; + + public WebSocketData(Long userId, Session session) { + this.userId = userId; + this.session = session; + } + + public WebSocketData(Session session, String sessionId) { + this.session = session; + this.sessionId = sessionId; + } +} \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/api/IotThingApiLogMapper.xml b/modules/thing/src/main/resources/mapper/api/IotThingApiLogMapper.xml new file mode 100644 index 0000000..88c6d07 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/api/IotThingApiLogMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/api/IotThingApiMapper.xml b/modules/thing/src/main/resources/mapper/api/IotThingApiMapper.xml new file mode 100644 index 0000000..e7ae8b9 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/api/IotThingApiMapper.xml @@ -0,0 +1,11 @@ + + + + + + + + update iot_thing_api set calls_num = #{callsNum} where id = #{id} + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigItemMapper.xml b/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigItemMapper.xml new file mode 100644 index 0000000..d7b045b --- /dev/null +++ b/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigItemMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + update iot_thing_biz_config_item + set item_value = + case item_name + + when #{item.itemName} then #{item.itemValue} + + end + where config_id = #{configId} + and item_name in + + #{item.itemName} + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigMapper.xml b/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigMapper.xml new file mode 100644 index 0000000..5e2bab1 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/bizconfig/IotThingBizConfigMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/control/IotDeviceControlLogMapper.xml b/modules/thing/src/main/resources/mapper/control/IotDeviceControlLogMapper.xml new file mode 100644 index 0000000..5de83db --- /dev/null +++ b/modules/thing/src/main/resources/mapper/control/IotDeviceControlLogMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/control/IotDeviceControlMapper.xml b/modules/thing/src/main/resources/mapper/control/IotDeviceControlMapper.xml new file mode 100644 index 0000000..0c45041 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/control/IotDeviceControlMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/dashboard/IotDashboardElementMapper.xml b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardElementMapper.xml new file mode 100644 index 0000000..40196af --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardElementMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/dashboard/IotDashboardGroupMapper.xml b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardGroupMapper.xml new file mode 100644 index 0000000..6da43b0 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardGroupMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/dashboard/IotDashboardMapper.xml b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardMapper.xml new file mode 100644 index 0000000..c67cfa7 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/dashboard/IotDashboardSvgMapper.xml b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardSvgMapper.xml new file mode 100644 index 0000000..1d6a602 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dashboard/IotDashboardSvgMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/device/IotThingMenuConfigMapper.xml b/modules/thing/src/main/resources/mapper/device/IotThingMenuConfigMapper.xml new file mode 100644 index 0000000..c35407a --- /dev/null +++ b/modules/thing/src/main/resources/mapper/device/IotThingMenuConfigMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/device/IotThingSourceMapper.xml b/modules/thing/src/main/resources/mapper/device/IotThingSourceMapper.xml new file mode 100644 index 0000000..f23b943 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/device/IotThingSourceMapper.xml @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/dict/IotThingDictMapper.xml b/modules/thing/src/main/resources/mapper/dict/IotThingDictMapper.xml new file mode 100644 index 0000000..ec82ff0 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dict/IotThingDictMapper.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/dict/IotThingDictRelationMapper.xml b/modules/thing/src/main/resources/mapper/dict/IotThingDictRelationMapper.xml new file mode 100644 index 0000000..f251657 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/dict/IotThingDictRelationMapper.xml @@ -0,0 +1,303 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/ext/CommonConfigMapper.xml b/modules/thing/src/main/resources/mapper/ext/CommonConfigMapper.xml new file mode 100644 index 0000000..5c7574d --- /dev/null +++ b/modules/thing/src/main/resources/mapper/ext/CommonConfigMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/extend/TransportExtendCalculationMapper.xml b/modules/thing/src/main/resources/mapper/extend/TransportExtendCalculationMapper.xml new file mode 100644 index 0000000..3151f97 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/extend/TransportExtendCalculationMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/extend/TransportExtendMapper.xml b/modules/thing/src/main/resources/mapper/extend/TransportExtendMapper.xml new file mode 100644 index 0000000..a258260 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/extend/TransportExtendMapper.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/extend/TransportExtendMsgMapper.xml b/modules/thing/src/main/resources/mapper/extend/TransportExtendMsgMapper.xml new file mode 100644 index 0000000..4b53b5e --- /dev/null +++ b/modules/thing/src/main/resources/mapper/extend/TransportExtendMsgMapper.xml @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/group/IotGroupInfoMapper.xml b/modules/thing/src/main/resources/mapper/group/IotGroupInfoMapper.xml new file mode 100644 index 0000000..dd802f9 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/group/IotGroupInfoMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/group/IotGroupRelationMapper.xml b/modules/thing/src/main/resources/mapper/group/IotGroupRelationMapper.xml new file mode 100644 index 0000000..c84dde3 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/group/IotGroupRelationMapper.xml @@ -0,0 +1,137 @@ + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/model/IotThingEntityMapper.xml b/modules/thing/src/main/resources/mapper/model/IotThingEntityMapper.xml new file mode 100644 index 0000000..11d4759b --- /dev/null +++ b/modules/thing/src/main/resources/mapper/model/IotThingEntityMapper.xml @@ -0,0 +1,21 @@ + + + + + + + + INSERT INTO iot_thing_entity + (id, code, name, real_type, template_mark, type, enable_status, remark, extend_data, img, + tenant_code, company_id, dept_id, creator, create_date, updater, update_date) + VALUES + + (#{item.id},#{item.code},#{item.name},#{item.realType},#{item.templateMark},#{item.type},#{item.enableStatus},#{item.remark},#{item.extendData},#{item.img}, + #{item.tenantCode},#{item.companyId},#{item.deptId},#{item.creator},#{item.createDate},#{item.updater},#{item.updateDate}) + + ON CONFLICT (code, tenant_code) + DO NOTHING + + diff --git a/modules/thing/src/main/resources/mapper/model/IotThingModelMapper.xml b/modules/thing/src/main/resources/mapper/model/IotThingModelMapper.xml new file mode 100644 index 0000000..537e3cd --- /dev/null +++ b/modules/thing/src/main/resources/mapper/model/IotThingModelMapper.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/modules/thing/src/main/resources/mapper/relation/IotThingRelationDetailMapper.xml b/modules/thing/src/main/resources/mapper/relation/IotThingRelationDetailMapper.xml new file mode 100644 index 0000000..6082707 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/relation/IotThingRelationDetailMapper.xml @@ -0,0 +1,107 @@ + + + + + + update iot_thing_relation_detail SET sort = sort + 1 + where sort = ]]> #{toSort} and sort #{fromSort} and root_id = #{rootId} and from_id= #{fromId} + + + update iot_thing_relation_detail SET sort = sort - 1 where sort #{toSort} and sort ]]> #{fromSort} and root_id = #{rootId} and from_id=#{fromId} + + + + + + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/relation/IotThingRelationRootMapper.xml b/modules/thing/src/main/resources/mapper/relation/IotThingRelationRootMapper.xml new file mode 100644 index 0000000..d19ddb6 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/relation/IotThingRelationRootMapper.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + UPDATE iot_thing_relation_root SET sort = sort + 1 + WHERE sort = ]]> #{toSort} and sort #{fromSort} + + and tenant_code = #{tenantCode} + + + and company_id = #{companyId} + + + and dept_id = #{deptId} + + + + UPDATE iot_thing_relation_root SET sort = sort - 1 + WHERE sort ]]> #{fromSort} and sort #{toSort} + + and tenant_code = #{tenantCode} + + + and company_id = #{companyId} + + + and dept_id = #{deptId} + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysDeptMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysDeptMapper.xml new file mode 100644 index 0000000..10cf1a6 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysDeptMapper.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysDictDataMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysDictDataMapper.xml new file mode 100644 index 0000000..eb21bb3 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysDictDataMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysDictTypeMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysDictTypeMapper.xml new file mode 100644 index 0000000..0676b60 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysDictTypeMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysIndustryTypeMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysIndustryTypeMapper.xml new file mode 100644 index 0000000..f049a23 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysIndustryTypeMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysLanguageMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysLanguageMapper.xml new file mode 100644 index 0000000..2bacdc2 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysLanguageMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + delete from sys_language where table_name=#{tableName} and table_id=#{tableId} + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysMenuMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysMenuMapper.xml new file mode 100644 index 0000000..ea47cd3 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysMenuMapper.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysParamsMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysParamsMapper.xml new file mode 100644 index 0000000..b8f2bfb --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysParamsMapper.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + update sys_params set param_value = #{paramValue} where param_code = #{paramCode} + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysPostMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysPostMapper.xml new file mode 100644 index 0000000..a1a524b --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysPostMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysRegionMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysRegionMapper.xml new file mode 100644 index 0000000..f46ca17 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysRegionMapper.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysRoleDataScopeMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysRoleDataScopeMapper.xml new file mode 100644 index 0000000..e51d913 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysRoleDataScopeMapper.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + delete from sys_role_data_scope where role_id in + + #{roleId} + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysRoleMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysRoleMapper.xml new file mode 100644 index 0000000..54fb9d9 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysRoleMapper.xml @@ -0,0 +1,9 @@ + + + + + + + insert into sys_role(id, name, company_id, default_role, remark, creator, create_date)values(#{id}, #{name}, #{companyId}, #{defaultRole}, #{remark}, #{creator}, now()) + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysRoleMenuMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysRoleMenuMapper.xml new file mode 100644 index 0000000..c4861ca --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysRoleMenuMapper.xml @@ -0,0 +1,27 @@ + + + + + + + + + delete from sys_role_menu where role_id in + + #{roleId} + + + + + delete from sys_role_menu where menu_id = #{value} + + + + delete from sys_role_menu where menu_id in + + #{menuId} + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysRoleUserMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysRoleUserMapper.xml new file mode 100644 index 0000000..f1d49d3 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysRoleUserMapper.xml @@ -0,0 +1,56 @@ + + + + + + + delete from sys_role_user where role_id in + + #{roleId} + + + + + delete from sys_role_user where user_id in + + #{userId} + + + + + + + + + + + + + + + + + delete from sys_role_user + where 1=1 + + AND role_id in + + #{roleId} + + + and user_id =#{userId} + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysTenantDetailMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysTenantDetailMapper.xml new file mode 100644 index 0000000..25aff67 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysTenantDetailMapper.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysTenantGroupMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysTenantGroupMapper.xml new file mode 100644 index 0000000..4556a85 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysTenantGroupMapper.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysTenantMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysTenantMapper.xml new file mode 100644 index 0000000..dfad9de --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysTenantMapper.xml @@ -0,0 +1,158 @@ + + + + + + insert into public_org(id,dept_id,org_name,tenant_code,creator,create_date) values (#{id},#{deptId},#{orgName},#{tenantCode},#{updater},#{updateDate}) + + + + + + + + + + + + + + update sys_tenant set del_flag = 1 where id in + + #{id} + + + + update sys_tenant + set status = #{status} + where id in + + #{id} + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysUserMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysUserMapper.xml new file mode 100644 index 0000000..80597cc --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysUserMapper.xml @@ -0,0 +1,87 @@ + + + + + + + + + + + + + update sys_user set password = #{newPassword} where id = #{id} + + + update sys_user + set status = #{status} + where id in + + #{id} + + + + + + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/SysUserMenuMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysUserMenuMapper.xml new file mode 100644 index 0000000..78e7ece --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysUserMenuMapper.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysUserPostMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysUserPostMapper.xml new file mode 100644 index 0000000..bacb01b --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysUserPostMapper.xml @@ -0,0 +1,24 @@ + + + + + + + delete from sys_user_post where post_id in + + #{postId} + + + + + delete from sys_user_post where user_id in + + #{userId} + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/SysUserTokenMapper.xml b/modules/thing/src/main/resources/mapper/sys/SysUserTokenMapper.xml new file mode 100644 index 0000000..10bd42b --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/SysUserTokenMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + update sys_user_token set expire_date = #{expireDate} where user_id = #{userId} + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/message/SysMailLogMapper.xml b/modules/thing/src/main/resources/mapper/sys/message/SysMailLogMapper.xml new file mode 100644 index 0000000..012bc51 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/message/SysMailLogMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/message/SysMailTemplateMapper.xml b/modules/thing/src/main/resources/mapper/sys/message/SysMailTemplateMapper.xml new file mode 100644 index 0000000..178c8b5 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/message/SysMailTemplateMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + diff --git a/modules/thing/src/main/resources/mapper/sys/message/SysSmsLogMapper.xml b/modules/thing/src/main/resources/mapper/sys/message/SysSmsLogMapper.xml new file mode 100644 index 0000000..1b4d02c --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/message/SysSmsLogMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/message/SysSmsMapper.xml b/modules/thing/src/main/resources/mapper/sys/message/SysSmsMapper.xml new file mode 100644 index 0000000..a2e7f76 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/message/SysSmsMapper.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeMapper.xml b/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeMapper.xml new file mode 100644 index 0000000..48f8964 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeUserMapper.xml b/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeUserMapper.xml new file mode 100644 index 0000000..0ab87b0 --- /dev/null +++ b/modules/thing/src/main/resources/mapper/sys/notice/SysNoticeUserMapper.xml @@ -0,0 +1,14 @@ + + + + + + insert into sys_notice_user(notice_id, receiver_id, read_status) + select #{noticeId}, t1.id, #{readStatus} from sys_user t1 + + + + + \ No newline at end of file