feat(facade): introduce facade layer for cross-service calls

Adds three new modules that let business code call center services
through protocol-neutral interfaces instead of @GrpcClient stubs:

- dc3-common-facade-api: Device/Driver/Point + Tenant/User/UserLogin/Token
  facade interfaces plus facade-owned BO/Query/Page types
- dc3-common-facade-local: in-process implementations backed by the
  auth/manager services; zero serialization cost in single deployments
- dc3-common-facade-grpc: gRPC implementations holding BlockingStubs;
  default mode for distributed deployments

Implementation selection is driven by dc3.facade.mode={local|grpc} via
@ConditionalOnProperty, with grpc as the matchIfMissing default. Wire-up
for dc3-center-single (dc3.facade.mode=local + facade-local dependency)
is included so the single-deployment mode is runnable out of the box.
This commit is contained in:
pnoker
2026-04-27 19:46:15 +08:00
parent ca38651909
commit fddf7bb60e
54 changed files with 3017 additions and 0 deletions
+4
View File
@@ -43,6 +43,10 @@
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-data</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-local</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-manager</artifactId>
@@ -40,6 +40,10 @@ data:
speed: ${POINT_BATCH_SPEED:100}
interval: ${POINT_BATCH_INTERVAL:5}
dc3:
facade:
mode: ${DC3_FACADE_MODE:local}
logging:
file:
name: dc3/logs/center/single/${spring.application.name}.log
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present the IoT DC3 original author or authors.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common</artifactId>
<version>2026.4.19</version>
</parent>
<name>${project.artifactId}</name>
<artifactId>dc3-common-facade-api</artifactId>
<version>2026.4.19</version>
<packaging>jar</packaging>
<inceptionYear>2016</inceptionYear>
<url>https://github.com/pnoker/iot-dc3</url>
<description>
The Facade API of the IoT DC3 platform. Protocol-neutral interfaces and BO/Query/Page types
used to decouple callers from the underlying transport (local or gRPC).
</description>
<dependencies>
<!-- DC3 Common Related -->
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-constant</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-model</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,71 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
import java.util.List;
/**
* Protocol-neutral device facade.
* <p>
* Mirrors the four RPCs on {@code api.center.manager.DeviceApi} but returns plain
* BO/Page types so callers never have to touch gRPC or protobuf classes.
* Two implementations back this interface:
* <ul>
* <li>{@code DeviceLocalFacade} — in-process call into {@code DeviceService},
* selected when {@code dc3.facade.mode=local} (single deployment).</li>
* <li>{@code DeviceGrpcFacade} — gRPC call against Manager Center,
* selected when {@code dc3.facade.mode=grpc} (distributed deployment, default).</li>
* </ul>
*
* @author pnoker
* @since 2026.4.19
*/
public interface DeviceFacade {
/**
* Query a single device by id.
*
* @return the device, or {@code null} when the device does not exist.
*/
FacadeDeviceBO selectById(Long id);
/**
* Paginated query.
*
* @return a page of devices (never {@code null}; empty page when nothing matches).
*/
FacadePage<FacadeDeviceBO> selectByPage(FacadeDeviceQuery query);
/**
* List devices attached to a given profile.
*
* @return an immutable list (never {@code null}; empty when nothing matches).
*/
List<FacadeDeviceBO> selectByProfileId(Long profileId);
/**
* List devices attached to a given driver.
*
* @return an immutable list (never {@code null}; empty when nothing matches).
*/
List<FacadeDeviceBO> selectByDriverId(Long driverId);
}
@@ -0,0 +1,52 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
/**
* Protocol-neutral driver facade.
* <p>
* Mirrors the three RPCs on {@code api.center.manager.DriverApi}.
* Implementation selection follows the same {@code dc3.facade.mode} switch
* used by {@link DeviceFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
public interface DriverFacade {
/**
* @return the driver, or {@code null} when it does not exist.
*/
FacadeDriverBO selectById(Long id);
/**
* @return a page of drivers (never {@code null}; empty page when nothing matches).
*/
FacadePage<FacadeDriverBO> selectByPage(FacadeDriverQuery query);
/**
* Resolve the driver that owns a given device.
*
* @return the driver, or {@code null} when the device has no bound driver.
*/
FacadeDriverBO selectByDeviceId(Long deviceId);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
/**
* Protocol-neutral point facade. Mirrors the two RPCs on
* {@code api.center.manager.PointApi}.
*
* @author pnoker
* @since 2026.4.19
*/
public interface PointFacade {
/**
* @return the point, or {@code null} when it does not exist.
*/
FacadePointBO selectById(Long id);
/**
* @return a page of points (never {@code null}; empty page when nothing matches).
*/
FacadePage<FacadePointBO> selectByPage(FacadePointQuery query);
}
@@ -0,0 +1,34 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
/**
* Protocol-neutral tenant facade. Mirrors {@code api.center.auth.TenantApi}.
*
* @author pnoker
* @since 2026.4.19
*/
public interface TenantFacade {
/**
* @return the tenant, or {@code null} when no tenant carries the given code.
*/
FacadeTenantBO selectByCode(String code);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
/**
* Protocol-neutral token facade. Mirrors {@code api.center.auth.TokenApi}.
* <p>
* Returns a plain boolean rather than a {@code TokenBO} because callers only
* care whether the token is valid — the existing RPC only returns an expiry
* string that nobody reads from the gateway.
*
* @author pnoker
* @since 2026.4.19
*/
public interface TokenFacade {
/**
* Validate a token triple against the backing auth service.
*
* @param tenant tenant code
* @param name login name
* @param salt salt the client holds
* @param token token the client holds
* @return {@code true} when the triple is valid and unexpired
*/
boolean checkValid(String tenant, String name, String salt, String token);
}
@@ -0,0 +1,34 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
/**
* Protocol-neutral user facade. Mirrors {@code api.center.auth.UserApi}.
*
* @author pnoker
* @since 2026.4.19
*/
public interface UserFacade {
/**
* @return the user, or {@code null} when the id does not match any user.
*/
FacadeUserBO selectById(Long id);
}
@@ -0,0 +1,34 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.api;
import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
/**
* Protocol-neutral user-login facade. Mirrors {@code api.center.auth.UserLoginApi}.
*
* @author pnoker
* @since 2026.4.19
*/
public interface UserLoginFacade {
/**
* @return the user-login record, or {@code null} when the login name does not exist.
*/
FacadeUserLoginBO selectByName(String name);
}
@@ -0,0 +1,62 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import io.github.pnoker.common.entity.ext.DeviceExt;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
import java.util.List;
/**
* Facade-level device BO.
* <p>
* Field set matches {@code api.center.manager.DeviceApi} wire shape. Intentionally
* excludes driver-side attachments (pointIds, attributeConfig maps) — those remain
* in {@code dc3-common-driver}'s DeviceBO and are not part of the center-to-center
* contract.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadeDeviceBO extends BaseBO {
private String deviceName;
private String deviceCode;
private Long driverId;
private DeviceExt deviceExt;
private EnableFlagEnum enableFlag;
private Long tenantId;
private String signature;
private Integer version;
private List<Long> profileIds;
}
@@ -0,0 +1,58 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import io.github.pnoker.common.entity.ext.DriverExt;
import io.github.pnoker.common.enums.DriverTypeFlagEnum;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
/**
* Facade-level driver BO. Field set matches {@code api.center.manager.DriverApi}.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadeDriverBO extends BaseBO {
private String driverName;
private String driverCode;
private String serviceName;
private String serviceHost;
private DriverTypeFlagEnum driverTypeFlag;
private DriverExt driverExt;
private EnableFlagEnum enableFlag;
private Long tenantId;
private String signature;
private Integer version;
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import io.github.pnoker.common.entity.ext.PointExt;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.enums.PointTypeFlagEnum;
import io.github.pnoker.common.enums.RwFlagEnum;
import lombok.*;
import java.math.BigDecimal;
/**
* Facade-level point BO. Field set matches {@code api.center.manager.PointApi}.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadePointBO extends BaseBO {
private String pointName;
private String pointCode;
private PointTypeFlagEnum pointTypeFlag;
private RwFlagEnum rwFlag;
private BigDecimal baseValue;
private BigDecimal multiple;
private Byte valueDecimal;
private String unit;
private Long profileId;
private PointExt pointExt;
private EnableFlagEnum enableFlag;
private Long tenantId;
private String signature;
private Integer version;
}
@@ -0,0 +1,43 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
/**
* Facade-level tenant BO. Field set matches {@code api.center.auth.TenantApi}
* wire shape (no tenantExt — that is not exposed through gRPC).
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadeTenantBO extends BaseBO {
private String tenantName;
private String tenantCode;
private EnableFlagEnum enableFlag;
}
@@ -0,0 +1,49 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import lombok.*;
/**
* Facade-level user BO. Field set matches {@code api.center.auth.UserApi} wire
* shape — {@code enableFlag} is intentionally absent because the
* {@code GrpcUserDTO} contract does not expose it.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadeUserBO extends BaseBO {
private String nickName;
private String userName;
private String phone;
private String email;
private String socialExt;
private String identityExt;
}
@@ -0,0 +1,45 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.bo;
import io.github.pnoker.common.entity.base.BaseBO;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
/**
* Facade-level user-login BO. Field set matches
* {@code api.center.auth.UserLoginApi} wire shape.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString(callSuper = true)
public class FacadeUserLoginBO extends BaseBO {
private String loginName;
private Long userId;
private Long userPasswordId;
private EnableFlagEnum enableFlag;
}
@@ -0,0 +1,59 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.common;
import lombok.*;
import java.io.Serial;
import java.io.Serializable;
import java.util.Collections;
import java.util.List;
/**
* Transport-neutral page envelope for facade return values.
* <p>
* Intentionally does not extend MyBatis-Plus {@code Page} so that
* {@code dc3-common-facade-api} keeps zero persistence dependencies.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class FacadePage<T> implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private long current;
private long size;
private long total;
private long pages;
private List<T> records;
public static <T> FacadePage<T> empty() {
return new FacadePage<>(1L, 0L, 0L, 0L, Collections.emptyList());
}
}
@@ -0,0 +1,61 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.query;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
import java.io.Serial;
import java.io.Serializable;
/**
* Facade-level device query.
* <p>
* Field set matches the filters supported by {@code DeviceApi.SelectByPage}.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class FacadeDeviceQuery implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Pages page;
private Long tenantId;
private String deviceName;
private String deviceCode;
private Long driverId;
private EnableFlagEnum enableFlag;
private Integer version;
private Long profileId;
}
@@ -0,0 +1,62 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.query;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.enums.DriverTypeFlagEnum;
import io.github.pnoker.common.enums.EnableFlagEnum;
import lombok.*;
import java.io.Serial;
import java.io.Serializable;
/**
* Facade-level driver query.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class FacadeDriverQuery implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Pages page;
private Long tenantId;
private String driverName;
private String driverCode;
private String serviceName;
private String serviceHost;
private DriverTypeFlagEnum driverTypeFlag;
private EnableFlagEnum enableFlag;
private Integer version;
}
@@ -0,0 +1,68 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.entity.query;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.enums.PointTypeFlagEnum;
import io.github.pnoker.common.enums.RwFlagEnum;
import lombok.*;
import java.io.Serial;
import java.io.Serializable;
/**
* Facade-level point query.
*
* @author pnoker
* @since 2026.4.19
*/
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class FacadePointQuery implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private Pages page;
private Long tenantId;
private String pointName;
private String pointCode;
private PointTypeFlagEnum pointTypeFlag;
private RwFlagEnum rwFlag;
private Long profileId;
private EnableFlagEnum enableFlag;
private Integer version;
/**
* Device ID — filter points bound to a specific device.
*/
private Long deviceId;
}
+78
View File
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present the IoT DC3 original author or authors.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common</artifactId>
<version>2026.4.19</version>
</parent>
<name>${project.artifactId}</name>
<artifactId>dc3-common-facade-grpc</artifactId>
<version>2026.4.19</version>
<packaging>jar</packaging>
<inceptionYear>2016</inceptionYear>
<url>https://github.com/pnoker/iot-dc3</url>
<description>
gRPC implementation of the Facade API, selected when dc3.facade.mode=grpc (default).
Holds BlockingStubs and translates between Facade BO/Query and protobuf DTOs.
</description>
<dependencies>
<!-- gRpc Client -->
<dependency>
<groupId>net.devh</groupId>
<artifactId>grpc-spring-boot-starter</artifactId>
</dependency>
<!-- DC3 Api Related -->
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-api-auth</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-api-manager</artifactId>
</dependency>
<!-- DC3 Common Related -->
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-api</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-exception</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-api</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-public</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,126 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.manager.*;
import io.github.pnoker.api.common.GrpcDeviceDTO;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.ManagerConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.DeviceFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcDeviceBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
/**
* gRPC implementation: forwards each call to Manager Center via
* {@link DeviceApiGrpc.DeviceApiBlockingStub}.
* <p>
* Selected when {@code dc3.facade.mode=grpc} (or unset — grpc is the default
* in the auto-configuration declaration).
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class DeviceGrpcFacade implements DeviceFacade {
@GrpcClient(ManagerConstant.SERVICE_NAME)
private DeviceApiGrpc.DeviceApiBlockingStub deviceApiBlockingStub;
@Resource
private FacadeGrpcDeviceBuilder facadeGrpcDeviceBuilder;
@Override
public FacadeDeviceBO selectById(Long id) {
GrpcDeviceQuery request = GrpcDeviceQuery.newBuilder().setDeviceId(id).build();
GrpcRDeviceDTO response = deviceApiBlockingStub.selectByDeviceId(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectById");
return null;
}
return facadeGrpcDeviceBuilder.toFacadeBO(response.getData());
}
@Override
public FacadePage<FacadeDeviceBO> selectByPage(FacadeDeviceQuery query) {
GrpcPageDeviceQuery request = facadeGrpcDeviceBuilder.toGrpcPageQuery(query);
GrpcRPageDeviceDTO response = deviceApiBlockingStub.selectByPage(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByPage");
return FacadePage.empty();
}
GrpcPageDeviceDTO pageDTO = response.getData();
List<FacadeDeviceBO> records = pageDTO.getDataList().stream()
.map(facadeGrpcDeviceBuilder::toFacadeBO)
.toList();
return new FacadePage<>(
pageDTO.getPage().getCurrent(),
pageDTO.getPage().getSize(),
pageDTO.getPage().getTotal(),
pageDTO.getPage().getPages(),
records);
}
@Override
public List<FacadeDeviceBO> selectByProfileId(Long profileId) {
GrpcProfileQuery request = GrpcProfileQuery.newBuilder().setProfileId(profileId).build();
GrpcRDeviceListDTO response = deviceApiBlockingStub.selectByProfileId(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByProfileId");
return Collections.emptyList();
}
return response.getDataList().stream().map(facadeGrpcDeviceBuilder::toFacadeBO).toList();
}
@Override
public List<FacadeDeviceBO> selectByDriverId(Long driverId) {
GrpcDriverQuery request = GrpcDriverQuery.newBuilder().setDriverId(driverId).build();
GrpcRDeviceListDTO response = deviceApiBlockingStub.selectByDriverId(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByDriverId");
return Collections.emptyList();
}
return response.getDataList().stream().map(facadeGrpcDeviceBuilder::toFacadeBO).toList();
}
/**
* NO_RESOURCE is a normal "not found" signal — swallow and let the caller see null / empty.
* Any other non-OK code (server error, param error, etc.) escalates to an exception.
*/
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("DeviceGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("DeviceFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,106 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.manager.*;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.ManagerConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.DriverFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcDriverBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* gRPC DriverFacade: forwards to Manager Center via
* {@link DriverApiGrpc.DriverApiBlockingStub}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class DriverGrpcFacade implements DriverFacade {
@GrpcClient(ManagerConstant.SERVICE_NAME)
private DriverApiGrpc.DriverApiBlockingStub driverApiBlockingStub;
@Resource
private FacadeGrpcDriverBuilder facadeGrpcDriverBuilder;
@Override
public FacadeDriverBO selectById(Long id) {
GrpcDriverQuery request = GrpcDriverQuery.newBuilder().setDriverId(id).build();
GrpcRDriverDTO response = driverApiBlockingStub.selectByDriverId(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectById");
return null;
}
return facadeGrpcDriverBuilder.toFacadeBO(response.getData());
}
@Override
public FacadePage<FacadeDriverBO> selectByPage(FacadeDriverQuery query) {
GrpcPageDriverQuery request = facadeGrpcDriverBuilder.toGrpcPageQuery(query);
GrpcRPageDriverDTO response = driverApiBlockingStub.selectByPage(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByPage");
return FacadePage.empty();
}
GrpcPageDriverDTO pageDTO = response.getData();
List<FacadeDriverBO> records = pageDTO.getDataList().stream()
.map(facadeGrpcDriverBuilder::toFacadeBO)
.toList();
return new FacadePage<>(
pageDTO.getPage().getCurrent(),
pageDTO.getPage().getSize(),
pageDTO.getPage().getTotal(),
pageDTO.getPage().getPages(),
records);
}
@Override
public FacadeDriverBO selectByDeviceId(Long deviceId) {
GrpcDeviceQuery request = GrpcDeviceQuery.newBuilder().setDeviceId(deviceId).build();
GrpcRDriverDTO response = driverApiBlockingStub.selectByDeviceId(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByDeviceId");
return null;
}
return facadeGrpcDriverBuilder.toFacadeBO(response.getData());
}
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("DriverGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("DriverFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,95 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.manager.*;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.ManagerConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.PointFacade;
import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcPointBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* gRPC PointFacade: forwards to Manager Center via
* {@link PointApiGrpc.PointApiBlockingStub}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class PointGrpcFacade implements PointFacade {
@GrpcClient(ManagerConstant.SERVICE_NAME)
private PointApiGrpc.PointApiBlockingStub pointApiBlockingStub;
@Resource
private FacadeGrpcPointBuilder facadeGrpcPointBuilder;
@Override
public FacadePointBO selectById(Long id) {
GrpcPointQuery request = GrpcPointQuery.newBuilder().setPointId(id).build();
GrpcRPointDTO response = pointApiBlockingStub.selectById(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectById");
return null;
}
return facadeGrpcPointBuilder.toFacadeBO(response.getData());
}
@Override
public FacadePage<FacadePointBO> selectByPage(FacadePointQuery query) {
GrpcPagePointQuery request = facadeGrpcPointBuilder.toGrpcPageQuery(query);
GrpcRPagePointDTO response = pointApiBlockingStub.selectByPage(request);
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByPage");
return FacadePage.empty();
}
GrpcPagePointDTO pageDTO = response.getData();
List<FacadePointBO> records = pageDTO.getDataList().stream()
.map(facadeGrpcPointBuilder::toFacadeBO)
.toList();
return new FacadePage<>(
pageDTO.getPage().getCurrent(),
pageDTO.getPage().getSize(),
pageDTO.getPage().getTotal(),
pageDTO.getPage().getPages(),
records);
}
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("PointGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("PointFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.auth.GrpcCodeQuery;
import io.github.pnoker.api.center.auth.GrpcRTenantDTO;
import io.github.pnoker.api.center.auth.TenantApiGrpc;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.AuthConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.TenantFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcTenantBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
/**
* gRPC {@link TenantFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class TenantGrpcFacade implements TenantFacade {
@GrpcClient(AuthConstant.SERVICE_NAME)
private TenantApiGrpc.TenantApiBlockingStub tenantApiBlockingStub;
@Resource
private FacadeGrpcTenantBuilder facadeGrpcTenantBuilder;
@Override
public FacadeTenantBO selectByCode(String code) {
GrpcRTenantDTO response = tenantApiBlockingStub.selectByCode(GrpcCodeQuery.newBuilder().setCode(code).build());
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByCode");
return null;
}
return facadeGrpcTenantBuilder.toFacadeBO(response.getData());
}
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("TenantGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("TenantFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.auth.GrpcLoginQuery;
import io.github.pnoker.api.center.auth.GrpcRTokenDTO;
import io.github.pnoker.api.center.auth.TokenApiGrpc;
import io.github.pnoker.common.constant.service.AuthConstant;
import io.github.pnoker.common.facade.api.TokenFacade;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
/**
* gRPC {@link TokenFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class TokenGrpcFacade implements TokenFacade {
@GrpcClient(AuthConstant.SERVICE_NAME)
private TokenApiGrpc.TokenApiBlockingStub tokenApiBlockingStub;
@Override
public boolean checkValid(String tenant, String name, String salt, String token) {
GrpcLoginQuery login = GrpcLoginQuery.newBuilder()
.setTenant(tenant)
.setName(name)
.setSalt(salt)
.setToken(token)
.build();
GrpcRTokenDTO response = tokenApiBlockingStub.checkValid(login);
return response.getResult().getOk();
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.auth.GrpcIdQuery;
import io.github.pnoker.api.center.auth.GrpcRUserDTO;
import io.github.pnoker.api.center.auth.UserApiGrpc;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.AuthConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.UserFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcUserBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
/**
* gRPC {@link UserFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class UserGrpcFacade implements UserFacade {
@GrpcClient(AuthConstant.SERVICE_NAME)
private UserApiGrpc.UserApiBlockingStub userApiBlockingStub;
@Resource
private FacadeGrpcUserBuilder facadeGrpcUserBuilder;
@Override
public FacadeUserBO selectById(Long id) {
GrpcRUserDTO response = userApiBlockingStub.selectById(GrpcIdQuery.newBuilder().setId(id).build());
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectById");
return null;
}
return facadeGrpcUserBuilder.toFacadeBO(response.getData());
}
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("UserGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("UserFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc;
import io.github.pnoker.api.center.auth.GrpcNameQuery;
import io.github.pnoker.api.center.auth.GrpcRUserLoginDTO;
import io.github.pnoker.api.center.auth.UserLoginApiGrpc;
import io.github.pnoker.api.common.GrpcR;
import io.github.pnoker.common.constant.service.AuthConstant;
import io.github.pnoker.common.enums.ResponseEnum;
import io.github.pnoker.common.exception.ServiceException;
import io.github.pnoker.common.facade.api.UserLoginFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
import io.github.pnoker.common.facade.grpc.builder.FacadeGrpcUserLoginBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.devh.boot.grpc.client.inject.GrpcClient;
import org.springframework.stereotype.Component;
/**
* gRPC {@link UserLoginFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class UserLoginGrpcFacade implements UserLoginFacade {
@GrpcClient(AuthConstant.SERVICE_NAME)
private UserLoginApiGrpc.UserLoginApiBlockingStub userLoginApiBlockingStub;
@Resource
private FacadeGrpcUserLoginBuilder facadeGrpcUserLoginBuilder;
@Override
public FacadeUserLoginBO selectByName(String name) {
GrpcRUserLoginDTO response = userLoginApiBlockingStub.selectByName(GrpcNameQuery.newBuilder().setName(name).build());
if (!response.getResult().getOk()) {
guardOrThrow(response.getResult(), "selectByName");
return null;
}
return facadeGrpcUserLoginBuilder.toFacadeBO(response.getData());
}
private void guardOrThrow(GrpcR result, String op) {
String code = result.getCode();
if (ResponseEnum.NO_RESOURCE.getCode().equals(code)) {
log.debug("UserLoginGrpcFacade.{} => no resource", op);
return;
}
throw new ServiceException("UserLoginFacade." + op + " failed: [" + code + "] " + result.getMessage());
}
}
@@ -0,0 +1,105 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.manager.GrpcPageDeviceQuery;
import io.github.pnoker.api.common.GrpcDeviceDTO;
import io.github.pnoker.api.common.GrpcPage;
import io.github.pnoker.common.constant.common.DefaultConstant;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.entity.ext.DeviceExt;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
import io.github.pnoker.common.optional.LongOptional;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import io.github.pnoker.common.utils.JsonUtil;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Optional;
/**
* Converts between {@code dc3-common-facade-api} shapes and the protobuf types
* generated from {@code api/center/manager/manager_device.proto}.
* <p>
* Hand-rolled rather than MapStruct because protobuf builders expose dozens of
* generated accessors ({@code mergeFrom}, {@code clearField}, {@code *Bytes}, ...)
* that would each need an explicit {@code @Mapping(target = "...", ignore = true)}.
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcDeviceBuilder {
public GrpcPageDeviceQuery toGrpcPageQuery(FacadeDeviceQuery query) {
GrpcPageDeviceQuery.Builder builder = GrpcPageDeviceQuery.newBuilder();
Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
GrpcPage.Builder page = GrpcPage.newBuilder()
.setCurrent(pages.getCurrent())
.setSize(pages.getSize());
builder.setPage(page);
LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
StringOptional.ofNullable(query.getDeviceName()).ifPresent(builder::setDeviceName);
StringOptional.ofNullable(query.getDeviceCode()).ifPresent(builder::setDeviceCode);
LongOptional.ofNullable(query.getDriverId()).ifPresent(builder::setDriverId);
LongOptional.ofNullable(query.getProfileId()).ifPresent(builder::setProfileId);
Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
value -> builder.setEnableFlag(value.getIndex()),
() -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
);
return builder.build();
}
public FacadeDeviceBO toFacadeBO(GrpcDeviceDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadeDeviceBO bo = new FacadeDeviceBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getDeviceName()).ifPresent(bo::setDeviceName);
StringOptional.ofNullable(dto.getDeviceCode()).ifPresent(bo::setDeviceCode);
LongOptional.ofNullable(dto.getDriverId()).ifPresent(bo::setDriverId);
StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
bo.setVersion(dto.getVersion());
}
byte enableIndex = (byte) dto.getEnableFlag();
Optional.ofNullable(EnableFlagEnum.ofIndex(enableIndex)).ifPresent(bo::setEnableFlag);
StringOptional.ofNullable(dto.getDeviceExt())
.ifPresent(value -> bo.setDeviceExt(JsonUtil.parseObject(value, DeviceExt.class)));
if (dto.getProfileIdsCount() > 0) {
bo.setProfileIds(new ArrayList<>(dto.getProfileIdsList()));
}
return bo;
}
}
@@ -0,0 +1,108 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.manager.GrpcPageDriverQuery;
import io.github.pnoker.api.common.GrpcDriverDTO;
import io.github.pnoker.api.common.GrpcPage;
import io.github.pnoker.common.constant.common.DefaultConstant;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.entity.ext.DriverExt;
import io.github.pnoker.common.enums.DriverTypeFlagEnum;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
import io.github.pnoker.common.optional.LongOptional;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import io.github.pnoker.common.utils.JsonUtil;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
/**
* Hand-rolled conversion between facade shapes and protobuf driver types.
* <p>
* Driver uses {@code NULL_INT = -1} as the "not set" marker for
* {@code driverTypeFlag} (unlike Device, which piggy-backs on {@code DEFAULT_INT = 0}).
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcDriverBuilder {
public GrpcPageDriverQuery toGrpcPageQuery(FacadeDriverQuery query) {
GrpcPageDriverQuery.Builder builder = GrpcPageDriverQuery.newBuilder();
Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
GrpcPage.Builder page = GrpcPage.newBuilder()
.setCurrent(pages.getCurrent())
.setSize(pages.getSize());
builder.setPage(page);
LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
StringOptional.ofNullable(query.getDriverName()).ifPresent(builder::setDriverName);
StringOptional.ofNullable(query.getDriverCode()).ifPresent(builder::setDriverCode);
StringOptional.ofNullable(query.getServiceName()).ifPresent(builder::setServiceName);
StringOptional.ofNullable(query.getServiceHost()).ifPresent(builder::setServiceHost);
Optional.ofNullable(query.getDriverTypeFlag()).ifPresentOrElse(
value -> builder.setDriverTypeFlag(value.getIndex()),
() -> builder.setDriverTypeFlag(DefaultConstant.NULL_INT)
);
Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
value -> builder.setEnableFlag(value.getIndex()),
() -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
);
return builder.build();
}
public FacadeDriverBO toFacadeBO(GrpcDriverDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadeDriverBO bo = new FacadeDriverBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getDriverName()).ifPresent(bo::setDriverName);
StringOptional.ofNullable(dto.getDriverCode()).ifPresent(bo::setDriverCode);
StringOptional.ofNullable(dto.getServiceName()).ifPresent(bo::setServiceName);
StringOptional.ofNullable(dto.getServiceHost()).ifPresent(bo::setServiceHost);
StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
bo.setVersion(dto.getVersion());
}
int driverType = dto.getDriverTypeFlag();
if (driverType != DefaultConstant.NULL_INT) {
Optional.ofNullable(DriverTypeFlagEnum.ofIndex((byte) driverType)).ifPresent(bo::setDriverTypeFlag);
}
Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
StringOptional.ofNullable(dto.getDriverExt())
.ifPresent(value -> bo.setDriverExt(JsonUtil.parseObject(value, DriverExt.class)));
return bo;
}
}
@@ -0,0 +1,132 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.manager.GrpcPagePointQuery;
import io.github.pnoker.api.common.GrpcPage;
import io.github.pnoker.api.common.GrpcPointDTO;
import io.github.pnoker.common.constant.common.DefaultConstant;
import io.github.pnoker.common.entity.common.Pages;
import io.github.pnoker.common.entity.ext.PointExt;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.enums.PointTypeFlagEnum;
import io.github.pnoker.common.enums.RwFlagEnum;
import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
import io.github.pnoker.common.optional.LongOptional;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import io.github.pnoker.common.utils.JsonUtil;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Objects;
import java.util.Optional;
/**
* Hand-rolled conversion between facade shapes and protobuf point types.
* <p>
* {@code baseValue} / {@code multiple} are {@link BigDecimal} on the Java side but
* {@code double} on the wire — precision loss is inherited from the existing proto
* contract, not introduced here.
* <p>
* Uses {@link DefaultConstant#NULL_INT NULL_INT} as "not set" for
* {@code pointTypeFlag} / {@code rwFlag} / {@code profileId}, matching
* {@code GrpcPointBuilder}.
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcPointBuilder {
public GrpcPagePointQuery toGrpcPageQuery(FacadePointQuery query) {
GrpcPagePointQuery.Builder builder = GrpcPagePointQuery.newBuilder();
Pages pages = Objects.isNull(query.getPage()) ? new Pages() : query.getPage();
GrpcPage.Builder page = GrpcPage.newBuilder()
.setCurrent(pages.getCurrent())
.setSize(pages.getSize());
builder.setPage(page);
LongOptional.ofNullable(query.getTenantId()).ifPresent(builder::setTenantId);
StringOptional.ofNullable(query.getPointName()).ifPresent(builder::setPointName);
StringOptional.ofNullable(query.getPointCode()).ifPresent(builder::setPointCode);
LongOptional.ofNullable(query.getDeviceId()).ifPresent(builder::setDeviceId);
Optional.ofNullable(query.getPointTypeFlag()).ifPresentOrElse(
value -> builder.setPointTypeFlag(value.getIndex()),
() -> builder.setPointTypeFlag(DefaultConstant.NULL_INT)
);
Optional.ofNullable(query.getRwFlag()).ifPresentOrElse(
value -> builder.setRwFlag(value.getIndex()),
() -> builder.setRwFlag(DefaultConstant.NULL_INT)
);
Optional.ofNullable(query.getProfileId()).ifPresentOrElse(
builder::setProfileId,
() -> builder.setProfileId(DefaultConstant.NULL_INT)
);
Optional.ofNullable(query.getEnableFlag()).ifPresentOrElse(
value -> builder.setEnableFlag(value.getIndex()),
() -> builder.setEnableFlag(DefaultConstant.DEFAULT_INT)
);
return builder.build();
}
public FacadePointBO toFacadeBO(GrpcPointDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadePointBO bo = new FacadePointBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getPointName()).ifPresent(bo::setPointName);
StringOptional.ofNullable(dto.getPointCode()).ifPresent(bo::setPointCode);
StringOptional.ofNullable(dto.getUnit()).ifPresent(bo::setUnit);
StringOptional.ofNullable(dto.getSignature()).ifPresent(bo::setSignature);
LongOptional.ofNullable(dto.getTenantId()).ifPresent(bo::setTenantId);
LongOptional.ofNullable(dto.getProfileId()).ifPresent(bo::setProfileId);
bo.setBaseValue(BigDecimal.valueOf(dto.getBaseValue()));
bo.setMultiple(BigDecimal.valueOf(dto.getMultiple()));
bo.setValueDecimal((byte) dto.getValueDecimal());
if (dto.getVersion() != DefaultConstant.DEFAULT_INT) {
bo.setVersion(dto.getVersion());
}
int pointType = dto.getPointTypeFlag();
if (pointType != DefaultConstant.NULL_INT) {
Optional.ofNullable(PointTypeFlagEnum.ofIndex((byte) pointType)).ifPresent(bo::setPointTypeFlag);
}
int rw = dto.getRwFlag();
if (rw != DefaultConstant.NULL_INT) {
Optional.ofNullable(RwFlagEnum.ofIndex((byte) rw)).ifPresent(bo::setRwFlag);
}
Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
StringOptional.ofNullable(dto.getPointExt())
.ifPresent(value -> bo.setPointExt(JsonUtil.parseObject(value, PointExt.class)));
return bo;
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.auth.GrpcTenantDTO;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
/**
* {@code GrpcTenantDTO} → {@link FacadeTenantBO}.
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcTenantBuilder {
public FacadeTenantBO toFacadeBO(GrpcTenantDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadeTenantBO bo = new FacadeTenantBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getTenantName()).ifPresent(bo::setTenantName);
StringOptional.ofNullable(dto.getTenantCode()).ifPresent(bo::setTenantCode);
Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
return bo;
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.auth.GrpcUserDTO;
import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* {@code GrpcUserDTO} → {@link FacadeUserBO}. {@code socialExt}/{@code identityExt}
* stay as opaque JSON strings — keeping the wire representation untouched.
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcUserBuilder {
public FacadeUserBO toFacadeBO(GrpcUserDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadeUserBO bo = new FacadeUserBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getNickName()).ifPresent(bo::setNickName);
StringOptional.ofNullable(dto.getUserName()).ifPresent(bo::setUserName);
StringOptional.ofNullable(dto.getPhone()).ifPresent(bo::setPhone);
StringOptional.ofNullable(dto.getEmail()).ifPresent(bo::setEmail);
StringOptional.ofNullable(dto.getSocialExt()).ifPresent(bo::setSocialExt);
StringOptional.ofNullable(dto.getIdentityExt()).ifPresent(bo::setIdentityExt);
return bo;
}
}
@@ -0,0 +1,55 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.builder;
import io.github.pnoker.api.center.auth.GrpcUserLoginDTO;
import io.github.pnoker.common.enums.EnableFlagEnum;
import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
import io.github.pnoker.common.optional.LongOptional;
import io.github.pnoker.common.optional.StringOptional;
import io.github.pnoker.common.utils.GrpcBuilderUtil;
import org.springframework.stereotype.Component;
import java.util.Objects;
import java.util.Optional;
/**
* {@code GrpcUserLoginDTO} → {@link FacadeUserLoginBO}.
*
* @author pnoker
* @since 2026.4.19
*/
@Component
public class FacadeGrpcUserLoginBuilder {
public FacadeUserLoginBO toFacadeBO(GrpcUserLoginDTO dto) {
if (Objects.isNull(dto)) {
return null;
}
FacadeUserLoginBO bo = new FacadeUserLoginBO();
GrpcBuilderUtil.buildBaseBOByGrpcBase(dto.getBase(), bo);
StringOptional.ofNullable(dto.getLoginName()).ifPresent(bo::setLoginName);
LongOptional.ofNullable(dto.getUserId()).ifPresent(bo::setUserId);
LongOptional.ofNullable(dto.getUserPasswordId()).ifPresent(bo::setUserPasswordId);
Optional.ofNullable(EnableFlagEnum.ofIndex((byte) dto.getEnableFlag())).ifPresent(bo::setEnableFlag);
return bo;
}
}
@@ -0,0 +1,37 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.grpc.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
/**
* Auto-configuration for the gRPC facade implementation. Active by default
* (when {@code dc3.facade.mode} is absent) or when explicitly set to
* {@code grpc}. Scans the facade-grpc package so the {@code @Component} beans
* are registered.
*
* @author pnoker
* @since 2026.4.19
*/
@AutoConfiguration
@ConditionalOnProperty(name = "dc3.facade.mode", havingValue = "grpc", matchIfMissing = true)
@ComponentScan("io.github.pnoker.common.facade.grpc")
public class GrpcFacadeAutoConfiguration {
}
@@ -0,0 +1,18 @@
#
# Copyright 2016-present the IoT DC3 original author or authors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
io.github.pnoker.common.facade.grpc.config.GrpcFacadeAutoConfiguration
@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
~ Copyright 2016-present the IoT DC3 original author or authors.
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU Affero 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 Affero General Public License for more details.
~
~ You should have received a copy of the GNU Affero General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common</artifactId>
<version>2026.4.19</version>
</parent>
<name>${project.artifactId}</name>
<artifactId>dc3-common-facade-local</artifactId>
<version>2026.4.19</version>
<packaging>jar</packaging>
<inceptionYear>2016</inceptionYear>
<url>https://github.com/pnoker/iot-dc3</url>
<description>
Local (in-process) implementation of the Facade API, selected when
dc3.facade.mode=local. Directly invokes manager services without gRPC.
</description>
<dependencies>
<!-- DC3 Common Related -->
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-api</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-auth</artifactId>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-manager</artifactId>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,93 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.pnoker.common.facade.api.DeviceFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
import io.github.pnoker.common.facade.local.builder.FacadeDeviceBuilder;
import io.github.pnoker.common.manager.entity.bo.DeviceBO;
import io.github.pnoker.common.manager.entity.query.DeviceQuery;
import io.github.pnoker.common.manager.service.DeviceService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
/**
* In-process implementation: routes each call straight into {@link DeviceService}.
* <p>
* Selected when {@code dc3.facade.mode=local}. Carries zero serialization cost
* — the same JVM handles both caller and service.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class DeviceLocalFacade implements DeviceFacade {
@Resource
private DeviceService deviceService;
@Resource
private FacadeDeviceBuilder facadeDeviceBuilder;
@Override
public FacadeDeviceBO selectById(Long id) {
DeviceBO managerBO = deviceService.selectById(id);
return Objects.isNull(managerBO) ? null : facadeDeviceBuilder.toFacadeBO(managerBO);
}
@Override
public FacadePage<FacadeDeviceBO> selectByPage(FacadeDeviceQuery query) {
DeviceQuery managerQuery = facadeDeviceBuilder.toManagerQuery(query);
Page<DeviceBO> page = deviceService.selectByPage(managerQuery);
if (Objects.isNull(page)) {
return FacadePage.empty();
}
List<FacadeDeviceBO> records = page.getRecords().stream()
.map(facadeDeviceBuilder::toFacadeBO)
.toList();
return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
}
@Override
public List<FacadeDeviceBO> selectByProfileId(Long profileId) {
List<DeviceBO> list = deviceService.selectByProfileId(profileId);
if (Objects.isNull(list) || list.isEmpty()) {
return Collections.emptyList();
}
return list.stream().map(facadeDeviceBuilder::toFacadeBO).toList();
}
@Override
public List<FacadeDeviceBO> selectByDriverId(Long driverId) {
List<DeviceBO> list = deviceService.selectByDriverId(driverId);
if (Objects.isNull(list) || list.isEmpty()) {
return Collections.emptyList();
}
return list.stream().map(facadeDeviceBuilder::toFacadeBO).toList();
}
}
@@ -0,0 +1,77 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.pnoker.common.facade.api.DriverFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
import io.github.pnoker.common.facade.local.builder.FacadeDriverBuilder;
import io.github.pnoker.common.manager.entity.bo.DriverBO;
import io.github.pnoker.common.manager.entity.query.DriverQuery;
import io.github.pnoker.common.manager.service.DriverService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
/**
* In-process DriverFacade implementation.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class DriverLocalFacade implements DriverFacade {
@Resource
private DriverService driverService;
@Resource
private FacadeDriverBuilder facadeDriverBuilder;
@Override
public FacadeDriverBO selectById(Long id) {
DriverBO managerBO = driverService.selectById(id);
return Objects.isNull(managerBO) ? null : facadeDriverBuilder.toFacadeBO(managerBO);
}
@Override
public FacadePage<FacadeDriverBO> selectByPage(FacadeDriverQuery query) {
DriverQuery managerQuery = facadeDriverBuilder.toManagerQuery(query);
Page<DriverBO> page = driverService.selectByPage(managerQuery);
if (Objects.isNull(page)) {
return FacadePage.empty();
}
List<FacadeDriverBO> records = page.getRecords().stream()
.map(facadeDriverBuilder::toFacadeBO)
.toList();
return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
}
@Override
public FacadeDriverBO selectByDeviceId(Long deviceId) {
DriverBO managerBO = driverService.selectByDeviceId(deviceId);
return Objects.isNull(managerBO) ? null : facadeDriverBuilder.toFacadeBO(managerBO);
}
}
@@ -0,0 +1,71 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.pnoker.common.facade.api.PointFacade;
import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
import io.github.pnoker.common.facade.entity.common.FacadePage;
import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
import io.github.pnoker.common.facade.local.builder.FacadePointBuilder;
import io.github.pnoker.common.manager.entity.bo.PointBO;
import io.github.pnoker.common.manager.entity.query.PointQuery;
import io.github.pnoker.common.manager.service.PointService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
/**
* In-process PointFacade implementation.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class PointLocalFacade implements PointFacade {
@Resource
private PointService pointService;
@Resource
private FacadePointBuilder facadePointBuilder;
@Override
public FacadePointBO selectById(Long id) {
PointBO managerBO = pointService.selectById(id);
return Objects.isNull(managerBO) ? null : facadePointBuilder.toFacadeBO(managerBO);
}
@Override
public FacadePage<FacadePointBO> selectByPage(FacadePointQuery query) {
PointQuery managerQuery = facadePointBuilder.toManagerQuery(query);
Page<PointBO> page = pointService.selectByPage(managerQuery);
if (Objects.isNull(page)) {
return FacadePage.empty();
}
List<FacadePointBO> records = page.getRecords().stream()
.map(facadePointBuilder::toFacadeBO)
.toList();
return new FacadePage<>(page.getCurrent(), page.getSize(), page.getTotal(), page.getPages(), records);
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import io.github.pnoker.common.auth.entity.bo.TenantBO;
import io.github.pnoker.common.auth.service.TenantService;
import io.github.pnoker.common.facade.api.TenantFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
import io.github.pnoker.common.facade.local.builder.FacadeTenantBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* In-process {@link TenantFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class TenantLocalFacade implements TenantFacade {
@Resource
private TenantService tenantService;
@Resource
private FacadeTenantBuilder facadeTenantBuilder;
@Override
public FacadeTenantBO selectByCode(String code) {
TenantBO bo = tenantService.selectByCode(code);
return Objects.isNull(bo) ? null : facadeTenantBuilder.toFacadeBO(bo);
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import io.github.pnoker.common.auth.biz.TokenService;
import io.github.pnoker.common.auth.entity.bean.TokenValid;
import io.github.pnoker.common.facade.api.TokenFacade;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* In-process {@link TokenFacade}. Collapses the three-way outcome of
* {@code TokenService.checkValid} (null / invalid / valid) into a boolean.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class TokenLocalFacade implements TokenFacade {
@Resource
private TokenService tokenService;
@Override
public boolean checkValid(String tenant, String name, String salt, String token) {
TokenValid entity = tokenService.checkValid(name, salt, token, tenant);
return Objects.nonNull(entity) && entity.isValid();
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import io.github.pnoker.common.auth.entity.bo.UserBO;
import io.github.pnoker.common.auth.service.UserService;
import io.github.pnoker.common.facade.api.UserFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
import io.github.pnoker.common.facade.local.builder.FacadeUserBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* In-process {@link UserFacade}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class UserLocalFacade implements UserFacade {
@Resource
private UserService userService;
@Resource
private FacadeUserBuilder facadeUserBuilder;
@Override
public FacadeUserBO selectById(Long id) {
UserBO bo = userService.selectById(id);
return Objects.isNull(bo) ? null : facadeUserBuilder.toFacadeBO(bo);
}
}
@@ -0,0 +1,53 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local;
import io.github.pnoker.common.auth.entity.bo.UserLoginBO;
import io.github.pnoker.common.auth.service.UserLoginService;
import io.github.pnoker.common.facade.api.UserLoginFacade;
import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
import io.github.pnoker.common.facade.local.builder.FacadeUserLoginBuilder;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Objects;
/**
* In-process {@link UserLoginFacade}. Matches {@code UserLoginServer} which
* also calls {@code selectByLoginName(name, false)}.
*
* @author pnoker
* @since 2026.4.19
*/
@Slf4j
@Component
public class UserLoginLocalFacade implements UserLoginFacade {
@Resource
private UserLoginService userLoginService;
@Resource
private FacadeUserLoginBuilder facadeUserLoginBuilder;
@Override
public FacadeUserLoginBO selectByName(String name) {
UserLoginBO bo = userLoginService.selectByLoginName(name, false);
return Objects.isNull(bo) ? null : facadeUserLoginBuilder.toFacadeBO(bo);
}
}
@@ -0,0 +1,40 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.facade.entity.bo.FacadeDeviceBO;
import io.github.pnoker.common.facade.entity.query.FacadeDeviceQuery;
import io.github.pnoker.common.manager.entity.bo.DeviceBO;
import io.github.pnoker.common.manager.entity.query.DeviceQuery;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.Mapper;
/**
* Converts between the facade-api shapes and {@code dc3-common-manager} internals.
* Field sets are aligned by design, so MapStruct auto-maps every property.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadeDeviceBuilder {
DeviceQuery toManagerQuery(FacadeDeviceQuery facadeQuery);
FacadeDeviceBO toFacadeBO(DeviceBO managerBO);
}
@@ -0,0 +1,39 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.facade.entity.bo.FacadeDriverBO;
import io.github.pnoker.common.facade.entity.query.FacadeDriverQuery;
import io.github.pnoker.common.manager.entity.bo.DriverBO;
import io.github.pnoker.common.manager.entity.query.DriverQuery;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.Mapper;
/**
* FacadeDriver ↔ manager DriverBO/Query mapper. Field sets are aligned.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadeDriverBuilder {
DriverQuery toManagerQuery(FacadeDriverQuery facadeQuery);
FacadeDriverBO toFacadeBO(DriverBO managerBO);
}
@@ -0,0 +1,39 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.facade.entity.bo.FacadePointBO;
import io.github.pnoker.common.facade.entity.query.FacadePointQuery;
import io.github.pnoker.common.manager.entity.bo.PointBO;
import io.github.pnoker.common.manager.entity.query.PointQuery;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.Mapper;
/**
* FacadePoint ↔ manager PointBO/Query mapper.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadePointBuilder {
PointQuery toManagerQuery(FacadePointQuery facadeQuery);
FacadePointBO toFacadeBO(PointBO managerBO);
}
@@ -0,0 +1,36 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.auth.entity.bo.TenantBO;
import io.github.pnoker.common.facade.entity.bo.FacadeTenantBO;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.Mapper;
/**
* FacadeTenant ↔ auth TenantBO mapper. {@code tenantExt} is absent from the
* facade shape and therefore dropped by MapStruct.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadeTenantBuilder {
FacadeTenantBO toFacadeBO(TenantBO authBO);
}
@@ -0,0 +1,62 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.auth.entity.bo.UserBO;
import io.github.pnoker.common.entity.ext.UserIdentityExt;
import io.github.pnoker.common.entity.ext.UserSocialExt;
import io.github.pnoker.common.facade.entity.bo.FacadeUserBO;
import io.github.pnoker.common.utils.JsonUtil;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.AfterMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import java.util.Objects;
/**
* FacadeUser ↔ auth UserBO mapper.
* <p>
* {@code UserBO} carries typed {@code UserSocialExt}/{@code UserIdentityExt}
* objects, while the facade contract (matching {@code GrpcUserDTO}) carries
* their JSON forms, so these two fields need an explicit conversion.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadeUserBuilder {
@Mapping(target = "socialExt", ignore = true)
@Mapping(target = "identityExt", ignore = true)
FacadeUserBO toFacadeBO(UserBO authBO);
@AfterMapping
default void afterProcess(UserBO authBO, @MappingTarget FacadeUserBO facadeBO) {
UserSocialExt social = authBO.getSocialExt();
if (Objects.nonNull(social)) {
facadeBO.setSocialExt(JsonUtil.toJsonString(social));
}
UserIdentityExt identity = authBO.getIdentityExt();
if (Objects.nonNull(identity)) {
facadeBO.setIdentityExt(JsonUtil.toJsonString(identity));
}
}
}
@@ -0,0 +1,35 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.builder;
import io.github.pnoker.common.auth.entity.bo.UserLoginBO;
import io.github.pnoker.common.facade.entity.bo.FacadeUserLoginBO;
import io.github.pnoker.common.utils.MapStructUtil;
import org.mapstruct.Mapper;
/**
* FacadeUserLogin ↔ auth UserLoginBO mapper.
*
* @author pnoker
* @since 2026.4.19
*/
@Mapper(componentModel = "spring", uses = {MapStructUtil.class})
public interface FacadeUserLoginBuilder {
FacadeUserLoginBO toFacadeBO(UserLoginBO authBO);
}
@@ -0,0 +1,36 @@
/*
* Copyright 2016-present the IoT DC3 original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package io.github.pnoker.common.facade.local.config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.ComponentScan;
/**
* Auto-configuration for the local facade implementation. Active only when
* {@code dc3.facade.mode=local}. Scans the facade-local package so the
* {@code @Component}/{@code @Mapper} beans are registered.
*
* @author pnoker
* @since 2026.4.19
*/
@AutoConfiguration
@ConditionalOnProperty(name = "dc3.facade.mode", havingValue = "local")
@ComponentScan("io.github.pnoker.common.facade.local")
public class LocalFacadeAutoConfiguration {
}
@@ -0,0 +1,18 @@
#
# Copyright 2016-present the IoT DC3 original author or authors.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
io.github.pnoker.common.facade.local.config.LocalFacadeAutoConfiguration
+18
View File
@@ -82,6 +82,9 @@
<module>dc3-common-data</module>
<module>dc3-common-driver</module>
<module>dc3-common-exception</module>
<module>dc3-common-facade-api</module>
<module>dc3-common-facade-grpc</module>
<module>dc3-common-facade-local</module>
<module>dc3-common-gateway</module>
<module>dc3-common-log</module>
<module>dc3-common-manager</module>
@@ -213,6 +216,21 @@
<artifactId>dc3-common-exception</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-api</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-grpc</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-local</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-gateway</artifactId>
+15
View File
@@ -141,6 +141,21 @@
<artifactId>dc3-common-exception</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-api</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-grpc</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-facade-local</artifactId>
<version>${dc3.version}</version>
</dependency>
<dependency>
<groupId>io.github.pnoker</groupId>
<artifactId>dc3-common-gateway</artifactId>