From 7885cede659f3255be56f77c1eef2ada7387d6f1 Mon Sep 17 00:00:00 2001
From: chenyc <501753378@qq.com>
Date: 星期日, 22 三月 2026 16:23:21 +0800
Subject: [PATCH] 初始化项目

---
 src/aliyunClient.js |  158 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 158 insertions(+), 0 deletions(-)

diff --git a/src/aliyunClient.js b/src/aliyunClient.js
new file mode 100644
index 0000000..af6f19c
--- /dev/null
+++ b/src/aliyunClient.js
@@ -0,0 +1,158 @@
+const aliyunIot = require("aliyun-iot-device-sdk");
+const logger = require("./logger");
+const config = require("./config");
+const { getAliyunDeviceSecret } = require("./api");
+
+// 阿里云物联网对接管理器:按设备号获取三元组、建立 iotDevice,并在每次收到数据时上报属性
+function createAliyunManager({ propertyMapper }) {
+  if (!config.aliyun || !config.aliyun.enabled) {
+    logger.info("Aliyun IoT is disabled by config");
+    return {
+      reportDeviceData: () => {},
+      closeAll: () => {}
+    };
+  }
+
+  // 设备号 -> { iotDevice, productKey, deviceName }
+  const deviceMap = new Map();
+
+  async function ensureIotDevice(deviceNumber) {
+    let entry = deviceMap.get(deviceNumber);
+    if (entry && entry.iotDevice) {
+      return entry;
+    }
+
+    try {
+      logger.info("Request Aliyun device secret", { deviceNumber });
+      // 复用旧项目的 api.js 约定:第一个参数是相对路径
+      const resp = await getAliyunDeviceSecret(
+        "device/info/getAliyunDeviceSecret",
+        deviceNumber
+      );
+      const body = resp && resp.data ? resp.data : null;
+      if (!body || !body.data) {
+        logger.warn("Aliyun device secret response invalid", {
+          deviceNumber,
+          body
+        });
+        return null;
+      }
+      const data = body.data;
+      if (!data.productKey || !data.deviceName || !data.deviceSecret) {
+        logger.warn("Aliyun device secret missing fields", {
+          deviceNumber,
+          data
+        });
+        return null;
+      }
+
+      const productKey = data.productKey;
+      const deviceName = data.deviceName;
+      const deviceSecret = data.deviceSecret;
+
+      logger.info("Creating Aliyun IoT device", {
+        deviceNumber,
+        productKey,
+        deviceName
+      });
+
+      const iotDevice = aliyunIot.device({
+        ProductKey: productKey,
+        DeviceName: deviceName,
+        DeviceSecret: deviceSecret
+      });
+
+      iotDevice.on("connect", () => {
+        logger.info("Aliyun IoT connected", { deviceNumber, productKey, deviceName });
+      });
+
+      iotDevice.on("error", (err) => {
+        logger.error("Aliyun IoT error", {
+          deviceNumber,
+          error: err.message || err
+        });
+      });
+
+      iotDevice.on("close", () => {
+        logger.warn("Aliyun IoT connection closed", { deviceNumber });
+      });
+
+      entry = { iotDevice, productKey, deviceName };
+      deviceMap.set(deviceNumber, entry);
+      return entry;
+    } catch (err) {
+      logger.error("ensureIotDevice error", {
+        deviceNumber,
+        error: err.message || err
+      });
+      return null;
+    }
+  }
+
+  async function reportDeviceData(deviceNumber, rawData) {
+    try {
+      const entry = await ensureIotDevice(deviceNumber);
+      if (!entry || !entry.iotDevice) {
+        return;
+      }
+
+      // 使用 PropertyMapper 将内部字段转换为阿里云物模型属性对象
+      let props = rawData;
+      if (propertyMapper && typeof propertyMapper.transformForAliyun === "function") {
+        try {
+          props = propertyMapper.transformForAliyun(rawData);
+        } catch (e) {
+          logger.error("transformForAliyun error", {
+            deviceNumber,
+            error: e.message || e
+          });
+          props = rawData;
+        }
+      }
+
+      // 记录准备上报到阿里云的物模型数据,便于和设备原始数据对照
+      logger.info("Aliyun postProps payload", {
+        deviceNumber,
+        productKey: entry.productKey,
+        deviceName: entry.deviceName,
+        props
+      });
+
+      entry.iotDevice.postProps(props, (res) => {
+        if (res && res.message === "success") {
+          logger.info("Aliyun postProps success", { deviceNumber });
+        } else {
+          logger.error("Aliyun postProps failed", { deviceNumber, res });
+        }
+      });
+    } catch (err) {
+      logger.error("reportDeviceData error", {
+        deviceNumber,
+        error: err.message || err
+      });
+    }
+  }
+
+  function closeAll() {
+    for (const [deviceNumber, entry] of deviceMap.entries()) {
+      try {
+        if (entry.iotDevice && typeof entry.iotDevice.end === "function") {
+          entry.iotDevice.end();
+        }
+      } catch (err) {
+        logger.error("Close Aliyun device error", {
+          deviceNumber,
+          error: err.message || err
+        });
+      }
+      deviceMap.delete(deviceNumber);
+    }
+  }
+
+  return {
+    reportDeviceData,
+    closeAll
+  };
+}
+
+module.exports = createAliyunManager;

--
Gitblit v1.8.0