> messages, Acknowledgment ack) { ... }
* }
- *
+ *
* Batch size, prefetch and the retry policy bind from configuration
* ({@code dc3.data.point.batch.*}), not annotation literals.
*
@@ -77,7 +77,7 @@ public @interface Dc3Listener {
/**
* @return consumer group / per-instance queue suffix, empty for the platform-shared
- * destination (drivers set their client id programmatically)
+ * destination (drivers set their client id programmatically)
*/
String group() default "";
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java
index 635fa93f0..a80430c88 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/config/MqAutoConfiguration.java
@@ -44,7 +44,9 @@ import java.util.Objects;
@EnableConfigurationProperties(BatchConsumerProperties.class)
public class MqAutoConfiguration {
- /** Publishing facade over the active adapter. */
+ /**
+ * Publishing facade over the active adapter.
+ */
@Bean
@ConditionalOnMissingBean(MessageSender.class)
public MessageSender messageSender(ObjectProvider adapterProvider) {
@@ -64,7 +66,9 @@ public class MqAutoConfiguration {
return new MessageSenderImpl(adapter);
}
- /** Registers beans carrying @Dc3Listener methods with the active adapter. */
+ /**
+ * Registers beans carrying @Dc3Listener methods with the active adapter.
+ */
@Bean
@ConditionalOnMissingBean
public Dc3ListenerProcessor dc3ListenerProcessor(ObjectProvider adapterProvider) {
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java
index 6e42f757e..1a244a06a 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/Dc3ListenerProcessor.java
@@ -98,7 +98,7 @@ public class Dc3ListenerProcessor implements SmartInitializingSingleton, Applica
return listenerMethodCache.computeIfAbsent(targetClass, clazz -> {
List methods = new ArrayList<>();
for (Class> current = clazz; Objects.nonNull(current) && current != Object.class;
- current = current.getSuperclass()) {
+ current = current.getSuperclass()) {
for (Method method : current.getDeclaredMethods()) {
if (method.isAnnotationPresent(Dc3Listener.class) && !Modifier.isStatic(method.getModifiers())) {
method.setAccessible(true);
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java
index 98f1d9494..b877e92b6 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/core/EnvelopeCodec.java
@@ -77,9 +77,9 @@ public final class EnvelopeCodec {
* header is informational; the subscription's declared type wins, which also keeps
* pre-migration messages (carrying only the legacy type header) consumable.
*
- * @param delivery the raw delivery
- * @param payloadType the declared payload type
- * @param payload type
+ * @param delivery the raw delivery
+ * @param payloadType the declared payload type
+ * @param payload type
* @return the deserialized payload
*/
public static T deserialize(WireMqDelivery delivery, Class payloadType) {
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java
index 97cef0a66..6eea66bab 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/message/MqMessage.java
@@ -63,7 +63,9 @@ public class MqMessage {
@Builder.Default
private final Duration delay = Duration.ZERO;
- /** Build a message for a topic with an explicit partition key (ordered streams). */
+ /**
+ * Build a message for a topic with an explicit partition key (ordered streams).
+ */
public static MqMessage of(MqTopic topic, String partitionKey, Object payload) {
return MqMessage.builder()
.topic(topic)
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java
index 447ca0ec5..b6201708c 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/RetryPolicy.java
@@ -21,10 +21,10 @@ package io.github.pnoker.common.mq.subscription;
* Bounded redelivery with exponential backoff; exhaustion routes to the dead-letter
* instead of dropping. Defaults mirror the point-value batch consumer configuration.
*
- * @param maxAttempts maximum delivery attempts before dead-lettering
- * @param initialBackoffMillis first retry delay
- * @param multiplier backoff multiplier
- * @param maxBackoffMillis backoff ceiling
+ * @param maxAttempts maximum delivery attempts before dead-lettering
+ * @param initialBackoffMillis first retry delay
+ * @param multiplier backoff multiplier
+ * @param maxBackoffMillis backoff ceiling
* @author pnoker
* @since 2026.8.19
*/
diff --git a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java
index 0a97b6fbd..50b7ac203 100644
--- a/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java
+++ b/dc3-mq/dc3-mq-core/src/main/java/io/github/pnoker/common/mq/subscription/SubscriptionSpec.java
@@ -28,16 +28,16 @@ import java.time.Duration;
* Subscription declaration — replaces {@code @RabbitListener} plus the container-factory
* choice. Physical destinations are derived by the adapter from topic + mode + keyPattern.
*
- * @param topic logical destination
- * @param mode load-balanced or broadcast
- * @param profile latency/throughput tuning preset
- * @param delivery single or batch delivery
- * @param keyPattern subscription key filter relative to the topic (empty = topic
- * default), e.g. {@code "driver.*"} on STATE vs {@code "device.*"}
- * @param group consumer group / per-instance queue suffix (drivers use their
- * client id); empty = platform-shared destination
- * @param instanceTtl per-instance queue/subscription expiry, null = never expire
- * @param payloadType type the listener expects
+ * @param topic logical destination
+ * @param mode load-balanced or broadcast
+ * @param profile latency/throughput tuning preset
+ * @param delivery single or batch delivery
+ * @param keyPattern subscription key filter relative to the topic (empty = topic
+ * default), e.g. {@code "driver.*"} on STATE vs {@code "device.*"}
+ * @param group consumer group / per-instance queue suffix (drivers use their
+ * client id); empty = platform-shared destination
+ * @param instanceTtl per-instance queue/subscription expiry, null = never expire
+ * @param payloadType type the listener expects
* @param deadLetterEnabled whether rejects route to the topic's dead-letter
* @author pnoker
* @since 2026.8.19
diff --git a/dc3-mq/dc3-mq-kafka/README.md b/dc3-mq/dc3-mq-kafka/README.md
index 1c6ff4853..65f10cd43 100644
--- a/dc3-mq/dc3-mq-kafka/README.md
+++ b/dc3-mq/dc3-mq-kafka/README.md
@@ -10,8 +10,8 @@ Active when `dc3.mq.type=kafka`.
## Configuration
-| Key | Default | Meaning |
-|---|---|---|
+| Key | Default | Meaning |
+|----------------------------------|----------------------------------------------------------------------------------------|-------------|
| `dc3.mq.kafka.bootstrap-servers` | `DC3_MQ_KAFKA_BOOTSTRAP`, then `spring.kafka.bootstrap-servers`, then `localhost:9092` | broker list |
## Dependencies
diff --git a/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java b/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java
index a749f2b16..934615f70 100644
--- a/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java
+++ b/dc3-mq/dc3-mq-kafka/src/main/java/io/github/pnoker/common/mq/kafka/config/KafkaMqAdapterConfiguration.java
@@ -40,7 +40,9 @@ import java.util.Map;
@ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "kafka")
public class KafkaMqAdapterConfiguration {
- /** Producer template on the adapter bootstrap servers; overridable by a user bean. */
+ /**
+ * Producer template on the adapter bootstrap servers; overridable by a user bean.
+ */
@Bean
@ConditionalOnMissingBean(KafkaTemplate.class)
public KafkaTemplate kafkaMqTemplate(
@@ -48,7 +50,9 @@ public class KafkaMqAdapterConfiguration {
return KafkaMqAdapter.template(bootstrapServers);
}
- /** The port adapter bound to the Kafka template. */
+ /**
+ * The port adapter bound to the Kafka template.
+ */
@Bean
public KafkaMqAdapter kafkaMqAdapter(KafkaTemplate kafkaTemplate,
@Value("${dc3.mq.kafka.bootstrap-servers:${DC3_MQ_KAFKA_BOOTSTRAP:${spring.kafka.bootstrap-servers:localhost:9092}}}")
diff --git a/dc3-mq/dc3-mq-mqtt/README.md b/dc3-mq/dc3-mq-mqtt/README.md
index 0328c6120..d6fe8c04d 100644
--- a/dc3-mq/dc3-mq-mqtt/README.md
+++ b/dc3-mq/dc3-mq-mqtt/README.md
@@ -1,7 +1,7 @@
# DC3 MQ MQTT
-`dc3-mq-mqtt` adapts the broker-neutral port to MQTT 5 (`hivemq-mqtt-client`), compatible with EMQX, HiveMQ,
-NanoMQ, and other MQTT 5 brokers.
+`dc3-mq-mqtt` adapts the broker-neutral port to MQTT 5 (`hivemq-mqtt-client`), compatible with EMQX, HiveMQ, NanoMQ, and
+other MQTT 5 brokers.
## Activation
@@ -9,10 +9,10 @@ Active when `dc3.mq.type=mqtt`.
## Configuration
-| Key | Default | Meaning |
-|---|---|---|
+| Key | Default | Meaning |
+|--------------------|-------------|-------------|
| `dc3.mq.mqtt.host` | `localhost` | broker host |
-| `dc3.mq.mqtt.port` | `1883` | broker port |
+| `dc3.mq.mqtt.port` | `1883` | broker port |
## Dependencies
@@ -26,8 +26,7 @@ mvn -s .mvn/settings.xml -pl dc3-mq/dc3-mq-mqtt -am package
## Testing
-No module-specific tests; behaviour is verified by `MqttContractTest` in `dc3-mq-tck` (disposable HiveMQ CE
-container).
+No module-specific tests; behaviour is verified by `MqttContractTest` in `dc3-mq-tck` (disposable HiveMQ CE container).
## Related Modules
diff --git a/dc3-mq/dc3-mq-mqtt/pom.xml b/dc3-mq/dc3-mq-mqtt/pom.xml
index 22d3d2af5..c290fa7d8 100644
--- a/dc3-mq/dc3-mq-mqtt/pom.xml
+++ b/dc3-mq/dc3-mq-mqtt/pom.xml
@@ -31,7 +31,8 @@
2026.5.22
jar
- IoT DC3 MQTT 5 adapter (EMQX / HiveMQ / NanoMQ / ...) for the broker-neutral messaging port
+ IoT DC3 MQTT 5 adapter (EMQX / HiveMQ / NanoMQ / ...) for the broker-neutral messaging port
+
diff --git a/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java b/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java
index 815d74f8b..40748e3fd 100644
--- a/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java
+++ b/dc3-mq/dc3-mq-mqtt/src/main/java/io/github/pnoker/common/mq/mqtt/config/MqttMqAdapterConfiguration.java
@@ -36,7 +36,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "mqtt")
public class MqttMqAdapterConfiguration {
- /** The port adapter over the HiveMQ MQTT5 client. */
+ /**
+ * The port adapter over the HiveMQ MQTT5 client.
+ */
@Bean
public MqttMqAdapter mqttMqAdapter(@Value("${dc3.mq.mqtt.host:localhost}") String host,
@Value("${dc3.mq.mqtt.port:1883}") int port,
diff --git a/dc3-mq/dc3-mq-pulsar/README.md b/dc3-mq/dc3-mq-pulsar/README.md
index 6dc467867..cf7f2caef 100644
--- a/dc3-mq/dc3-mq-pulsar/README.md
+++ b/dc3-mq/dc3-mq-pulsar/README.md
@@ -1,7 +1,7 @@
# DC3 MQ Pulsar
-`dc3-mq-pulsar` adapts the broker-neutral port to Apache Pulsar (`pulsar-client`). Logical topics map to Pulsar
-topics; publish and subscription use the standard client APIs with the port's confirmation model.
+`dc3-mq-pulsar` adapts the broker-neutral port to Apache Pulsar (`pulsar-client`). Logical topics map to Pulsar topics;
+publish and subscription use the standard client APIs with the port's confirmation model.
## Activation
@@ -9,8 +9,8 @@ Active when `dc3.mq.type=pulsar`.
## Configuration
-| Key | Default | Meaning |
-|---|---|---|
+| Key | Default | Meaning |
+|-----------------------------|---------------------------|--------------------|
| `dc3.mq.pulsar.service-url` | `pulsar://localhost:6650` | broker service URL |
## Dependencies
diff --git a/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java b/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java
index 4839e2ea0..a5b7afe60 100644
--- a/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java
+++ b/dc3-mq/dc3-mq-pulsar/src/main/java/io/github/pnoker/common/mq/pulsar/config/PulsarMqAdapterConfiguration.java
@@ -38,7 +38,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "pulsar")
public class PulsarMqAdapterConfiguration {
- /** Shared Pulsar client on the service url. */
+ /**
+ * Shared Pulsar client on the service url.
+ */
@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(PulsarClient.class)
public PulsarClient pulsarClient(
@@ -47,7 +49,9 @@ public class PulsarMqAdapterConfiguration {
return PulsarClient.builder().serviceUrl(serviceUrl).build();
}
- /** The port adapter bound to the shared client. */
+ /**
+ * The port adapter bound to the shared client.
+ */
@Bean
public PulsarMqAdapter pulsarMqAdapter(PulsarClient pulsarClient, BatchConsumerProperties batchProperties) {
return new PulsarMqAdapter(pulsarClient, batchProperties);
diff --git a/dc3-mq/dc3-mq-rabbitmq/README.md b/dc3-mq/dc3-mq-rabbitmq/README.md
index a3f332fb9..3381ba350 100644
--- a/dc3-mq/dc3-mq-rabbitmq/README.md
+++ b/dc3-mq/dc3-mq-rabbitmq/README.md
@@ -10,12 +10,12 @@ Active when `dc3.mq.type=rabbitmq` — the default (`matchIfMissing = true`).
## Key types
-| Type | Role |
-|---|---|
-| `RabbitMqAdapter` | `BrokerAdapter` implementation (exchanges, queues, listeners, confirms) |
-| `RabbitNames` / `RabbitTopology` | canonical exchange/queue/routing names and bindings |
-| `RabbitAcknowledgment` | publisher-confirm handling |
-| `ActiveRabbitProfileConfig` / `RabbitEnvironmentConfig` | profile wiring and environment defaults |
+| Type | Role |
+|---------------------------------------------------------|-------------------------------------------------------------------------|
+| `RabbitMqAdapter` | `BrokerAdapter` implementation (exchanges, queues, listeners, confirms) |
+| `RabbitNames` / `RabbitTopology` | canonical exchange/queue/routing names and bindings |
+| `RabbitAcknowledgment` | publisher-confirm handling |
+| `ActiveRabbitProfileConfig` / `RabbitEnvironmentConfig` | profile wiring and environment defaults |
## Configuration
diff --git a/dc3-mq/dc3-mq-rabbitmq/pom.xml b/dc3-mq/dc3-mq-rabbitmq/pom.xml
index 17e475ca4..7c26d5915 100644
--- a/dc3-mq/dc3-mq-rabbitmq/pom.xml
+++ b/dc3-mq/dc3-mq-rabbitmq/pom.xml
@@ -31,7 +31,9 @@
2026.5.22
jar
- IoT DC3 RabbitMQ adapter for the broker-neutral messaging port; physical topology is byte-for-byte identical to the pre-port layout
+ IoT DC3 RabbitMQ adapter for the broker-neutral messaging port; physical topology is byte-for-byte
+ identical to the pre-port layout
+
diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java
index 7512244cb..5a3c975a7 100644
--- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java
+++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitAcknowledgment.java
@@ -43,12 +43,16 @@ public final class RabbitAcknowledgment implements Acknowledgment {
this.multiple = multiple;
}
- /** Ack exactly one delivery ({@code multiple=false}). */
+ /**
+ * Ack exactly one delivery ({@code multiple=false}).
+ */
public static RabbitAcknowledgment single(Channel channel, long deliveryTag) {
return new RabbitAcknowledgment(channel, deliveryTag, false);
}
- /** Ack everything up to the tag ({@code multiple=true}) — the broker-batch commit path. */
+ /**
+ * Ack everything up to the tag ({@code multiple=true}) — the broker-batch commit path.
+ */
public static RabbitAcknowledgment batch(Channel channel, long lastDeliveryTag) {
return new RabbitAcknowledgment(channel, lastDeliveryTag, true);
}
diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java
index b68905a4b..9678b7d41 100644
--- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java
+++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/RabbitTopology.java
@@ -55,12 +55,12 @@ public final class RabbitTopology {
/**
* Descriptor of a platform-shared queue.
*
- * @param queueName queue name
- * @param exchangeName source exchange
- * @param routingKey binding routing key (pattern)
- * @param ttlMillis per-queue message TTL, 0 = none
- * @param deadExchange dead-letter exchange, null = none
- * @param deadRouting dead-letter routing key
+ * @param queueName queue name
+ * @param exchangeName source exchange
+ * @param routingKey binding routing key (pattern)
+ * @param ttlMillis per-queue message TTL, 0 = none
+ * @param deadExchange dead-letter exchange, null = none
+ * @param deadRouting dead-letter routing key
* @param bindingArgument whether the binding carries the x-auto-delete argument
*/
private record SharedQueue(String queueName, String exchangeName, String routingKey, int ttlMillis,
@@ -147,8 +147,8 @@ public final class RabbitTopology {
/**
* Declare the driver-side metadata broadcast queue (auto-delete, 30 s TTL).
*
- * @param admin rabbit admin
- * @param client driver client id
+ * @param admin rabbit admin
+ * @param client driver client id
* @param routingKey exact routing key (service name)
*/
public static void declareMetadataQueue(RabbitAdmin admin, String client, String routingKey) {
diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java
index bef62b3d1..6f842d2b3 100644
--- a/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java
+++ b/dc3-mq/dc3-mq-rabbitmq/src/main/java/io/github/pnoker/common/mq/rabbit/config/RabbitMqAdapterConfiguration.java
@@ -47,14 +47,18 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "rabbitmq", matchIfMissing = true)
public class RabbitMqAdapterConfiguration {
- /** JSON converter with typed envelope headers. */
+ /**
+ * JSON converter with typed envelope headers.
+ */
@Bean
@ConditionalOnMissingBean
public MessageConverter messageConverter() {
return new JacksonJsonMessageConverter(JsonUtil.getJsonMapper());
}
- /** Publisher-confirms template with mandatory returns. */
+ /**
+ * Publisher-confirms template with mandatory returns.
+ */
@Bean(name = "rabbitTemplate")
@ConditionalOnMissingBean(RabbitTemplate.class)
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
@@ -81,14 +85,18 @@ public class RabbitMqAdapterConfiguration {
return rabbitTemplate;
}
- /** Declares queues/exchanges/bindings at startup. */
+ /**
+ * Declares queues/exchanges/bindings at startup.
+ */
@Bean
@ConditionalOnMissingBean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
- /** The port adapter bound to the template and admin. */
+ /**
+ * The port adapter bound to the template and admin.
+ */
@Bean
public RabbitMqAdapter rabbitMqAdapter(RabbitTemplate rabbitTemplate, RabbitAdmin rabbitAdmin,
ConnectionFactory connectionFactory, BatchConsumerProperties batchProperties,
diff --git a/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories b/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories
index 1f1315989..41d168881 100644
--- a/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories
+++ b/dc3-mq/dc3-mq-rabbitmq/src/main/resources/META-INF/spring.factories
@@ -14,7 +14,6 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
#
-
org.springframework.boot.env.EnvironmentPostProcessor=\
io.github.pnoker.common.mq.rabbit.config.ActiveRabbitProfileConfig,\
io.github.pnoker.common.mq.rabbit.config.RabbitEnvironmentConfig
diff --git a/dc3-mq/dc3-mq-rocketmq/README.md b/dc3-mq/dc3-mq-rocketmq/README.md
index 3a62ed5eb..15cb96721 100644
--- a/dc3-mq/dc3-mq-rocketmq/README.md
+++ b/dc3-mq/dc3-mq-rocketmq/README.md
@@ -1,7 +1,7 @@
# DC3 MQ RocketMQ
-`dc3-mq-rocketmq` adapts the broker-neutral port to Apache RocketMQ (`rocketmq-client`). Logical topics map to
-RocketMQ topics; publish and subscription use the standard producer/consumer APIs with the port's confirmation model.
+`dc3-mq-rocketmq` adapts the broker-neutral port to Apache RocketMQ (`rocketmq-client`). Logical topics map to RocketMQ
+topics; publish and subscription use the standard producer/consumer APIs with the port's confirmation model.
## Activation
@@ -9,8 +9,8 @@ Active when `dc3.mq.type=rocketmq`.
## Configuration
-| Key | Default | Meaning |
-|---|---|---|
+| Key | Default | Meaning |
+|---------------------------------------|------------------|--------------------|
| `dc3.mq.rocketmq.name-server-address` | `localhost:9876` | NameServer address |
## Dependencies
diff --git a/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java b/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java
index f720a72f7..30ee10502 100644
--- a/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java
+++ b/dc3-mq/dc3-mq-rocketmq/src/main/java/io/github/pnoker/common/mq/rocketmq/config/RocketMqAdapterConfiguration.java
@@ -35,7 +35,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.mq", name = "type", havingValue = "rocketmq")
public class RocketMqAdapterConfiguration {
- /** The port adapter bound to the RocketMQ producer/consumer. */
+ /**
+ * The port adapter bound to the RocketMQ producer/consumer.
+ */
@Bean
public RocketMqAdapter rocketMqAdapter(
@Value("${dc3.mq.rocketmq.name-server-address:localhost:9876}") String namesrvAddr,
diff --git a/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java b/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java
index aae538c55..20ee775b4 100644
--- a/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java
+++ b/dc3-mq/dc3-mq-rocketmq/src/test/java/io/github/pnoker/common/mq/rocketmq/RocketMqFreshGroupProbe.java
@@ -72,7 +72,7 @@ class RocketMqFreshGroupProbe {
private List subscribe(String group) {
List received = new CopyOnWriteArrayList<>();
adapter.subscribe(new SubscriptionSpec(MqTopic.EVENT, SubscriptionMode.LOAD_BALANCE,
- ConsumptionProfile.LATENCY, DeliveryMode.SINGLE, "", group, null, String.class, true),
+ ConsumptionProfile.LATENCY, DeliveryMode.SINGLE, "", group, null, String.class, true),
delivery -> {
received.add(new String(delivery.body(), java.nio.charset.StandardCharsets.UTF_8));
delivery.acknowledgment().ack();
diff --git a/dc3-mq/dc3-mq-tck/README.md b/dc3-mq/dc3-mq-tck/README.md
index 2178cf59b..07650f267 100644
--- a/dc3-mq/dc3-mq-tck/README.md
+++ b/dc3-mq/dc3-mq-tck/README.md
@@ -7,14 +7,14 @@ Testcontainers container.
## Contract tests
-| Test | Broker |
-|---|---|
+| Test | Broker |
+|------------------------|----------------------------------------------|
| `RabbitMqContractTest` | RabbitMQ (`rabbitmq:3.13-management-alpine`) |
-| `KafkaContractTest` | Apache Kafka (`apache/kafka:3.9.0`) |
-| `RocketMqContractTest` | RocketMQ |
-| `PulsarContractTest` | Pulsar |
-| `ActiveMqContractTest` | ActiveMQ |
-| `MqttContractTest` | HiveMQ CE |
+| `KafkaContractTest` | Apache Kafka (`apache/kafka:3.9.0`) |
+| `RocketMqContractTest` | RocketMQ |
+| `PulsarContractTest` | Pulsar |
+| `ActiveMqContractTest` | ActiveMQ |
+| `MqttContractTest` | HiveMQ CE |
## Running
diff --git a/dc3-tsdb/README.md b/dc3-tsdb/README.md
index a6e5d5ddd..5a05ff9c5 100644
--- a/dc3-tsdb/README.md
+++ b/dc3-tsdb/README.md
@@ -1,19 +1,19 @@
# DC3 TSDB
-dc3-tsdb is the pluggable time-series storage layer of IoT DC3. It defines a store-neutral port — the TsdbStore SPI
-with a sample model and capability set — plus one adapter per supported time-series database. The Data Center writes
-point values through the port and never through store-specific classes.
+dc3-tsdb is the pluggable time-series storage layer of IoT DC3. It defines a store-neutral port — the TsdbStore SPI with
+a sample model and capability set — plus one adapter per supported time-series database. The Data Center writes point
+values through the port and never through store-specific classes.
## Modules
-| Module | Role |
-|---|---|
-| dc3-tsdb-core | store-neutral port: TsdbStore SPI, TsdbModel sample model, capabilities; zero store dependencies |
-| dc3-tsdb-timescale | TimescaleDB adapter (default; embedded or standalone PostgreSQL) |
-| dc3-tsdb-tdengine | TDengine adapter — supertable + per-series subtables over the REST/WS JDBC driver |
-| dc3-tsdb-influxdb | InfluxDB 3 adapter — tags/fields over the documented v3 HTTP SQL and line-protocol APIs |
-| dc3-tsdb-iotdb | Apache IoTDB adapter — tree paths root.dc3.* over the session API |
-| dc3-tsdb-tck | store-neutral contract suite: an adapter that passes these tests is compliant |
+| Module | Role |
+|--------------------|--------------------------------------------------------------------------------------------------|
+| dc3-tsdb-core | store-neutral port: TsdbStore SPI, TsdbModel sample model, capabilities; zero store dependencies |
+| dc3-tsdb-timescale | TimescaleDB adapter (default; embedded or standalone PostgreSQL) |
+| dc3-tsdb-tdengine | TDengine adapter — supertable + per-series subtables over the REST/WS JDBC driver |
+| dc3-tsdb-influxdb | InfluxDB 3 adapter — tags/fields over the documented v3 HTTP SQL and line-protocol APIs |
+| dc3-tsdb-iotdb | Apache IoTDB adapter — tree paths root.dc3.* over the session API |
+| dc3-tsdb-tck | store-neutral contract suite: an adapter that passes these tests is compliant |
## Selection
diff --git a/dc3-tsdb/dc3-tsdb-core/README.md b/dc3-tsdb/dc3-tsdb-core/README.md
index 9f8d53f57..b128a535a 100644
--- a/dc3-tsdb/dc3-tsdb-core/README.md
+++ b/dc3-tsdb/dc3-tsdb-core/README.md
@@ -6,10 +6,10 @@ store-specific classes. The module has zero store dependencies.
## Key types
-| Type | Role |
-|---|---|
+| Type | Role |
+|-------------|-----------------------------------------------------------------------------------|
| `TsdbStore` | SPI implemented by every time-series adapter (write, query, schema, capabilities) |
-| `TsdbModel` | sample model shared across adapters |
+| `TsdbModel` | sample model shared across adapters |
## Build Instructions
diff --git a/dc3-tsdb/dc3-tsdb-core/pom.xml b/dc3-tsdb/dc3-tsdb-core/pom.xml
index 9445b05b5..d41df91a2 100644
--- a/dc3-tsdb/dc3-tsdb-core/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-core/pom.xml
@@ -31,6 +31,8 @@
2026.5.22
jar
- IoT DC3 store-neutral time-series port: sample model, TsdbStore SPI, capabilities. Zero store dependencies
+ IoT DC3 store-neutral time-series port: sample model, TsdbStore SPI, capabilities. Zero store
+ dependencies
+
diff --git a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java
index 814d4bd2e..c1f064002 100644
--- a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java
+++ b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/model/TsdbModel.java
@@ -155,21 +155,37 @@ public final class TsdbModel {
* form the M4 rendering quadruple with MIN/MAX; PERCENTILE is capability-gated.
*/
public enum AggregateFunction {
- /** Arithmetic mean over the window. */
+ /**
+ * Arithmetic mean over the window.
+ */
AVG,
- /** Minimum value in the window. */
+ /**
+ * Minimum value in the window.
+ */
MIN,
- /** Maximum value in the window. */
+ /**
+ * Maximum value in the window.
+ */
MAX,
- /** Sum over the window. */
+ /**
+ * Sum over the window.
+ */
SUM,
- /** Row count over the window. */
+ /**
+ * Row count over the window.
+ */
COUNT,
- /** First sample in the window. */
+ /**
+ * First sample in the window.
+ */
FIRST,
- /** Last sample in the window. */
+ /**
+ * Last sample in the window.
+ */
LAST,
- /** Percentile, p supplied per call and capability-gated. */
+ /**
+ * Percentile, p supplied per call and capability-gated.
+ */
PERCENTILE
}
@@ -180,7 +196,9 @@ public final class TsdbModel {
* @param toExclusive exclusive end
*/
public record TimeWindow(Instant from, Instant toExclusive) {
- /** Rejects empty or reversed windows. */
+ /**
+ * Rejects empty or reversed windows.
+ */
public TimeWindow {
if (!from.isBefore(toExclusive)) {
throw new IllegalArgumentException("window from must be before toExclusive");
@@ -253,11 +271,17 @@ public final class TsdbModel {
* S13-② grouping dimensions (the dashboard's whitelisted set).
*/
public enum GroupDimension {
- /** Group by device. */
+ /**
+ * Group by device.
+ */
DEVICE,
- /** Group by point. */
+ /**
+ * Group by point.
+ */
POINT,
- /** Group by driver. */
+ /**
+ * Group by driver.
+ */
DRIVER
}
@@ -283,7 +307,7 @@ public final class TsdbModel {
/**
* S19 aligned-bucket Pearson correlation.
*
- * @param pearson correlation coefficient in [-1,1]
+ * @param pearson correlation coefficient in [-1,1]
* @param alignedBuckets buckets used after alignment
*/
public record CorrelationResult(double pearson, long alignedBuckets) {
@@ -307,7 +331,9 @@ public final class TsdbModel {
}
}
- /** S18/S6 read timeout signal — the port's runaway-scan guard. */
+ /**
+ * S18/S6 read timeout signal — the port's runaway-scan guard.
+ */
public static final class TsdbQueryTimeout extends RuntimeException {
/**
diff --git a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java
index 81ad3bb9f..0323ad308 100644
--- a/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java
+++ b/dc3-tsdb/dc3-tsdb-core/src/main/java/io/github/pnoker/common/tsdb/spi/TsdbStore.java
@@ -249,18 +249,18 @@ public interface TsdbStore {
* Adapter capability declaration (§8 of the design). The startup negotiation log
* prints this row, mirroring the MQ port.
*
- * @param gapFill zero-fill empty buckets
- * @param tenantWideScan series-empty history/aggregate/count/last
- * @param tenantWideAnalytics S13 facet
- * @param latencyHistogram S13-④ store-side
- * @param percentile S15 PERCENTILE
- * @param rollupSupport S16 tiered-rollup mode
- * @param maxAppendBatch S18 chunking threshold
- * @param deleteRange S10
- * @param ordering NONE | PER_SERIES
- * @param precision native timestamp precision
- * @param backfill out-of-order/late writes accepted
- * @param correlation S19 store-side correlation
+ * @param gapFill zero-fill empty buckets
+ * @param tenantWideScan series-empty history/aggregate/count/last
+ * @param tenantWideAnalytics S13 facet
+ * @param latencyHistogram S13-④ store-side
+ * @param percentile S15 PERCENTILE
+ * @param rollupSupport S16 tiered-rollup mode
+ * @param maxAppendBatch S18 chunking threshold
+ * @param deleteRange S10
+ * @param ordering NONE | PER_SERIES
+ * @param precision native timestamp precision
+ * @param backfill out-of-order/late writes accepted
+ * @param correlation S19 store-side correlation
*/
record TsdbCapabilities(
boolean gapFill,
@@ -277,31 +277,53 @@ public interface TsdbStore {
boolean correlation) {
}
- /** S16 tiered-rollup support levels. */
+ /**
+ * S16 tiered-rollup support levels.
+ */
enum RollupSupport {
- /** Store-side rollup tiers. */
+ /**
+ * Store-side rollup tiers.
+ */
NATIVE,
- /** Rollup maintained by the platform on top of the store. */
+ /**
+ * Rollup maintained by the platform on top of the store.
+ */
MANUAL,
- /** No rollup support. */
+ /**
+ * No rollup support.
+ */
NONE
}
- /** S2/S8 result ordering guarantees. */
+ /**
+ * S2/S8 result ordering guarantees.
+ */
enum OrderingGuarantee {
- /** No ordering guarantee. */
+ /**
+ * No ordering guarantee.
+ */
NONE,
- /** Samples ordered within each series. */
+ /**
+ * Samples ordered within each series.
+ */
PER_SERIES
}
- /** Native timestamp precision of the store. */
+ /**
+ * Native timestamp precision of the store.
+ */
enum Precision {
- /** Microsecond precision. */
+ /**
+ * Microsecond precision.
+ */
MICRO,
- /** Millisecond precision. */
+ /**
+ * Millisecond precision.
+ */
MILLI,
- /** Nanosecond precision. */
+ /**
+ * Nanosecond precision.
+ */
NANO
}
}
diff --git a/dc3-tsdb/dc3-tsdb-influxdb/README.md b/dc3-tsdb/dc3-tsdb-influxdb/README.md
index 94121af26..9064ac669 100644
--- a/dc3-tsdb/dc3-tsdb-influxdb/README.md
+++ b/dc3-tsdb/dc3-tsdb-influxdb/README.md
@@ -1,7 +1,7 @@
# DC3 TSDB InfluxDB
-`dc3-tsdb-influxdb` adapts the store-neutral port to InfluxDB 3 over the documented v3 HTTP SQL and line-protocol
-APIs. Points map to tag/field columns; no InfluxDB-specific JDBC or client SDK is required.
+`dc3-tsdb-influxdb` adapts the store-neutral port to InfluxDB 3 over the documented v3 HTTP SQL and line-protocol APIs.
+Points map to tag/field columns; no InfluxDB-specific JDBC or client SDK is required.
## Activation
@@ -11,11 +11,11 @@ Active when `dc3.tsdb.type=influxdb`.
`InfluxdbTsdbProperties` binds the `dc3.tsdb.influxdb` prefix:
-| Key | Default | Meaning |
-|---|---|---|
-| `dc3.tsdb.influxdb.url` | `http://localhost:8181` | InfluxDB 3 HTTP endpoint |
-| `dc3.tsdb.influxdb.token` | *(empty)* | auth token |
-| `dc3.tsdb.influxdb.database` | `dc3` | target database |
+| Key | Default | Meaning |
+|------------------------------|-------------------------|--------------------------|
+| `dc3.tsdb.influxdb.url` | `http://localhost:8181` | InfluxDB 3 HTTP endpoint |
+| `dc3.tsdb.influxdb.token` | *(empty)* | auth token |
+| `dc3.tsdb.influxdb.database` | `dc3` | target database |
## Dependencies
diff --git a/dc3-tsdb/dc3-tsdb-influxdb/pom.xml b/dc3-tsdb/dc3-tsdb-influxdb/pom.xml
index 77ce20fa5..858dcf206 100644
--- a/dc3-tsdb/dc3-tsdb-influxdb/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-influxdb/pom.xml
@@ -29,7 +29,9 @@
dc3-tsdb-influxdb
${project.artifactId}
- InfluxDB 3 adapter of the TSDB port — tags/fields over the documented v3 HTTP SQL and line-protocol APIs, zero client dependencies
+ InfluxDB 3 adapter of the TSDB port — tags/fields over the documented v3 HTTP SQL and line-protocol
+ APIs, zero client dependencies
+
diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java
index 83228bb66..5d900e1c6 100644
--- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java
+++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/InfluxdbTsdbStore.java
@@ -437,7 +437,9 @@ public final class InfluxdbTsdbStore implements TsdbStore {
return "date_bin(%s, time, TIMESTAMP '1970-01-01T00:00:00Z')".formatted(intervalLiteral(bucketWidth));
}
- /** Largest exactly-dividing unit; DataFusion intervals lack a generic millisecond form. */
+ /**
+ * Largest exactly-dividing unit; DataFusion intervals lack a generic millisecond form.
+ */
private static String intervalLiteral(Duration width) {
long nanos = width.toNanos();
if (nanos % 3_600_000_000_000L == 0) {
@@ -452,7 +454,9 @@ public final class InfluxdbTsdbStore implements TsdbStore {
return "INTERVAL '" + (nanos / 1_000_000L) + " millisecond'";
}
- /** RFC3339 literal — InfluxDB 3 compares timestamps against string literals. */
+ /**
+ * RFC3339 literal — InfluxDB 3 compares timestamps against string literals.
+ */
private static String literal(Instant instant) {
return "TIMESTAMP '" + instant + "'";
}
@@ -506,7 +510,9 @@ public final class InfluxdbTsdbStore implements TsdbStore {
}
}
- /** CSV response: header row then data rows, RFC-4180 quoting. */
+ /**
+ * CSV response: header row then data rows, RFC-4180 quoting.
+ */
private List query(String sql, TsdbDeadline deadline) {
String body = "{\"db\":\"" + database + "\",\"q\":"
+ com.fasterxml.jackson.databind.node.JsonNodeFactory.instance.textNode(sql).toString()
@@ -537,7 +543,9 @@ public final class InfluxdbTsdbStore implements TsdbStore {
return CsvRows.parse(response);
}
- /** Minimal CSV row with typed accessors; values arrive as text and parse on demand. */
+ /**
+ * Minimal CSV row with typed accessors; values arrive as text and parse on demand.
+ */
static final class CsvRow {
private final Map values;
diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java
index bbc292a9b..70aa3242f 100644
--- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java
+++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbAutoConfiguration.java
@@ -39,7 +39,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "influxdb")
public class InfluxdbTsdbAutoConfiguration {
- /** The InfluxDB 3 adapter over the v3 HTTP APIs. */
+ /**
+ * The InfluxDB 3 adapter over the v3 HTTP APIs.
+ */
@Bean
@ConditionalOnMissingBean(TsdbStore.class)
public TsdbStore tsdbStore(InfluxdbTsdbProperties properties) {
diff --git a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java
index ae28e9f2e..579897c31 100644
--- a/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java
+++ b/dc3-tsdb/dc3-tsdb-influxdb/src/main/java/io/github/pnoker/common/tsdb/influxdb/config/InfluxdbTsdbProperties.java
@@ -33,12 +33,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "dc3.tsdb.influxdb")
public class InfluxdbTsdbProperties {
- /** Base url of the InfluxDB 3 node, e.g. {@code http://dc3-influxdb:8181}. */
+ /**
+ * Base url of the InfluxDB 3 node, e.g. {@code http://dc3-influxdb:8181}.
+ */
private String url = "http://localhost:8181";
- /** Bearer token with write+query permissions. */
+ /**
+ * Bearer token with write+query permissions.
+ */
private String token = "";
- /** Database (auto-created on first write). */
+ /**
+ * Database (auto-created on first write).
+ */
private String database = "dc3";
}
diff --git a/dc3-tsdb/dc3-tsdb-iotdb/README.md b/dc3-tsdb/dc3-tsdb-iotdb/README.md
index 4c9600656..b51f4b37a 100644
--- a/dc3-tsdb/dc3-tsdb-iotdb/README.md
+++ b/dc3-tsdb/dc3-tsdb-iotdb/README.md
@@ -1,7 +1,7 @@
# DC3 TSDB IoTDB
-`dc3-tsdb-iotdb` adapts the store-neutral port to Apache IoTDB over the session API (`iotdb-session`). Series are
-stored as tree paths under `root.dc3.*`.
+`dc3-tsdb-iotdb` adapts the store-neutral port to Apache IoTDB over the session API (`iotdb-session`). Series are stored
+as tree paths under `root.dc3.*`.
## Activation
@@ -11,12 +11,12 @@ Active when `dc3.tsdb.type=iotdb`.
`IotdbTsdbProperties` binds the `dc3.tsdb.iotdb` prefix:
-| Key | Default | Meaning |
-|---|---|---|
-| `dc3.tsdb.iotdb.host` | `localhost` | IoTDB host |
-| `dc3.tsdb.iotdb.port` | `6667` | session port |
-| `dc3.tsdb.iotdb.username` | `root` | login name |
-| `dc3.tsdb.iotdb.password` | `root` | login password |
+| Key | Default | Meaning |
+|---------------------------|-------------|----------------|
+| `dc3.tsdb.iotdb.host` | `localhost` | IoTDB host |
+| `dc3.tsdb.iotdb.port` | `6667` | session port |
+| `dc3.tsdb.iotdb.username` | `root` | login name |
+| `dc3.tsdb.iotdb.password` | `root` | login password |
## Dependencies
diff --git a/dc3-tsdb/dc3-tsdb-iotdb/pom.xml b/dc3-tsdb/dc3-tsdb-iotdb/pom.xml
index 16cee039d..8f7db823c 100644
--- a/dc3-tsdb/dc3-tsdb-iotdb/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-iotdb/pom.xml
@@ -29,7 +29,9 @@
dc3-tsdb-iotdb
${project.artifactId}
- Apache IoTDB adapter of the TSDB port — tree paths root.dc3.* over the session API; requires timestamp_precision=us
+ Apache IoTDB adapter of the TSDB port — tree paths root.dc3.* over the session API; requires
+ timestamp_precision=us
+
diff --git a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java
index 741eb352f..1c3db1e00 100644
--- a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java
+++ b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/IotdbTsdbStore.java
@@ -561,12 +561,16 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
return rows;
}
- /** One result row: timestamp plus typed accessors over the fixed sample layout
- * or name-derived series/value views for aggregate shapes. */
+ /**
+ * One result row: timestamp plus typed accessors over the fixed sample layout
+ * or name-derived series/value views for aggregate shapes.
+ */
static final class Row {
private final long timestamp;
- /** Result column names; IoTDB prefixes a "Time" entry the field list omits. */
+ /**
+ * Result column names; IoTDB prefixes a "Time" entry the field list omits.
+ */
private final List columns;
private final List fieldColumns;
private final RowRecord record;
@@ -602,8 +606,10 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
return Objects.isNull(value) ? null : value.doubleValue();
}
- /** Aggregate columns may come back typed differently per function; the
- * typed getters on Field throw when the backing slot is unset. */
+ /**
+ * Aggregate columns may come back typed differently per function; the
+ * typed getters on Field throw when the backing slot is unset.
+ */
private Object valueAt(int index) {
org.apache.tsfile.read.common.Field field = fieldAt(index);
if (Objects.isNull(field)) {
@@ -624,7 +630,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
return Objects.isNull(field) || field.getDataType() == TSDataType.UNKNOWN ? null : field;
}
- /** Measurement-name keyed view of a SELECT * row (last path segment). */
+ /**
+ * Measurement-name keyed view of a SELECT * row (last path segment).
+ */
Map measurements() {
Map out = new LinkedHashMap<>();
for (int i = 0; i < fieldColumns.size() && i < record.getFields().size(); i++) {
@@ -675,7 +683,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
return null;
}
- /** Series of the first field column, for aggregate result shapes. */
+ /**
+ * Series of the first field column, for aggregate result shapes.
+ */
SeriesKey series() {
return fieldColumns.isEmpty() ? null : seriesOfPath(fieldColumns.getFirst());
}
@@ -684,7 +694,9 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
return nullableDoubleAt(fieldIndex);
}
- /** Second column of the (value, count) aggregate pair, when selected. */
+ /**
+ * Second column of the (value, count) aggregate pair, when selected.
+ */
long count() {
return columns.size() > 1 ? longAt(1) : 1L;
}
@@ -692,8 +704,10 @@ public final class IotdbTsdbStore implements TsdbStore, AutoCloseable {
record Cell(Double value, long count, boolean countColumn) {
}
- /** Series-keyed cells for aggregate shapes; a series may carry one value
- * column and one count column. */
+ /**
+ * Series-keyed cells for aggregate shapes; a series may carry one value
+ * column and one count column.
+ */
Map cells() {
Map cells = new LinkedHashMap<>();
for (int i = 0; i < fieldColumns.size() && i < record.getFields().size(); i++) {
diff --git a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java
index 416020053..3ab6e5559 100644
--- a/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java
+++ b/dc3-tsdb/dc3-tsdb-iotdb/src/main/java/io/github/pnoker/common/tsdb/iotdb/config/IotdbTsdbAutoConfiguration.java
@@ -39,7 +39,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "iotdb")
public class IotdbTsdbAutoConfiguration {
- /** The IoTDB adapter over the session API; closed with the context. */
+ /**
+ * The IoTDB adapter over the session API; closed with the context.
+ */
@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(TsdbStore.class)
public IotdbTsdbStore tsdbStore(IotdbTsdbProperties properties) {
diff --git a/dc3-tsdb/dc3-tsdb-tck/README.md b/dc3-tsdb/dc3-tsdb-tck/README.md
index ae80e618b..252748ebf 100644
--- a/dc3-tsdb/dc3-tsdb-tck/README.md
+++ b/dc3-tsdb/dc3-tsdb-tck/README.md
@@ -1,18 +1,18 @@
# DC3 TSDB TCK
`dc3-tsdb-tck` is the store-neutral contract suite of the `dc3-tsdb` family: an adapter that passes these tests is
-compliant with the time-series port. `AbstractTsdbContractTest` defines the shared contract (write, latest-value
-read, history read, and schema behaviour); one concrete test per store boots the engine in a disposable Testcontainers
+compliant with the time-series port. `AbstractTsdbContractTest` defines the shared contract (write, latest-value read,
+history read, and schema behaviour); one concrete test per store boots the engine in a disposable Testcontainers
container.
## Contract tests
-| Test | Store |
-|---|---|
-| `TimescaleContractTest` | TimescaleDB |
-| `TdengineContractTest` | TDengine |
-| `InfluxdbContractTest` | InfluxDB 3 |
-| `IotdbContractTest` | Apache IoTDB |
+| Test | Store |
+|-------------------------|--------------|
+| `TimescaleContractTest` | TimescaleDB |
+| `TdengineContractTest` | TDengine |
+| `InfluxdbContractTest` | InfluxDB 3 |
+| `IotdbContractTest` | Apache IoTDB |
## Running
diff --git a/dc3-tsdb/dc3-tsdb-tck/pom.xml b/dc3-tsdb/dc3-tsdb-tck/pom.xml
index 9e7f7b5dc..35d0be151 100644
--- a/dc3-tsdb/dc3-tsdb-tck/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-tck/pom.xml
@@ -31,7 +31,8 @@
2026.5.22
jar
- IoT DC3 store-neutral time-series contract suite: an adapter that passes these tests is compliant
+ IoT DC3 store-neutral time-series contract suite: an adapter that passes these tests is compliant
+
diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java
index cca9f6b62..2d4b21554 100644
--- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java
+++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/AbstractTsdbContractTest.java
@@ -355,7 +355,7 @@ public abstract class AbstractTsdbContractTest {
// COUNT via (possibly tiered) minute buckets must equal the raw count.
long tierCount = store().bucketedAggregate(SeriesFilter.of(key), AggregateFunction.COUNT,
- window, Duration.ofMinutes(1), null, DEADLINE)
+ window, Duration.ofMinutes(1), null, DEADLINE)
.getOrDefault(key, List.of()).stream().mapToLong(BucketAggregate::sampleCount).sum();
assertThat(tierCount).isEqualTo(store().count(SeriesFilter.of(key), window, DEADLINE));
@@ -384,7 +384,7 @@ public abstract class AbstractTsdbContractTest {
// percentiles on the raw path without supertable-style failures.
if (store().capabilities().percentile()) {
List p50 = store().bucketedAggregate(SeriesFilter.of(key),
- AggregateFunction.PERCENTILE, window, Duration.ofMinutes(1), 0.5, DEADLINE)
+ AggregateFunction.PERCENTILE, window, Duration.ofMinutes(1), 0.5, DEADLINE)
.getOrDefault(key, List.of());
assertThat(p50).hasSize(5);
assertThat(p50).allSatisfy(bucket -> assertThat(bucket.value()).isNotNull());
diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java
index c57d73f9a..bebd30156 100644
--- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java
+++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/InfluxdbContractTest.java
@@ -64,7 +64,9 @@ class InfluxdbContractTest extends AbstractTsdbContractTest {
INFLUX.stop();
}
- /** Any HTTP answer (even 401) means the node is serving; then mint a token. */
+ /**
+ * Any HTTP answer (even 401) means the node is serving; then mint a token.
+ */
private static void awaitHttpUp() {
String url = "http://" + INFLUX.getHost() + ":" + INFLUX.getMappedPort(8181) + "/health";
long deadline = System.currentTimeMillis() + 5 * 60 * 1000;
diff --git a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java
index 65e6593bb..ae857596e 100644
--- a/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java
+++ b/dc3-tsdb/dc3-tsdb-tck/src/test/java/io/github/pnoker/common/tsdb/tck/IotdbContractTest.java
@@ -43,7 +43,9 @@ import java.util.Objects;
@EnabledIfEnvironmentVariable(named = "DC3_TSDB_TCK", matches = "(?i)true|1|yes|on")
class IotdbContractTest extends AbstractTsdbContractTest {
- /** The minimal properties override: Java properties merge over code defaults. */
+ /**
+ * The minimal properties override: Java properties merge over code defaults.
+ */
private static final GenericContainer> IOTDB = new GenericContainer<>(
DockerImageName.parse("apache/iotdb:2.0.10-standalone"))
.withExposedPorts(6667)
diff --git a/dc3-tsdb/dc3-tsdb-tdengine/README.md b/dc3-tsdb/dc3-tsdb-tdengine/README.md
index a8055223c..3b41a5b2e 100644
--- a/dc3-tsdb/dc3-tsdb-tdengine/README.md
+++ b/dc3-tsdb/dc3-tsdb-tdengine/README.md
@@ -1,7 +1,7 @@
# DC3 TSDB TDengine
-`dc3-tsdb-tdengine` adapts the store-neutral port to TDengine over its REST/WS JDBC driver
-(`taos-jdbcdriver`). Points are written to a supertable with per-series subtables.
+`dc3-tsdb-tdengine` adapts the store-neutral port to TDengine over its REST/WS JDBC driver (`taos-jdbcdriver`). Points
+are written to a supertable with per-series subtables.
## Activation
@@ -11,13 +11,13 @@ Active when `dc3.tsdb.type=tdengine`.
`TdengineTsdbProperties` binds the `dc3.tsdb.tdengine` prefix:
-| Key | Default | Meaning |
-|---|---|---|
-| `dc3.tsdb.tdengine.url` | `jdbc:TAOS-RS://localhost:6041/` | JDBC URL |
-| `dc3.tsdb.tdengine.username` | `root` | login name |
-| `dc3.tsdb.tdengine.password` | `taosdata` | login password |
-| `dc3.tsdb.tdengine.database` | `dc3` | target database |
-| `dc3.tsdb.tdengine.maximum-pool-size` | `8` | connection pool size |
+| Key | Default | Meaning |
+|---------------------------------------|----------------------------------|----------------------|
+| `dc3.tsdb.tdengine.url` | `jdbc:TAOS-RS://localhost:6041/` | JDBC URL |
+| `dc3.tsdb.tdengine.username` | `root` | login name |
+| `dc3.tsdb.tdengine.password` | `taosdata` | login password |
+| `dc3.tsdb.tdengine.database` | `dc3` | target database |
+| `dc3.tsdb.tdengine.maximum-pool-size` | `8` | connection pool size |
## Dependencies
diff --git a/dc3-tsdb/dc3-tsdb-tdengine/pom.xml b/dc3-tsdb/dc3-tsdb-tdengine/pom.xml
index f23376103..ffc9ab813 100644
--- a/dc3-tsdb/dc3-tsdb-tdengine/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-tdengine/pom.xml
@@ -29,7 +29,8 @@
dc3-tsdb-tdengine
${project.artifactId}
- TDengine adapter of the TSDB port — supertable + per-series subtables over the REST/WS JDBC driver
+ TDengine adapter of the TSDB port — supertable + per-series subtables over the REST/WS JDBC driver
+
diff --git a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java
index 18ce03c5f..db3310c54 100644
--- a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java
+++ b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbAutoConfiguration.java
@@ -41,7 +41,9 @@ import org.springframework.context.annotation.Bean;
@ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "tdengine")
public class TdengineTsdbAutoConfiguration {
- /** The TDengine adapter over the REST JDBC driver. */
+ /**
+ * The TDengine adapter over the REST JDBC driver.
+ */
@Bean(destroyMethod = "close")
@ConditionalOnMissingBean(TsdbStore.class)
public TsdbStore tsdbStore(TdengineTsdbProperties properties) {
diff --git a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java
index ec55ad31e..cb1bb65a7 100644
--- a/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java
+++ b/dc3-tsdb/dc3-tsdb-tdengine/src/main/java/io/github/pnoker/common/tsdb/tdengine/config/TdengineTsdbProperties.java
@@ -33,14 +33,18 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "dc3.tsdb.tdengine")
public class TdengineTsdbProperties {
- /** REST/WS JDBC url without a database segment, e.g. {@code jdbc:TAOS-RS://dc3-tdengine:6041/}. */
+ /**
+ * REST/WS JDBC url without a database segment, e.g. {@code jdbc:TAOS-RS://dc3-tdengine:6041/}.
+ */
private String url = "jdbc:TAOS-RS://localhost:6041/";
private String username = "root";
private String password = "taosdata";
- /** Database the adapter creates and owns (PRECISION 'us'). */
+ /**
+ * Database the adapter creates and owns (PRECISION 'us').
+ */
private String database = "dc3";
private int maximumPoolSize = 8;
diff --git a/dc3-tsdb/dc3-tsdb-timescale/README.md b/dc3-tsdb/dc3-tsdb-timescale/README.md
index f3b987ac4..c1aa39995 100644
--- a/dc3-tsdb/dc3-tsdb-timescale/README.md
+++ b/dc3-tsdb/dc3-tsdb-timescale/README.md
@@ -9,16 +9,16 @@ Active when `dc3.tsdb.type=timescale` — the default (`matchIfMissing = true`).
## Key types
-| Type | Role |
-|---|---|
-| `TimescaleTsdbStore` | `TsdbStore` implementation (hypertable writes, latest-value upserts, history queries) |
-| `TsdbTimescaleAutoConfiguration` | adapter wiring and rollup retention |
+| Type | Role |
+|----------------------------------|---------------------------------------------------------------------------------------|
+| `TimescaleTsdbStore` | `TsdbStore` implementation (hypertable writes, latest-value upserts, history queries) |
+| `TsdbTimescaleAutoConfiguration` | adapter wiring and rollup retention |
## Configuration
-| Key | Default | Meaning |
-|---|---|---|
-| `dc3.tsdb.timescale.rollup.minute-keep-days` | `365` | minute-tier retention used by the rollup job |
+| Key | Default | Meaning |
+|----------------------------------------------|---------|----------------------------------------------|
+| `dc3.tsdb.timescale.rollup.minute-keep-days` | `365` | minute-tier retention used by the rollup job |
## Dependencies
diff --git a/dc3-tsdb/dc3-tsdb-timescale/pom.xml b/dc3-tsdb/dc3-tsdb-timescale/pom.xml
index 64d2ecffc..936eb9eb8 100644
--- a/dc3-tsdb/dc3-tsdb-timescale/pom.xml
+++ b/dc3-tsdb/dc3-tsdb-timescale/pom.xml
@@ -31,7 +31,9 @@
2026.5.22
jar
- IoT DC3 TimescaleDB adapter for the store-neutral time-series port (embedded or standalone PostgreSQL)
+ IoT DC3 TimescaleDB adapter for the store-neutral time-series port (embedded or standalone
+ PostgreSQL)
+
diff --git a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java
index a76a2ed09..916653511 100644
--- a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java
+++ b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/TimescaleTsdbStore.java
@@ -535,7 +535,7 @@ public final class TimescaleTsdbStore implements TsdbStore {
OffsetDateTime.ofInstant(window.toExclusive(), ZoneOffset.UTC),
limit};
return timed(deadline, () -> jdbc.query(sql, (rs, i) -> new DimensionCount(dimension,
- rs.getLong(1), rs.getLong(2)), args));
+ rs.getLong(1), rs.getLong(2)), args));
}
@Override
@@ -561,7 +561,7 @@ public final class TimescaleTsdbStore implements TsdbStore {
OffsetDateTime.ofInstant(window.from(), ZoneOffset.UTC),
OffsetDateTime.ofInstant(window.toExclusive(), ZoneOffset.UTC)};
return timed(deadline, () -> jdbc.query(sql, (rs, i) -> new SeriesLastSeen(
- new SeriesKey(rs.getLong(1), rs.getLong(2), rs.getLong(3)), toInstant(rs, 4)), args));
+ new SeriesKey(rs.getLong(1), rs.getLong(2), rs.getLong(3)), toInstant(rs, 4)), args));
}
@Override
@@ -724,7 +724,9 @@ public final class TimescaleTsdbStore implements TsdbStore {
return result;
}
- /** Exactly composable re-aggregation over the shared observability tiers. */
+ /**
+ * Exactly composable re-aggregation over the shared observability tiers.
+ */
private static String tierExpression(AggregateFunction fn) {
return switch (fn) {
case AVG -> "SUM(num_sum) / NULLIF(SUM(num_count), 0)";
diff --git a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java
index a85512330..605af9e84 100644
--- a/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java
+++ b/dc3-tsdb/dc3-tsdb-timescale/src/main/java/io/github/pnoker/common/tsdb/timescale/config/TsdbTimescaleAutoConfiguration.java
@@ -50,7 +50,9 @@ import javax.sql.DataSource;
@ConditionalOnProperty(prefix = "dc3.tsdb", name = "type", havingValue = "timescale", matchIfMissing = true)
public class TsdbTimescaleAutoConfiguration {
- /** The TimescaleDB adapter on the application-provided tsdbDataSource. */
+ /**
+ * The TimescaleDB adapter on the application-provided tsdbDataSource.
+ */
@Bean
@ConditionalOnMissingBean(TsdbStore.class)
public TsdbStore tsdbStore(@Qualifier("tsdbDataSource") DataSource tsdbDataSource,
diff --git a/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md b/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md
index 2a5636523..5702e4e96 100644
--- a/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md
+++ b/dc3-web/FRONTEND_OPTIMIZATION_PLAN.md
@@ -6,18 +6,18 @@
## 优先级矩阵
-| 级别 | 编号 | 标题 | 工作量 | 类型 |
-|---|---|---|---|---|
-| P0 | P0-1 | 统一 done 回调契约:失败保留弹窗、消除双提示/误报成功 | S | 正确性 |
-| P0 | P0-2 | operationTime 误绑 createTime(4 处同源) | S | 正确性 |
-| P0 | P0-3 | DeviceEdit 切换 driver/profile 加未保存守卫 + i18n 离开确认 | S | 正确性 |
-| P0 | P0-4 | PointValue 分页 reset + DeviceImport 空文件永久 loading | S | 正确性 |
-| P1 | P1-1 | 系统化「错误/加载/空」三态 + CardListShell | M | 体验 |
-| P1 | P1-2 | 令牌化收敛:裸 hex → CSS 变量 + 圆角令牌 + 共享 footer 类 | M | 美化 |
-| P1 | P1-3 | 抽 useRemoteDictionary + Device/Profile 共享字段组件 | M | 重构 |
-| P1 | P1-4 | StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active | M | 美化 |
-| P1 | P1-5 | PointValueCard 数值按质量着色 + 稳定字号 | M | 美化 |
-| P2 | — | a11y / 暗色模式 / 死代码清理 / DeviceEdit 抽 ConfigMatrix | S–L | 打磨 |
+| 级别 | 编号 | 标题 | 工作量 | 类型 |
+|------|------|-----------------------------------------------------------------|--------|--------|
+| P0 | P0-1 | 统一 done 回调契约:失败保留弹窗、消除双提示/误报成功 | S | 正确性 |
+| P0 | P0-2 | operationTime 误绑 createTime(4 处同源) | S | 正确性 |
+| P0 | P0-3 | DeviceEdit 切换 driver/profile 加未保存守卫 + i18n 离开确认 | S | 正确性 |
+| P0 | P0-4 | PointValue 分页 reset + DeviceImport 空文件永久 loading | S | 正确性 |
+| P1 | P1-1 | 系统化「错误/加载/空」三态 + CardListShell | M | 体验 |
+| P1 | P1-2 | 令牌化收敛:裸 hex → CSS 变量 + 圆角令牌 + 共享 footer 类 | M | 美化 |
+| P1 | P1-3 | 抽 useRemoteDictionary + Device/Profile 共享字段组件 | M | 重构 |
+| P1 | P1-4 | StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active | M | 美化 |
+| P1 | P1-5 | PointValueCard 数值按质量着色 + 稳定字号 | M | 美化 |
+| P2 | — | a11y / 暗色模式 / 死代码清理 / DeviceEdit 抽 ConfigMatrix | S–L | 打磨 |
---
@@ -81,19 +81,18 @@ const onAdd = (form, done) => {
`submitting`,提示与重置交给父页 `.then`)。本方案与其对齐。
- `ProfileAddForm.vue:111`(事件名 `add-thing`)同样修改;`Profile.vue:122` `addThing` 父页同样改。
- `DeviceImportForm.vue:258` 的 `importTemplate` done 回调同源,一并按此契约修。
-- 顺带给 `DeviceAddForm` / `ProfileAddForm` 的确认按钮补 `:loading="submitting"`(引入 `submitting` ref,
- 提交前置 true、`try/finally` 复位),防重复提交——对齐 `PointEditForm.vue:108`。
+- 顺带给 `DeviceAddForm` / `ProfileAddForm` 的确认按钮补 `:loading="submitting"`(引入 `submitting` ref, 提交前置 true、
+ `try/finally` 复位),防重复提交——对齐 `PointEditForm.vue:108`。
-**验证**:`pnpm test`(补充用例:mock reject 时弹窗不关、不弹成功);手测新建失败/成功两种路径。
-**风险**:低。纯交互契约调整,不改数据流。
+**验证**:`pnpm test`(补充用例:mock reject 时弹窗不关、不弹成功);手测新建失败/成功两种路径。 **风险**:低。纯交互契约调整,不改数据流。
---
### P0-2 operationTime 误绑 createTime(4 处同源 bug)
-**问题**:详情页「操作时间」与「创建时间」都绑定 `createTime`,渲染完全相同的时刻。卡片层
-(`PointCard.vue:60`、`ProfileCard.vue:36`)与 settings 族(`CommandList.vue:93`、`EventList.vue:90`)
-都正确用了 `operateTime`,证明这是 detail 页的复制粘贴回归。
+**问题**:详情页「操作时间」与「创建时间」都绑定 `createTime`,渲染完全相同的时刻。卡片层 (`PointCard.vue:60`、
+`ProfileCard.vue:36`)与 settings 族(`CommandList.vue:93`、`EventList.vue:90`) 都正确用了 `operateTime`,证明这是 detail
+页的复制粘贴回归。
**Before**(`views/device/detail/DeviceDetail.vue:40`)
@@ -124,8 +123,8 @@ const onAdd = (form, done) => {
{{ $t('pointValue.card.saveTime') }}: {{ displayTime(data.createTime) }}
```
-参照 `PointValue.vue:156-158` 的 `interval = operateTime - createTime`(用于 `delay` 计算),
-语义为 `createTime`=采集/产生时刻、`operateTime`=保存/入库时刻,建议:
+参照 `PointValue.vue:156-158` 的 `interval = operateTime - createTime`(用于 `delay` 计算), 语义为 `createTime`=采集/产生时刻、
+`operateTime`=保存/入库时刻,建议:
```html
{{ $t('pointValue.card.collectTime') }}: {{ displayTime(data.createTime) }}
@@ -134,8 +133,7 @@ const onAdd = (form, done) => {
> ⚠️ 若后端 `PointValue` 字段语义相反,则 collectTime/saveTime 对调——实施前与后端字段定义核对一次。
-**验证**:构造 `operateTime ≠ createTime` 的 mock 数据,确认两个描述项显示不同时刻。
-**风险**:极低。纯展示字段替换。
+**验证**:构造 `operateTime ≠ createTime` 的 mock 数据,确认两个描述项显示不同时刻。 **风险**:极低。纯展示字段替换。
---
@@ -144,10 +142,10 @@ const onAdd = (form, done) => {
**问题**:
1. `device/edit/index.ts:625` `changeAttribute` 与 `:1423` `changeProfile` 切换时直接重建
- `driverFormData`/`pointInfoData`/`commandInfoData`/`eventInfoData` 四类矩阵,**不查 `totalDirtyCount`**,
+ `driverFormData`/`pointInfoData`/`commandInfoData`/`eventInfoData` 四类矩阵, **不查 `totalDirtyCount`**,
几十个未保存脏单元格被无声清空。`onBeforeRouteLeave`(:1478)只拦路由离开,拦不住本页内下拉切换。
-2. `:1480` `window.confirm('You have unsaved changes...')` 是全流程唯一硬编码英文文案,
- 可复用已有的 `common.discardConfirm`(`config/i18n/locales/en.ts`、`zh.ts`)。
+2. `:1480` `window.confirm('You have unsaved changes...')` 是全流程唯一硬编码英文文案, 可复用已有的
+ `common.discardConfirm`(`config/i18n/locales/en.ts`、`zh.ts`)。
**Before**(`views/device/edit/index.ts:1423`)
@@ -188,20 +186,20 @@ const changeProfile = () => {
const leave = window.confirm(t('common.discardConfirm'));
```
-> ⚠️ 注意点:`changeAttribute` 由 driver 下拉的 `@change` 触发,若用户取消需把下拉值**回退**到切换前
+> ⚠️ 注意点:`changeAttribute` 由 driver 下拉的 `@change` 触发,若用户取消需把下拉值 **回退**到切换前
> 的 `oldDriverFormData` 对应 driverId(否则 UI 已变但数据未重建,状态不一致)。建议在 select 上用
> `:value` + 手动 commit 模式,或在守卫取消时 `reactiveData.deviceFormData.driverId = prevDriverId`。
> 进一步可用 `ElMessageBox.confirm` 取代原生 `window.confirm` 以保持视觉统一。
-**验证**:先在矩阵里改几个单元格不保存,再切换 driver/profile,应弹确认;取消则数据保留。
-**风险**:中。需处理「取消后回退下拉值」的边界,建议配组件测试。
+**验证**:先在矩阵里改几个单元格不保存,再切换 driver/profile,应弹确认;取消则数据保留。 **风险**
+:中。需处理「取消后回退下拉值」的边界,建议配组件测试。
---
### P0-4 PointValue 分页 reset + DeviceImport 空文件永久 loading
**问题 A**:`views/point/value/PointValue.vue:110` 的 `list()` 使用 `reactiveData.page`,但
-`search()` / `sizeChange()` 只更新 `query`/`size` 就调用 `list()`,**未把 `page.current` 重置为 1**
+`search()` / `sizeChange()` 只更新 `query`/`size` 就调用 `list()`, **未把 `page.current` 重置为 1**
(`usePagedList` 会重置,而这里是手写分页)→ 在第 3 页搜索会落到空页。
**After**(最小修复:在 `search()` / `sizeChange()` 开头重置)
@@ -221,7 +219,7 @@ const sizeChange = () => {
> `listPointValue` / `getPointValueLatest`,顺带补齐错误态(见 P1-1)。
**问题 B**:`views/device/import/DeviceImportForm.vue:267` `importThing` 在 `form.validate()` 后立即
-`submit()` 并置 `formLoading = true`;但 `el-upload`(`auto-upload=false`)在**文件列表为空**时
+`submit()` 并置 `formLoading = true`;但 `el-upload`(`auto-upload=false`)在 **文件列表为空**时
`submit()` 不触发 `http-request` → 确认按钮永久 loading、无任何提示。
**Before**
@@ -259,8 +257,7 @@ const importThing = async () => {
> 同时确认 `http-request` handler 在其 `finally` 里复位 `formLoading = false`,避免上传完成后按钮卡住。
-**验证**:PointValue 翻到第 3 页再搜索,应回到第 1 页;导入弹窗不选文件点确认,应提示而非卡 loading。
-**风险**:低。
+**验证**:PointValue 翻到第 3 页再搜索,应回到第 1 页;导入弹窗不选文件点确认,应提示而非卡 loading。 **风险**:低。
---
@@ -269,19 +266,18 @@ const importThing = async () => {
### P1-1 系统化「错误/加载/空」三态:停止吞错 + CardListShell 补 error/retry
**问题**:`composables/usePagedList.ts:87` 的 `catch { // handled globally }` 与
-`PointValue.vue:126/140` 的 `.catch(() => {})` 静默吞错,`listData` 残留空数组,UI 退化为 `el-empty`,
-用户**无法区分「无数据」与「加载失败」**;全仓 grep `retry` 零命中,无重试入口。
+`PointValue.vue:126/140` 的 `.catch(() => {})` 静默吞错,`listData` 残留空数组,UI 退化为 `el-empty`, 用户
+**无法区分「无数据」与「加载失败」**;全仓 grep `retry` 零命中,无重试入口。
**方案**:
1. `usePagedList` 增加 `error` 态:`catch` 里捕获并存 `error` 标识,`load` 暴露 `retry`;
2. 抽取 `components/card/list/CardListShell.vue`(复用 `usePagedList`),封装 loading(12 个
`SkeletonCard`)+ empty(`el-empty`)+ **error+retry** 三态 + 卡片栅格;
-3. `Device.vue` / `Profile.vue` / `Point.vue` / `PointValue.vue` 仅传 card 组件与 query,删除三处近乎逐行
- 重复的列表模板。
+3. `Device.vue` / `Profile.vue` / `Point.vue` / `PointValue.vue` 仅传 card 组件与 query,删除三处近乎逐行 重复的列表模板。
-**收益**:一次修复覆盖 device/profile/point/command/event/pointValue 全族列表页。
-**验证**:mock `listXxx` reject,应显示错误态 + 重试按钮,而非空态。
+**收益**:一次修复覆盖 device/profile/point/command/event/pointValue 全族列表页。 **验证**:mock `listXxx`
+reject,应显示错误态 + 重试按钮,而非空态。
---
@@ -289,14 +285,14 @@ const importThing = async () => {
**问题**:大量样式绕过 Element Plus CSS 变量与 palette 令牌,阻断统一调参与暗色模式。
-| 位置 | 当前 | 建议 |
-|---|---|---|
-| `components/card/actions/ThingsCardActions.vue:24/37/50` | `icon-color: #e6a23c/#67c23a/#f56c6c` | `var(--el-color-warning/success/danger)` |
-| `components/card/base/CardShell.vue:117` | `background: #f6f7f9` | `var(--el-fill-color-light)` |
-| `views/driver/card/DriverCard.vue:134` | `border-top: 1px solid #dcdfe6` | `var(--el-border-color)` |
-| `components/card/stat/StatCard.vue:125`(TS) | `purple: '#9059f6'` | 引用 `DASHBOARD_PALETTE.driver`(`config/constant/palette.ts`),消除 TS/SCSS 双写 |
-| `components/card/stat/StatCard.vue:168`(SCSS) | `--stat-card-accent: #9059f6` | `@use '@/styles/palette'; → $dashboard-driver` |
-| 全仓 30+ 处(含 `ThingsCardHeader.vue:53`、`SkeletonCard.vue`) | `border-radius: 4px` | 新增 `$radius-card` 令牌或用 `var(--el-border-radius-base)` |
+| 位置 | 当前 | 建议 |
+|-----------------------------------------------------------------|---------------------------------------|------------------------------------------------------------------------------------|
+| `components/card/actions/ThingsCardActions.vue:24/37/50` | `icon-color: #e6a23c/#67c23a/#f56c6c` | `var(--el-color-warning/success/danger)` |
+| `components/card/base/CardShell.vue:117` | `background: #f6f7f9` | `var(--el-fill-color-light)` |
+| `views/driver/card/DriverCard.vue:134` | `border-top: 1px solid #dcdfe6` | `var(--el-border-color)` |
+| `components/card/stat/StatCard.vue:125`(TS) | `purple: '#9059f6'` | 引用 `DASHBOARD_PALETTE.driver`(`config/constant/palette.ts`),消除 TS/SCSS 双写 |
+| `components/card/stat/StatCard.vue:168`(SCSS) | `--stat-card-accent: #9059f6` | `@use '@/styles/palette'; → $dashboard-driver` |
+| 全仓 30+ 处(含 `ThingsCardHeader.vue:53`、`SkeletonCard.vue`) | `border-radius: 4px` | 新增 `$radius-card` 令牌或用 `var(--el-border-radius-base)` |
**圆角令牌**:`config/plugins/element/element-variables.scss` 当前仅有 `$form-width-*` 令牌(且被 Vite
`additionalData` 全局注入,符合约定)。在其中新增:
@@ -329,18 +325,16 @@ $radius-control: var(--el-border-radius-base); // 控件
三处卡片删除各自的 scoped footer 外壳样式,仅保留按钮。
-**验证**:`pnpm build`;切暗色(若已支持)确认无残留亮色硬编码。
-**风险**:低,但面广——建议分文件小步提交,每步 `pnpm build`。
+**验证**:`pnpm build`;切暗色(若已支持)确认无残留亮色硬编码。 **风险**:低,但面广——建议分文件小步提交,每步 `pnpm build`。
---
### P1-3 抽 useRemoteDictionary 组合式 + Device/Profile 共享字段组件
-**问题**:`driverDictionary` / `profileDictionary` 的「loading + `listXxxDictionary({page,label})` +
-visible-change 触发 + catch 吞错」模式在 5+ 处逐字重复(`DeviceAddForm.vue:164`、
-`DeviceImportForm.vue:177`、`device/edit/index.ts:551`、`DeviceTool.vue:118`、`PointTool.vue:139`,
-且 `PointTool.vue:160` 在 setup 顶层无条件预拉是浪费请求)。同时 device 的 add 与 edit 字段/校验各写
-一份(仅 `PointEditForm` 做到 add/edit 单组件复用)。
+**问题**:`driverDictionary` / `profileDictionary` 的「loading + `listXxxDictionary({page,label})` + visible-change 触发 +
+catch 吞错」模式在 5+ 处逐字重复(`DeviceAddForm.vue:164`、
+`DeviceImportForm.vue:177`、`device/edit/index.ts:551`、`DeviceTool.vue:118`、`PointTool.vue:139`, 且 `PointTool.vue:160` 在
+setup 顶层无条件预拉是浪费请求)。同时 device 的 add 与 edit 字段/校验各写 一份(仅 `PointEditForm` 做到 add/edit 单组件复用)。
**方案**:
@@ -353,32 +347,31 @@ visible-change 触发 + catch 吞错」模式在 5+ 处逐字重复(`DeviceAdd
}
```
-2. 抽 `views/device/components/DeviceFormFields.vue`(含 `deviceName/driverId/profileId/remark` 字段 +
- 校验 + 字典加载),`DeviceAddForm` 弹窗与 `DeviceEdit` 的 `InfoCard` `#fields` 插槽共用;
+2. 抽 `views/device/components/DeviceFormFields.vue`(含 `deviceName/driverId/profileId/remark` 字段 + 校验 + 字典加载),
+ `DeviceAddForm` 弹窗与 `DeviceEdit` 的 `InfoCard` `#fields` 插槽共用;
`ProfileFormFields.vue` 同理。
-**验证**:`pnpm test`;手测下拉远程搜索、visible 懒加载。
-**风险**:中。涉及多文件,保持 props/事件契约不变即可低风险替换。
+**验证**:`pnpm test`;手测下拉远程搜索、visible 懒加载。 **风险**:中。涉及多文件,保持 props/事件契约不变即可低风险替换。
---
### P1-4 StatCard 补加载态 + 业务卡实体色身份 + 修 DriverCard 死 .active
-**问题 A**:`StatCard.vue:72-87` 的 props 无 `loading`,`Home.vue` 首屏 `value` 默认 `0`、sparkline 空
-→ 显示**虚假数据**(而 `DashboardCard` 有完整 loading、列表卡有 `SkeletonCard`,三套不一致)。
+**问题 A**:`StatCard.vue:72-87` 的 props 无 `loading`,`Home.vue` 首屏 `value` 默认 `0`、sparkline 空 → 显示 **虚假数据**
+(而 `DashboardCard` 有完整 loading、列表卡有 `SkeletonCard`,三套不一致)。
**方案**:给 `StatCard` 加 `loading` prop,数值区与 sparkline 区用 `el-skeleton` 占位,命名与
`DashboardCard.loading` 对齐;可选地给 `SkeletonCard` 增 `variant: 'list' | 'stat' | 'dashboard'`。
-**问题 B**:`palette.scss:23-26` 已定义 driver 紫 / device 蓝 / profile 橙 / point 绿 四色令牌,但仅用于
-dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻卡时无法一眼分辨类型**。
+**问题 B**:`palette.scss:23-26` 已定义 driver 紫 / device 蓝 / profile 橙 / point 绿 四色令牌,但仅用于 dashboard,5
+张业务卡都用通用 PNG 图标 + `el-color-primary`, **翻卡时无法一眼分辨类型**。
-**方案**:在 `ThingsCardHeader.vue:49-60` 的图标容器(当前仅 `border-radius:4px` 无底色)加一层 8%
-透明度的实体色底(通过新增 `tone` prop 传入 `$dashboard-*` 令牌),各业务卡传自己的实体色。
+**方案**:在 `ThingsCardHeader.vue:49-60` 的图标容器(当前仅 `border-radius:4px` 无底色)加一层 8% 透明度的实体色底(通过新增
+`tone` prop 传入 `$dashboard-*` 令牌),各业务卡传自己的实体色。
**问题 C**:`views/driver/card/style.scss:18-20` 的 `.active { border-left: 5px solid #409eff }` 是死代码,
-`DriverCard.vue:93` emit 的 `select-change` 未被 `Driver.vue` 消费 → DriverCard 是**唯一没有选中反馈的
-列表卡**(对比 `PointInfoCard.vue:20` 用 `shadow='always'` 表达选中)。
+`DriverCard.vue:93` emit 的 `select-change` 未被 `Driver.vue` 消费 → DriverCard 是 **唯一没有选中反馈的 列表卡**(对比
+`PointInfoCard.vue:20` 用 `shadow='always'` 表达选中)。
**方案(二选一)**:
@@ -387,16 +380,15 @@ dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻
`$dashboard-driver`(紫);
- **B. 删除**:删 `style.scss` 整个文件 + `DriverCard.vue:126` 的 `@use` + 未消费的 `select-change` emit。
-**验证**:`pnpm build`;首页首屏应显示骨架而非 0;driver 列表点选应有高亮反馈。
-**风险**:低(A)/ 极低(B)。
+**验证**:`pnpm build`;首页首屏应显示骨架而非 0;driver 列表点选应有高亮反馈。 **风险**:低(A)/ 极低(B)。
---
### P1-5 PointValueCard 数值按质量着色 + 稳定字号
-**问题**:`views/point/value/card/PointValueCard.vue:321-326` 实时值用 `font-size: xx-large`(相对尺寸,
-跨屏不稳定)+ 装饰性 `hue` 动画(`:324`、`:364-374`,primary→light-9→primary),颜色不承载信息;
-唯一着色是 `value-missing` 灰。卡片已算出 `delayOk` / `delaySlow`(`:180-185`)却只用来染 header 下边框。
+**问题**:`views/point/value/card/PointValueCard.vue:321-326` 实时值用 `font-size: xx-large`(相对尺寸, 跨屏不稳定)+ 装饰性
+`hue` 动画(`:324`、`:364-374`,primary→light-9→primary),颜色不承载信息; 唯一着色是 `value-missing` 灰。卡片已算出
+`delayOk` / `delaySlow`(`:180-185`)却只用来染 header 下边框。
**Before**
@@ -428,23 +420,22 @@ dashboard,5 张业务卡都用通用 PNG 图标 + `el-color-primary`,**翻
模板按质量位绑定 `:class`:`delayOk` → `value--fresh`、`delaySlow` → `value--stale`,让颜色传达「新鲜度」。
-**验证**:`pnpm build`;构造不同 delay 的位号值,确认数值随新鲜度变色、字号稳定。
-**风险**:低。
+**验证**:`pnpm build`;构造不同 delay 的位号值,确认数值随新鲜度变色、字号稳定。 **风险**:低。
---
## P2 打磨与基础设施(迭代)
- **a11y 专项(最大盲区)**:全仓仅 9 处 `aria`/`role`。优先给纯图标按钮(`el-button` 仅 `:icon`:
- edit/delete/refresh/disable/import 等)批量补 `aria-label`;自定义交互(`PointValueCard`、卡片选中态)
- 加 `role` + keyboard handler;`components/layout/Layout.vue` 落地 skip-link 与弹窗/抽屉焦点管理。
+ edit/delete/refresh/disable/import 等)批量补 `aria-label`;自定义交互(`PointValueCard`、卡片选中态) 加 `role` + keyboard
+ handler;`components/layout/Layout.vue` 落地 skip-link 与弹窗/抽屉焦点管理。
- **错误通知 i18n 化**:`config/axios/index.ts:130-150` 全局错误通知硬编码英文(`'Server Error'` /
`'Network Error'`),影响面大于 `device/edit` 单点,统一收敛为 i18n key;并做 `en.ts(1334)` vs
`zh.ts(1332)` 的 key parity 审计。
- **暗色模式**(依赖 P1-2 令牌化前置完成):引入 Element Plus dark CSS + `useDark`,settings 增主题切换。
- **死代码清理**:`components/card/title/TitleCard.vue`(零消费者)、`PointInfoCard.vue` 未用字段、
- `profile/detail/index.ts` 未用字段、`Profile.vue` 的 `deviceId` 死参 + `listProfileByDeviceId`;
- 删除三处无效的 `enableFlag` 空校验规则(`PointEditForm.vue:172`、`device/edit/index.ts:376`、
+ `profile/detail/index.ts` 未用字段、`Profile.vue` 的 `deviceId` 死参 + `listProfileByDeviceId`; 删除三处无效的
+ `enableFlag` 空校验规则(`PointEditForm.vue:172`、`device/edit/index.ts:376`、
`profile/edit/index.ts:58`)。
- **DeviceEdit 现代化**:由 `defineComponent` 迁移到 `