添加设备token是否过期和缓存token接口
authorcheng <chengyf@zhihuishixun.com>
Tue, 16 Sep 2025 13:59:55 +0000 (21:59 +0800)
committercheng <chengyf@zhihuishixun.com>
Tue, 16 Sep 2025 13:59:55 +0000 (21:59 +0800)
code/sdk/zhihui/leiniao/imsl_ln/ln_api.c
code/sdk/zhihui/leiniao/imsl_ln/ln_api.h
code/sdk/zhihui/leiniao/imsl_ln/ln_api_token.c

index 3f288bd..53aa2c4 100644 (file)
@@ -65,15 +65,42 @@ char *uapi_ln_get_device_version(void)
     g_device_version = "1.0.0";
     return g_device_version;
 }
-/* 获取设备token: */
+/* 获取设备token: 优先读本地缓存,判断过期,否则刷新 */
 char *uapi_ln_get_device_token(void)
 {
-    if(g_device_token == NULL)
-    {
-        printf("device token is null!!!\n");
+    /* 若内存已有且非占位,直接返回 */
+    if (g_device_token && strcmp(g_device_token, "<null>") != 0) {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: cached token=%s", g_device_token);
+        return g_device_token;
+    }
+
+    /* 尝试从本地加载 */
+    char *cached = NULL;
+    uint64_t exp_ts = 0;
+    if (ln_token_load_json(&cached, &exp_ts) == ERRCODE_SUCC && cached && cached[0]) {
+        int expired = 1;
+        (void)ln_token_is_expired(&expired);
+        if (!expired) {
+            if (g_device_token) free(g_device_token);
+            g_device_token = cached; /* 接管指针 */
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: loaded from file, not expired(未过期)");
+            return g_device_token;
+        }
+        /* 已过期:释放缓存并尝试刷新 */
+        free(cached);
+        if (uapi_ln_refresh_device_token() == ERRCODE_SUCC) {
+            if (g_device_token && strcmp(g_device_token, "<null>") != 0) {
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"uapi_ln_get_device_token: refreshed expired token");
+                return g_device_token;
+            }
+        }
         return NULL;
     }
-    return g_device_token;
+
+    /* 没有,发出提示信息 */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[TOKEN] 请先激活设备获取 Token");
+
+    return NULL;
 }
 /* 清除设备token */
 void uapi_ln_clear_device_token(void)
index 9a3c3e7..6c2dfe0 100644 (file)
@@ -166,6 +166,13 @@ extern int g_ln_last_package_size;
 HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size);
 uint32_t ln_parse_file_and_process_data(const char *file_path, int interface_type);
 
+/* Token 本地缓存接口 */
+uint32_t ln_token_load_json(char **token_out, uint64_t *expire_timestamp_out);
+/* 判断本地缓存的 token 是否过期(支持秒级绝对时间或TTL秒)*/
+uint32_t ln_token_is_expired(int *expired_out);
+/* 主动刷新设备Token(调用HTTP接口),并落盘保存 */
+uint32_t uapi_ln_refresh_device_token(void);
+
 /* ============================== 各接口函数声明 ============================== */
 uint32_t ln_get_device_token_http(const char *device_id,
                                   const char *device_iccid,
index a20bc7b..1ba1080 100644 (file)
@@ -7,6 +7,11 @@
 #include "mbedtls/md.h"
 /* for MD5 file verify */
 #include "mbedtls/md5.h"
+#include <sys/stat.h>
+#include <time.h>
+
+#define LN_TOKEN_DIR "/user/leiniao/token"
+#define LN_TOKEN_FILE "/user/leiniao/token/device_token.json"
 
 static void ln_to_hex_lower(const uint8_t *in, size_t in_len, char *out)
 {
@@ -42,6 +47,162 @@ static int ln_hmac_sha256_hex(const char *key, const char *msg, char *out_hex, s
     return 0;
 }
 
+/* 创建目录 */
+static void ln_mkdir_p(const char *path)
+{
+    if (!path)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_mkdir_p: path is NULL");
+        return;
+    }
+    (void)mkdir(LN_TOKEN_DIR, 0755);
+}
+
+/* 保存设备Token到本地 */
+static uint32_t ln_token_save_json(const char *token, uint64_t expire_timestamp)
+{
+    if (token == NULL || token[0] == '\0')
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: token is NULL");
+        return ERRCODE_FAIL;
+    }
+    ln_mkdir_p(LN_TOKEN_DIR);
+
+    /* 生成create_time字符串 */
+    char create_time_str[32] = {0};
+    time_t now = time(NULL);
+    struct tm tm_now;
+    (void)localtime_r(&now, &tm_now);
+    (void)strftime(create_time_str, sizeof(create_time_str), "%Y-%m-%d %H:%M:%S", &tm_now);
+
+    cJSON *root = cJSON_CreateObject();
+    cJSON_AddStringToObject(root, "device_token", token);
+    /* 业务约定:expire_timestamp 保存的是“保存时的当前秒级时间戳”,用于与当前时间比较是否超过30天 */
+    cJSON_AddNumberToObject(root, "expire_timestamp", (double)now);
+    cJSON_AddStringToObject(root, "create_time", create_time_str);
+
+    char *text = cJSON_PrintUnformatted(root);
+    cJSON_Delete(root);
+    if (text == NULL)
+        return ERRCODE_FAIL;
+
+    FILE *fp = fopen(LN_TOKEN_FILE, "wb");
+    if (!fp)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: fopen failed");
+        free(text);
+        return ERRCODE_FAIL;
+    }
+    size_t n = fwrite(text, 1, strlen(text), fp);
+    if (n != strlen(text))
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: fwrite failed");
+        fclose(fp);
+        free(text);
+        return ERRCODE_FAIL;
+    }
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: fwrite success");
+    /* 打印保存的json */
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_token_save_json: save json=%s", text);
+    fclose(fp);
+    free(text);
+    return (n > 0) ? ERRCODE_SUCC : ERRCODE_FAIL;
+}
+
+uint32_t ln_token_load_json(char **token_out, uint64_t *expire_timestamp_out)
+{
+    if (token_out)
+        *token_out = NULL;
+    if (expire_timestamp_out)
+        *expire_timestamp_out = 0;
+
+    FILE *fp = fopen(LN_TOKEN_FILE, "rb");
+    if (!fp)
+        return ERRCODE_FAIL;
+    fseek(fp, 0, SEEK_END);
+    long len = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    if (len <= 0 || len > 64 * 1024)
+    {
+        fclose(fp);
+        return ERRCODE_FAIL;
+    }
+    char *buf = (char *)malloc((size_t)len + 1);
+    if (!buf)
+    {
+        fclose(fp);
+        return ERRCODE_FAIL;
+    }
+    size_t n = fread(buf, 1, (size_t)len, fp);
+    fclose(fp);
+    buf[n] = '\0';
+
+    cJSON *root = cJSON_Parse(buf);
+    if (!root)
+    {
+        free(buf);
+        return ERRCODE_FAIL;
+    }
+    cJSON *tok = cJSON_GetObjectItem(root, "device_token");
+    cJSON *exp = cJSON_GetObjectItem(root, "expire_timestamp");
+    if (tok && cJSON_IsString(tok) && token_out)
+    {
+        *token_out = strdup(tok->valuestring);
+    }
+    if (exp && cJSON_IsNumber(exp) && expire_timestamp_out)
+    {
+        *expire_timestamp_out = (uint64_t)exp->valuedouble;
+    }
+    cJSON_Delete(root);
+    free(buf);
+    return ERRCODE_SUCC;
+}
+
+uint32_t uapi_ln_refresh_device_token(void)
+{
+    return ln_get_device_token_http(uapi_ln_get_device_id(),
+                                    uapi_ln_get_device_iccid(),
+                                    uapi_ln_get_device_model(),
+                                    uapi_ln_get_device_version());
+}
+
+/*
+ * 判断本地缓存的 token 是否过期(按约定:expire_timestamp 存的是保存时的当前秒级时间戳)
+ * 规则:now - expire_timestamp > 2592000(30天) 则过期
+ */
+uint32_t ln_token_is_expired(int *expired_out)
+{
+    if (expired_out) *expired_out = 1; /* 默认过期 */
+
+    FILE *fp = fopen(LN_TOKEN_FILE, "rb");
+    if (!fp) return ERRCODE_FAIL;
+    fseek(fp, 0, SEEK_END);
+    long len = ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+    if (len <= 0 || len > 64 * 1024) { fclose(fp); return ERRCODE_FAIL; }
+    char *buf = (char*)malloc((size_t)len + 1);
+    if (!buf) { fclose(fp); return ERRCODE_FAIL; }
+    size_t n = fread(buf, 1, (size_t)len, fp);
+    fclose(fp);
+    buf[n] = '\0';
+
+    cJSON *root = cJSON_Parse(buf);
+    if (!root) { free(buf); return ERRCODE_FAIL; }
+    cJSON *exp = cJSON_GetObjectItem(root, "expire_timestamp");
+    time_t now_s = time(NULL);
+    int expired = 1; /* 默认过期 */
+    if (exp && cJSON_IsNumber(exp)) {
+        uint64_t saved_ts = (uint64_t)exp->valuedouble; /* 保存时的当前时间戳(秒) */
+        const uint64_t TTL_30D = 2592000ULL; /* 30 * 24 * 3600 */
+        expired = ((uint64_t)now_s - saved_ts > TTL_30D) ? 1 : 0;
+    }
+
+    if (expired_out) *expired_out = expired;
+    cJSON_Delete(root);
+    free(buf);
+    return ERRCODE_SUCC;
+}
+
 /* 生成[a-zA-Z0-9]随机串 */
 static void ln_gen_nonce(char *out, size_t len)
 {
@@ -68,7 +229,7 @@ uint32_t ln_get_device_token_http(const char *device_id,
 {
     if (device_id == NULL || device_iccid == NULL)
     {
-        printf("LN token: invalid parameters\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: invalid parameters");
         return ERRCODE_FAIL;
     }
 
@@ -87,7 +248,7 @@ uint32_t ln_get_device_token_http(const char *device_id,
     char signature[65] = {0};
     if (ln_hmac_sha256_hex(LN_SIGN_SECRET, sign_data, signature, sizeof(signature)) != 0)
     {
-        printf("LN token: hmac calc failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: hmac calc failed");
         return ERRCODE_FAIL;
     }
 
@@ -107,18 +268,18 @@ uint32_t ln_get_device_token_http(const char *device_id,
                            (device_version && device_version[0]) ? device_version : "1.0.0");
     if (hdr_len <= 0 || hdr_len >= (int)sizeof(header_buf))
     {
-        printf("LN token: header build failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: header build failed");
         return ERRCODE_FAIL;
     }
 
-    #ifdef SUPPORT_CAT1_SERVICE
+#ifdef SUPPORT_CAT1_SERVICE
     /* 4. 保活CAT1,准备网络 */
     cat1_api_add_cat1_vote();
     cat1_api_wakeup_cat1(1);
     cat1_api_keep_wakeup(1);
     cat1_api_disable_cat1_cmd(1);
     LOS_Msleep(500);
-    #endif
+#endif
 
     /* 5. 发起HTTPS GET */
     HttpClient client = (HttpClient){0};
@@ -128,14 +289,14 @@ uint32_t ln_get_device_token_http(const char *device_id,
     HTTPC_RESULT prep = HttpClientPrepare(&clientData, 2048, 4096);
     if (prep != HTTP_SUCCESS)
     {
-        printf("LN token: HttpClientPrepare failed %d\n", prep);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: HttpClientPrepare failed %d", prep);
         return ERRCODE_FAIL;
     }
 
     HTTPC_RESULT res = HttpClientGetRequest(&client, LN_FULL_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
-        printf("LN token: request failed %d\n", res);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: request failed %d", res);
         HttpClientUnprepare(&clientData);
         HttpClientClse(&client);
         return ERRCODE_FAIL;
@@ -144,7 +305,7 @@ uint32_t ln_get_device_token_http(const char *device_id,
     int code = HttpClientGetResponseCode(&client);
     if (code != 200)
     {
-        printf("LN token: HTTP %d\n", code);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: HTTP %d", code);
         HttpClientUnprepare(&clientData);
         HttpClientClse(&client);
         return ERRCODE_FAIL;
@@ -153,7 +314,7 @@ uint32_t ln_get_device_token_http(const char *device_id,
     /* 6. 解析响应JSON */
     if (clientData.responseBuf == NULL || clientData.responseBufLen <= 0)
     {
-        printf("LN token: empty body\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: empty body");
         HttpClientUnprepare(&clientData);
         HttpClientClse(&client);
         return ERRCODE_FAIL;
@@ -176,7 +337,7 @@ uint32_t ln_get_device_token_http(const char *device_id,
     cJSON *root = cJSON_Parse(json_text);
     if (!root)
     {
-        printf("LN token: JSON parse failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: JSON parse failed");
         free(json_text);
         HttpClientUnprepare(&clientData);
         HttpClientClse(&client);
@@ -187,7 +348,9 @@ uint32_t ln_get_device_token_http(const char *device_id,
     if (errorCode != NULL && !cJSON_IsNull(errorCode))
     {
         cJSON *msg = cJSON_GetObjectItem(root, "message");
-        printf("LN token: failed, err=%s\n", msg && cJSON_IsString(msg) ? msg->valuestring : "unknown");
+        /* 失败响应,打印errorCode和message 并返回失败 */
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: failed, err=%s", errorCode && cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: failed, message=%s", msg && cJSON_IsString(msg) ? msg->valuestring : "unknown");
         cJSON_Delete(root);
         free(json_text);
         HttpClientUnprepare(&clientData);
@@ -218,15 +381,17 @@ uint32_t ln_get_device_token_http(const char *device_id,
         {
             g_device_token = strdup("<null>");
         }
-        printf("\njson=%s\n", json_text);
+        /* 落盘保存 */
+        uint64_t exp_ts = 0;
         if (expire && cJSON_IsNumber(expire))
         {
-            printf("Token expireTime: %lld\n", (long long)expire->valuedouble);
+            exp_ts = (uint64_t)expire->valuedouble;
         }
+        (void)ln_token_save_json(g_device_token, exp_ts);
     }
     else
     {
-        printf("LN token: no data field\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"ln_get_device_token_http: no data field");
     }
 
     cJSON_Delete(root);