添加表情下载协议解析
authorluozheng <1732036298@qq.com>
Wed, 24 Sep 2025 14:56:27 +0000 (22:56 +0800)
committerluozheng <1732036298@qq.com>
Wed, 24 Sep 2025 14:56:27 +0000 (22:56 +0800)
code/sdk/zhihui/leiniao/imsl_ln/CMakeLists.txt
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_emoji.c

index 5ba40bc..98f893c 100644 (file)
@@ -19,6 +19,7 @@ set(SOURCES
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_ota.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_websocket.c
     ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_schedule.c
+    ${CMAKE_CURRENT_SOURCE_DIR}/ln_api_emoji.c
 )
 
 # 公共头文件路径(供其他模块引用)
index 188f302..23d9a95 100644 (file)
@@ -73,7 +73,7 @@ char *uapi_ln_get_device_version(void)
     if (!first_underline) {
         // 无下划线,直接使用原始版本号(异常情况处理)
         snprintf(g_device_version, sizeof(g_device_version), "%s", version_buf);
-        printf("g_device_version (no underline): %s\n", g_device_version);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version (no underline): %s", g_device_version);
         return g_device_version;
     }
     
@@ -82,7 +82,7 @@ char *uapi_ln_get_device_version(void)
     if (!second_underline) {
         // 只有一个下划线(如 "V1.0.0_20250920"),直接使用
         snprintf(g_device_version, sizeof(g_device_version), "%s", version_buf);
-        printf("g_device_version (only one underline): %s\n", g_device_version);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version (only one underline): %s", g_device_version);
         return g_device_version;
     }
     
@@ -97,7 +97,7 @@ char *uapi_ln_get_device_version(void)
     strncpy(g_device_version, version_buf, target_len);
     g_device_version[target_len] = '\0';  // 手动添加字符串结束符
     
-    printf("g_device_version: %s\n", g_device_version);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"g_device_version: %s", g_device_version);
     return g_device_version;
 }
 /* 获取设备token: 优先读本地缓存,判断过期,否则刷新 */
@@ -159,7 +159,7 @@ void uapi_ln_clear_device_token(void)
 {
     if(g_device_token != NULL)
     {
-        printf("clear device token\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"clear device token");
         free(g_device_token);
         g_device_token = NULL;
     }
@@ -198,6 +198,103 @@ void uapi_ln_set_device_bound(int val)
 {
     g_ln_is_device_bound = val;
 }
+int uapi_ln_download_file(const char *url, const char *save_path)
+{
+    if (!url || !save_path)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] url or save_path is NULL");
+        return ERRCODE_FAIL;
+    }
+
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 开始下载: %s -> %s", url, save_path);
+
+    HttpClient client = {0};
+    HttpClientData client_data = {0};
+    int ret = -1;
+    size_t total_written = 0;
+    // 打开文件准备写入
+    FILE *fd = fopen(save_path, "wb+");
+    if (!fd)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] fopen failed for %s", save_path);
+        return ERRCODE_FAIL;
+    }
+
+    HTTPC_RESULT prep = ln_http_prepare_auto(&client_data, 2048, LN_CLIENT_PREPARE_SIZE * 2);
+    if (prep != HTTP_SUCCESS)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] prepare failed: %d", prep);
+        fclose(fd);
+        return ERRCODE_FAIL;
+    }
+
+    // 建立连接
+    ret = HttpClientConn(&client, url);
+    if (ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] connection failed, ret=0x%x", ret);
+        fclose(fd);
+        HttpClientUnprepare(&client_data);
+        return ERRCODE_FAIL;
+    }
+
+    // 发送GET请求
+    ret = HttpClientSend(&client, url, HTTP_GET, &client_data);
+    if (ret != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] send request failed, ret=0x%x", ret);
+        fclose(fd);
+        HttpClientClse(&client);
+        HttpClientUnprepare(&client_data);
+        return ERRCODE_FAIL;
+    }
+
+    // 接收响应数据
+    do
+    {
+        HTTPC_RESULT recv_res = HttpClientRecvResponse(&client, &client_data);
+        if (recv_res == HTTP_SUCCESS || recv_res == HTTP_EAGAIN)
+        {
+            if (client_data.contentBlockLen > 0)
+            {
+                size_t written = fwrite(client_data.responseBuf, 1, client_data.contentBlockLen, fd);
+                if (written != client_data.contentBlockLen)
+                {
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] write failed, written=%zu, expected=%d",
+                                  written, client_data.contentBlockLen);
+                    fclose(fd);
+                    HttpClientClse(&client);
+                    HttpClientUnprepare(&client_data);
+                    return ERRCODE_FAIL;
+                }
+                total_written += written;
+            }
+        }
+    } while (client_data.isMore);
+
+    if (client_data.responseContentLen == 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] receive length = 0");
+    }
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] responsecode = %d", client.responseCode);
+    if (total_written == client_data.responseContentLen && client.responseCode == 200 && total_written > 0)
+    {
+        ret = ERRCODE_SUCC;
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file is downloaded successfully and the data is complete!");
+    }
+    else
+    {
+        ret = ERRCODE_FAIL;
+        unlink(save_path); // 删除不完整文件
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] The file data is incomplete!");
+    }
+
+    HttpClientUnprepare(&client_data);
+    HttpClientClse(&client);
+    fclose(fd);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] download process finished,ret=%d", ret);
+    return ret;
+}
 
 /* 自适应申请HttpClient缓冲(保证header>=2048),失败则逐步扩容重试 */
 HTTPC_RESULT ln_http_prepare_auto(HttpClientData *clientData, int header_size, int resp_size)
@@ -281,7 +378,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
 {
     if (!json_buf || json_len <= 0)
     {
-        printf("[OTA] handle ota task: invalid json\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: invalid json");
         return;
     }
     char *text = (char *)malloc((size_t)json_len + 1);
@@ -292,7 +389,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     cJSON *root = cJSON_Parse(text);
     if (!root)
     {
-        printf("[OTA] handle ota task: json parse failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] handle ota task: json parse failed");
         free(text);
         return;
     }
@@ -301,7 +398,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     if (errorCode && cJSON_IsString(errorCode))
     {
         /* 无升级或服务端错误 */
-        printf("[OTA] task errorCode=%s\n", errorCode->valuestring);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task errorCode=%s", errorCode->valuestring);
         cJSON_Delete(root);
         free(text);
         return;
@@ -310,7 +407,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     cJSON *data = cJSON_GetObjectItem(root, "data");
     if (!data || !cJSON_IsObject(data))
     {
-        printf("[OTA] task data missing\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task data missing");
         cJSON_Delete(root);
         free(text);
         return;
@@ -323,7 +420,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     cJSON *packageSize = cJSON_GetObjectItem(data, "packageSize");
     if (!(targetVersion && cJSON_IsString(targetVersion) && updateType && (cJSON_IsNumber(updateType) || cJSON_IsString(updateType))))
     {
-        printf("[OTA] task fields invalid\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task fields invalid");
         cJSON_Delete(root);
         free(text);
         return;
@@ -335,7 +432,7 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     else if (cJSON_IsString(updateType)) ut = atoi(updateType->valuestring);
     g_ln_last_update_type = ut;
 
-    printf("[OTA] task parsed: targetVersion=%s updateType=%d url=%s md5=%s\n",
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] task parsed: targetVersion=%s updateType=%d url=%s md5=%s",
            targetVersion->valuestring, ut,
            (packageUrl && cJSON_IsString(packageUrl)) ? packageUrl->valuestring : "<null>",
            (packageMd5 && cJSON_IsString(packageMd5)) ? packageMd5->valuestring : "<null>");
@@ -367,17 +464,17 @@ static void ln_handle_ota_task_response(const char *json_buf, int json_len)
     if (ut == 1)
     {
         /* 强制升级:直接触发下载 */
-        printf("[OTA] force upgrade: trigger download(强制升级:直接触发下载)\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] force upgrade: trigger download(强制升级:直接触发下载)");
         push_ota_status(OTA_UI_STATUS_FORCE_START);
     }
     else if (ut == 0)
     {
-        printf("[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] optional upgrade: waiting for user confirmation(可选升级:等待用户确认)");
         push_ota_status(OTA_UI_STATUS_OPTIONAL_START);
     }
     else
     {
-        printf("[OTA] unknown updateType, skip\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] unknown updateType, skip");
     }
 
     cJSON_Delete(root);
@@ -425,13 +522,13 @@ static int ln_ota_is_md5_ok(const char *file_path, const char *expected_md5)
 {
     if (file_path == NULL || expected_md5 == NULL || expected_md5[0] == '\0')
     {
-        printf("[OTA] md5 verify: invalid args\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: invalid args");
         return -1;
     }
     char calc[33] = {0};
     if (ln_calc_file_md5_lower_hex(file_path, calc) != 0)
     {
-        printf("[OTA] md5 verify: calc failed for %s\n", file_path);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify: calc failed for %s", file_path);
         return -1;
     }
     /* 规范化期望md5为小写并截断为32字符 */
@@ -447,7 +544,7 @@ static int ln_ota_is_md5_ok(const char *file_path, const char *expected_md5)
     expect_lower[32] = '\0';
 
     int ok = (strcmp(calc, expect_lower) == 0) ? 1 : 0;
-    printf("[OTA] md5 verify result=%s, calc=%s, expect=%s\n", ok ? "OK" : "FAIL", calc, expect_lower);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA] md5 verify result=%s, calc=%s, expect=%s", ok ? "OK" : "FAIL", calc, expect_lower);
     return ok;
 }
 
@@ -458,37 +555,37 @@ static void handle_ota_download_msg(const OtaDownloadMsg *msg)
     if (!msg) return;
     switch (msg->status) {
         case OTA_DOWNLOAD_STATUS_PROGRESS:
-            printf("[OTA_DOWNLOAD] 下载进度: %d%%\n", msg->progress);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载进度: %d%%", msg->progress);
             push_ota_progress(msg->progress);
             break;
         case OTA_DOWNLOAD_STATUS_COMPLETED:
-            printf("[OTA_DOWNLOAD] 下载完成,文件路径: %s\n", msg->file_path);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载完成,文件路径: %s", msg->file_path);
             if (msg->file_path[0] && msg->md5[0]) {
-                printf("[OTA_DOWNLOAD] 开始MD5校验: %s\n", msg->md5);
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 开始MD5校验: %s", msg->md5);
                 int md5_ok = ln_ota_is_md5_ok(msg->file_path, msg->md5);
                 if (md5_ok == 1) {
-                    printf("[OTA_DOWNLOAD] MD5校验成功,准备重启升级\n");
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验成功,准备重启升级");
                     push_ota_status(OTA_UI_STATUS_SUCCESS);
                     /* 外部可在此触发升级动作 */
                     uaip_cmd_update_request();
                 } else if (md5_ok == 0) {
-                    printf("[OTA_DOWNLOAD] MD5校验失败,建议重新下载\n");
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验失败,建议重新下载");
                     push_ota_status(OTA_UI_STATUS_FAILED);
                 } else {
-                    printf("[OTA_DOWNLOAD] MD5校验出错\n");
+                    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] MD5校验出错");
                     push_ota_status(OTA_UI_STATUS_FAILED);
                 }
             }
             break;
         case OTA_DOWNLOAD_STATUS_FAILED:
-            printf("[OTA_DOWNLOAD] 下载失败: %s\n", msg->error_msg);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 下载失败: %s", msg->error_msg);
             reset_cnt++;
             if(reset_cnt < 3){
              do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_OTA_DOWNLOAD, NULL, 0);
             }
             break;
         default:
-            printf("[OTA_DOWNLOAD] 未知状态: %d\n", msg->status);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP,"[OTA_DOWNLOAD] 未知状态: %d", msg->status);
             break;
     }
 }
@@ -582,7 +679,7 @@ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
             printf("[WS] DownloadEmoji:下载表情\n");
             if(json_str)
             {
-                // ln_parse_download_emoji(json_str, (int)strlen(json_str));
+                ln_parse_download_emoji(json_str, (int)strlen(json_str));
             }
             break;
         }
@@ -590,7 +687,7 @@ void ln_ws_process_message(const ws_data_msg_t *data_msg, ws_command_t type)
             printf("[WS] DeleteEmoji:删除表情\n");
             if(json_str)
             {
-                // ln_parse_delete_emoji(json_str, (int)strlen(json_str));
+                ln_parse_delete_emoji(json_str, (int)strlen(json_str));
             }
             break;
         }
@@ -1215,6 +1312,19 @@ static void ln_http_server_send_task(void const *arg)
         {
             printf("LN_HTTP_SERVICE_CMD_AUDIO_RESUME:恢复播放\n");
         }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT)
+        {
+            printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT:上报角色表情组下载状态\n");
+            if(mail.data)
+            {
+                ln_emoji_report_status(mail.data);
+            }
+        }
+        else if (mail.cmd == LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL)
+        {
+            printf("LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL:上报所有角色表情组状态\n");
+            // ln_emoji_report_all(mail.data);
+        }
         if (mail.data != NULL)
         {
             printf("free(mail.data)\n");
index c6bc20f..2b541e8 100644 (file)
@@ -59,6 +59,7 @@ typedef struct
 #define story_choose_file_path LN_DATA_PATH "/story/choose.txt"
 #define story_random_file_path LN_DATA_PATH "/story/random.txt"
 #define LN_CHARACTER_CONFIG_FILE LN_DATA_PATH "/character/character_config.json"
+#define EMOJI_CACHE_BASE_PATH LN_DATA_PATH "/emoji/" // 表情缓存根目录
 
 #define LN_CLIENT_PREPARE_SIZE 2 * 1024
 #define TEST_MP3_DOWNLOAD 0 // 测试下载mp3
@@ -99,8 +100,8 @@ typedef struct
 #define LN_HTTP_SERVICE_CMD_AUDIO_RESUME 0x1006    /* 恢复播放 */
 
 /* Emoji 上报结果接收命令 */
-#define LN_HTTP_RECV_EMOJI_REPORT 0x1007
-#define LN_HTTP_RECV_EMOJI_REPORT_ALL 0x1008
+#define LN_HTTP_SERVICE_CMD_EMOJI_REPORT 0x1007      /*上报角色表情组下载状态*/
+#define LN_HTTP_SERVICE_CMD_EMOJI_REPORT_ALL 0x1008  /*上报所有角色表情组状态*/
 
 /* 故事-随机故事 请求体(可选主题)*/
 typedef struct
@@ -207,8 +208,8 @@ int uapi_ln_is_device_bound(void);
 void uapi_ln_set_device_bound(int val);
 /* 获取设备绑定用户ID */
 const char *uapi_ln_get_user_id(void);
-/* 获取设备角色配置 */
-LnCharacterConfig uapi_ln_get_character_config(void);
+/*文件下载函数:从URL下载到指定路径*/
+int uapi_ln_download_file(const char *url, const char *save_path);
 
 /* ============================== 共享全局状态(跨文件) ============================== */
 extern char *g_device_token;
@@ -302,19 +303,12 @@ uint32_t ln_ota_get_task_http(const char *device_id,
 uint32_t ln_ota_download_flow_http(const char *device_id,
                                     const char *device_token,
                                     const char *source_version);
+/* Emoji 表情上报接口 */
+/* 上报角色表情组下载状态 */
+void ln_emoji_report_status(char* status);
+/* 上报所有角色表情组状态 */
+void ln_emoji_report_all(void);
 
-/* Emoji 表情接口 */
-uint32_t ln_emoji_report_status_http(const char *device_id,
-                                        const char *device_token,
-                                        const char *character_id,
-                                        const char *eyes_color,
-                                        const char *eyes_shape,
-                                        const char *status);
-
-uint32_t ln_emoji_report_all_http(const char *device_id,
-                                    const char *device_token,
-                                    const LnEmojiGroup *groups,
-                                    int group_count);
 
 /* ============================== WebSocket 客户端接口 ============================== */
 
index 3c17f02..36fa2b7 100644 (file)
@@ -1,17 +1,16 @@
 #include "ln_api.h"
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include "cJSON.h"
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <errno.h>
+#include <dirent.h>
 
 /* 宏定义 */
-#define EMOJI_CACHE_BASE_PATH "/emoji_cache/" // 表情缓存根目录
-#define EMOJI_DOWNLOAD_TASK_STACK 4096        // 下载任务栈大小
-#define EMOJI_DOWNLOAD_TASK_PRIO 25           // 下载任务优先级
-#define MAX_PATH_LEN 512                      // 最大路径长度
+#define MAX_PATH_LEN 512 // 最大路径长度
 
 /* 表情信息结构体定义 */
 typedef struct
@@ -59,49 +58,14 @@ static void ln_free_emoji_list()
     g_emoji_package.emojiCount = 0;
 }
 
-/* 工具函数:创建多级目录 */
-static int ln_mkdir_p(const char *path)
-{
-    if (!path || *path == '\0')
-        return -1;
-
-    char dir[MAX_PATH_LEN] = {0};
-    strncpy(dir, path, sizeof(dir) - 1);
-    int len = strlen(dir);
-    if (dir[len - 1] != '/')
-    {
-        strncat(dir, "/", sizeof(dir) - 1 - len);
-        len++;
-    }
-
-    char *p = dir + 1;
-    while (*p)
-    {
-        if (*p == '/')
-        {
-            *p = '\0';
-            if (mkdir(dir, 0777) != 0 && errno != EEXIST)
-            {
-                return -1;
-            }
-            *p = '/';
-        }
-        p++;
-    }
-
-    // 创建最后一级目录
-    if (mkdir(dir, 0777) != 0 && errno != EEXIST)
-    {
-        return -1;
-    }
-    return 0;
-}
-
 /* 工具函数:生成表情缓存路径 */
 static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji *emoji, char *path, size_t path_len)
 {
     if (!pkg || !emoji || !path || path_len == 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 生成表情缓存路径失败,pkg=%p,emoji=%p,path=%p,path_len=%zu", pkg, emoji, path, path_len);
         return;
+    }
 
     // 路径格式:BASE/characterId/eyesColor_eyesShape/emotion.ext
     char ext[32] = {0};
@@ -110,15 +74,27 @@ static void ln_gen_emoji_cache_path(const EmojiPackage *pkg, const EmotionEmoji
     {
         strncpy(ext, dot, sizeof(ext) - 1);
     }
+    else
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 生成表情缓存路径失败,emojiUrl=%s", emoji->emojiUrl);
+        return;
+    }
 
-    snprintf(path, path_len,
-             "%s%s/%s_%s/%s%s",
-             EMOJI_CACHE_BASE_PATH,
-             pkg->characterId,
-             pkg->eyesColor,
-             pkg->eyesShape,
-             emoji->emotion,
-             ext);
+    // emojiUrl=https://ambybox-emoji.test.leiniao.com/v1/emoji/getEmoji?characterId=1&eyesColor=1&eyesShape=1&emotion=1
+    // path=/user/userdata/leiniao/emoji/1/1_1/1.png
+
+    // snprintf(path, path_len,
+    //          "%s%s/%s_%s/%s%s",
+    //          EMOJI_CACHE_BASE_PATH,
+    //          pkg->characterId,
+    //          pkg->eyesColor,
+    //          pkg->eyesShape,
+    //          emoji->emotion,
+    //          ext);
+    // 暂时将文件存在EMOJI_CACHE_BASE_PATH下
+
+    snprintf(path, path_len, "%s%s%s", EMOJI_CACHE_BASE_PATH, emoji->emotion, ext);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 生成表情缓存路径成功,path=%s", path);
 }
 
 /* 工具函数:检查表情是否已缓存 */
@@ -130,75 +106,13 @@ static int ln_emoji_is_cached(const EmojiPackage *pkg, const EmotionEmoji *emoji
     return (stat(path, &st) == 0 && S_ISREG(st.st_mode)) ? 1 : 0;
 }
 
-/* 文件下载函数:从URL下载到指定路径 */
-static int ln_download_file(const char *url, const char *save_path)
-{
-    if (!url || !save_path)
-        return -1;
-
-    // 创建保存目录
-    char dir[MAX_PATH_LEN] = {0};
-    strncpy(dir, save_path, sizeof(dir) - 1);
-    char *last_slash = strrchr(dir, '/');
-    if (last_slash)
-    {
-        *last_slash = '\0';
-        if (ln_mkdir_p(dir) != 0)
-        {
-            printf("[DOWNLOAD] create dir %s failed\n", dir);
-            return -1;
-        }
-    }
-
-    // 初始化HTTP客户端
-    HttpClient client = {0};
-    HttpClientData clientData = {0};
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 32 * 1024); // 增大接收缓冲区
-    if (prep != HTTP_SUCCESS)
-    {
-        printf("[DOWNLOAD] prepare failed\n");
-        return -1;
-    }
-
-    // 发送GET请求
-    HTTPC_RESULT res = HttpClientGetRequest(&client, url, &clientData);
-    if (res != HTTP_SUCCESS || HttpClientGetResponseCode(&client) != 200)
-    {
-        printf("[DOWNLOAD] GET %s failed\n", url);
-        HttpClientUnprepare(&clientData);
-        return -1;
-    }
-
-    // 写入文件
-    int fd = open(save_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
-    if (fd < 0)
-    {
-        printf("[DOWNLOAD] open %s failed\n", save_path);
-        HttpClientUnprepare(&clientData);
-        return -1;
-    }
-
-    int write_len = write(fd, clientData.responseBuf, clientData.responseContentLen);
-    close(fd);
-    HttpClientUnprepare(&clientData);
-
-    if (write_len != clientData.responseContentLen)
-    {
-        printf("[DOWNLOAD] write file failed\n");
-        unlink(save_path); // 删除不完整文件
-        return -1;
-    }
-
-    printf("[DOWNLOAD] %s saved to %s\n", url, save_path);
-    return 0;
-}
-
-/* 下载任务:批量下载表情并上报结果 */
-static void ln_emoji_download_task(void *arg)
+/* 批量下载表情并上报结果 */
+static void ln_emoji_download(void *arg)
 {
     EmojiPackage *pkg = (EmojiPackage *)arg;
     if (!pkg || pkg->emojiCount <= 0 || !pkg->emojis)
     {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] 没有表情需要下载");
         g_emoji_downloading = 0;
         return;
     }
@@ -211,34 +125,28 @@ static void ln_emoji_download_task(void *arg)
         // 跳过已缓存的文件
         if (ln_emoji_is_cached(pkg, &pkg->emojis[i]))
         {
-            printf("[DOWNLOAD] emoji %s already cached\n", pkg->emojis[i].emotion);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s already cached", pkg->emojis[i].emotion);
             continue;
         }
 
         // 下载并缓存
         char save_path[MAX_PATH_LEN] = {0};
         ln_gen_emoji_cache_path(pkg, &pkg->emojis[i], save_path, sizeof(save_path));
-        if (ln_download_file(pkg->emojis[i].emojiUrl, save_path) != 0)
+        if (uapi_ln_download_file(pkg->emojis[i].emojiUrl, save_path) != ERRCODE_SUCC)
         {
             all_success = 0;
-            printf("[DOWNLOAD] emoji %s download failed\n", pkg->emojis[i].emotion);
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] emoji %s download failed", pkg->emojis[i].emotion);
         }
     }
 
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "上报表情下载状态");
+
     // 下载完成后上报状态
     const char *status = all_success ? "success" : "failed";
-    uint32_t report_res = ln_emoji_report_status_http(
-        get_device_id(),    // 假设存在设备ID获取接口
-        get_device_token(), // 假设存在设备Token获取接口
-        pkg->characterId,
-        pkg->eyesColor,
-        pkg->eyesShape,
-        status);
-
-    printf("[DOWNLOAD] report status %s, result: %d\n", status, report_res);
+    do_ln_http_server_send_data(LN_HTTP_SERVICE_CMD_EMOJI_REPORT, (char *)status, strlen(status));
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] report status %s, all_success: %d", status, all_success);
 
     g_emoji_downloading = 0;
-    LOS_TaskDelete(NULL); // 删除当前任务(根据实际系统接口调整)
 }
 
 /* 启动下载任务 */
@@ -246,32 +154,20 @@ static void ln_start_emoji_download()
 {
     if (g_emoji_downloading)
     {
-        printf("[DOWNLOAD] already downloading, skip\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] already downloading, skip");
         return;
     }
 
     if (g_emoji_package.emojiCount <= 0 || !g_emoji_package.emojis)
     {
-        printf("[DOWNLOAD] no emojis to download\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD] no emojis to download");
         return;
     }
 
-    g_emoji_downloading = 1;
-
-    // 创建下载任务(基于LiteOS接口示例)
-    UINT32 task_id;
-    TSK_INIT_PARAM_S task_param = {
-        .pfnTaskEntry = (TSK_ENTRY_FUNC)ln_emoji_download_task,
-        .ulStackSize = EMOJI_DOWNLOAD_TASK_STACK,
-        .pcName = "emoji_download",
-        .usTaskPrio = EMOJI_DOWNLOAD_TASK_PRIO,
-        .pvArg = &g_emoji_package};
+    // 下载表情
+    ln_emoji_download(&g_emoji_package);
 
-    if (LOS_TaskCreate(&task_id, &task_param) != LOS_OK)
-    {
-        printf("[DOWNLOAD] create task failed\n");
-        g_emoji_downloading = 0;
-    }
+    g_emoji_downloading = 1;
 }
 
 /* 解析下载表情命令JSON */
@@ -279,7 +175,7 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
 {
     if (!json_buf || json_len <= 0)
     {
-        printf("[DOWNLOAD_EMOJI] invalid json input\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] invalid json input");
         return;
     }
 
@@ -297,7 +193,7 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     cJSON *root = cJSON_Parse(text);
     if (!root)
     {
-        printf("[DOWNLOAD_EMOJI] json parse failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] json parse failed");
         free(text);
         return;
     }
@@ -306,8 +202,8 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode");
     if (errorCode && !cJSON_IsNull(errorCode))
     {
-        printf("[DOWNLOAD_EMOJI] server error: %s\n",
-               cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] server error: %s",
+                      cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
         cJSON_Delete(root);
         free(text);
         return;
@@ -317,7 +213,7 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     cJSON *data = cJSON_GetObjectItem(root, "data");
     if (!data || !cJSON_IsObject(data))
     {
-        printf("[DOWNLOAD_EMOJI] no data object\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] no data object");
         cJSON_Delete(root);
         free(text);
         return;
@@ -351,7 +247,7 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
             g_emoji_package.emojis = (EmotionEmoji *)malloc(sizeof(EmotionEmoji) * count);
             if (!g_emoji_package.emojis)
             {
-                printf("[DOWNLOAD_EMOJI] malloc failed for emojis\n");
+                WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] malloc failed for emojis");
                 g_emoji_package.emojiCount = 0;
             }
             else
@@ -379,18 +275,18 @@ void ln_parse_download_emoji(const char *json_buf, int json_len)
     }
 
     // 打印解析结果
-    printf("[DOWNLOAD_EMOJI] parsed successfully:\n");
-    printf("  characterId: %s\n", g_emoji_package.characterId);
-    printf("  eyesColor: %s\n", g_emoji_package.eyesColor);
-    printf("  eyesShape: %s\n", g_emoji_package.eyesShape);
-    printf("  emojiCount: %d\n", g_emoji_package.emojiCount);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DOWNLOAD_EMOJI] parsed successfully:");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  characterId: %s", g_emoji_package.characterId);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesColor: %s", g_emoji_package.eyesColor);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesShape: %s", g_emoji_package.eyesShape);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  emojiCount: %d", g_emoji_package.emojiCount);
 
     for (int i = 0; i < g_emoji_package.emojiCount; i++)
     {
-        printf("  emoji %d: emotion=%s, url=%s\n",
-               i + 1,
-               g_emoji_package.emojis[i].emotion,
-               g_emoji_package.emojis[i].emojiUrl);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  emoji %d: emotion=%s, url=%s",
+                      i + 1,
+                      g_emoji_package.emojis[i].emotion,
+                      g_emoji_package.emojis[i].emojiUrl);
     }
 
     // 解析完成后启动下载任务
@@ -448,7 +344,7 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
 {
     if (!json_buf || json_len <= 0)
     {
-        printf("[DELETE_EMOJI] invalid json input\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] invalid json input");
         return;
     }
 
@@ -463,7 +359,7 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     cJSON *root = cJSON_Parse(text);
     if (!root)
     {
-        printf("[DELETE_EMOJI] json parse failed\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] json parse failed");
         free(text);
         return;
     }
@@ -472,8 +368,8 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     cJSON *errorCode = cJSON_GetObjectItem(root, "errorCode");
     if (errorCode && !cJSON_IsNull(errorCode))
     {
-        printf("[DELETE_EMOJI] server error: %s\n",
-               cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] server error: %s",
+                      cJSON_IsString(errorCode) ? errorCode->valuestring : "unknown");
         cJSON_Delete(root);
         free(text);
         return;
@@ -483,7 +379,7 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
     cJSON *data = cJSON_GetObjectItem(root, "data");
     if (!data || !cJSON_IsObject(data))
     {
-        printf("[DELETE_EMOJI] no data object\n");
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] no data object");
         cJSON_Delete(root);
         free(text);
         return;
@@ -508,10 +404,10 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
                    (eyesShape && cJSON_IsString(eyesShape)) ? eyesShape->valuestring : "");
 
     // 打印解析结果
-    printf("[DELETE_EMOJI] parsed successfully:\n");
-    printf("  characterId: %s\n", del_characterId);
-    printf("  eyesColor: %s\n", del_eyesColor);
-    printf("  eyesShape: %s\n", del_eyesShape);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] parsed successfully:");
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  characterId: %s", del_characterId);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesColor: %s", del_eyesColor);
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "  eyesShape: %s", del_eyesShape);
 
     // 删除本地表情缓存
     char del_dir[MAX_PATH_LEN] = {0};
@@ -524,11 +420,11 @@ void ln_parse_delete_emoji(const char *json_buf, int json_len)
 
     if (ln_rmdir_recursive(del_dir) == 0)
     {
-        printf("[DELETE_EMOJI] cached files in %s deleted\n", del_dir);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] cached files in %s deleted", del_dir);
     }
     else
     {
-        printf("[DELETE_EMOJI] failed to delete cached files in %s\n", del_dir);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[DELETE_EMOJI] failed to delete cached files in %s", del_dir);
     }
 
     // 清理资源
@@ -566,8 +462,13 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
                                      const char *status)
 {
     if (!device_id || !device_token || !character_id || !eyes_color || !eyes_shape || !status)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] invalid input");
         return ERRCODE_FAIL;
+    }
 
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] starting");
+    // CAT1 保活
 #ifdef SUPPORT_CAT1_SERVICE
     cat1_api_add_cat1_vote();
     cat1_api_wakeup_cat1(1);
@@ -580,12 +481,18 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     HttpClientData clientData = (HttpClientData){0};
 
     if (ln_fill_auth_header(&client, device_id, device_token) != 0)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] fill auth header failed");
         return ERRCODE_FAIL;
+    }
 
     /* 组装 JSON */
     cJSON *root = cJSON_CreateObject();
     if (!root)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] create root object failed");
         return ERRCODE_FAIL;
+    }
     cJSON_AddStringToObject(root, "characterId", character_id);
     cJSON_AddStringToObject(root, "eyesColor", eyes_color);
     cJSON_AddStringToObject(root, "eyesShape", eyes_shape);
@@ -593,36 +500,65 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     char *body = cJSON_PrintUnformatted(root);
     cJSON_Delete(root);
     if (!body)
+    {
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] print unformatted failed");
         return ERRCODE_FAIL;
+    }
 
-    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, 8 * 1024);
+    HTTPC_RESULT prep = ln_http_prepare_auto(&clientData, 2048, LN_CLIENT_PREPARE_SIZE / 2);
     if (prep != HTTP_SUCCESS)
     {
         free(body);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] prepare auto failed");
         return ERRCODE_FAIL;
     }
     clientData.postBuf = body;
     clientData.postBufLen = (int)strlen(body);
     clientData.postContentType = (char *)"application/json";
 
+    // 打印请求请求响应
+    WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request body: %s", body);
+    // 使用说明:
+    // - 该接口用于设备向服务器同步表情下载/删除状态
+    // - 设备执行表情下载或删除操作成功后,必须调用此接口
+    // - 服务器会根据上报的信息更新小程序端的表情状态显示
+    // - characterId、eyesColor、eyesShape三个字段组合标识一个唯一的表情组
+
     HTTPC_RESULT res = HttpClientPostRequest(&client, LN_EMOJI_REPORT_URL, &clientData);
     if (res != HTTP_SUCCESS)
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] request failed");
         return ERRCODE_FAIL;
     }
 
     int code = HttpClientGetResponseCode(&client);
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
+        // {
+        //     "errorCode": null,
+        //     "message": null,
+        //     "data": "ok"
+        // }
+        // 打印成功响应
         int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
-        do_ln_http_server_recv_data(LN_HTTP_RECV_EMOJI_REPORT, clientData.responseBuf, (uint32_t)real_len);
+        char *response_buf = (char *)malloc(real_len + 1);
+        if (!response_buf)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc failed for response_buf");
+            return ERRCODE_FAIL;
+        }
+        memcpy(response_buf, clientData.responseBuf, real_len);
+        response_buf[real_len] = '\0';
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response body: %s", response_buf);
+        free(response_buf);
     }
     if (code != 200)
     {
         free(body);
         HttpClientUnprepare(&clientData);
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response code not 200");
         return ERRCODE_FAIL;
     }
 
@@ -630,7 +566,6 @@ uint32_t ln_emoji_report_status_http(const char *device_id,
     HttpClientUnprepare(&clientData);
     return ERRCODE_SUCC;
 }
-
 /* 7.2 上报设备所有表情组状态 */
 uint32_t ln_emoji_report_all_http(const char *device_id,
                                   const char *device_token,
@@ -707,7 +642,16 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     if (clientData.responseBuf && clientData.responseBufLen > 0)
     {
         int real_len = clientData.responseContentLen > 0 ? clientData.responseContentLen : clientData.responseBufLen;
-        do_ln_http_server_recv_data(LN_HTTP_RECV_EMOJI_REPORT_ALL, clientData.responseBuf, (uint32_t)real_len);
+        char *response_buf = (char *)malloc(real_len + 1);
+        if (!response_buf)
+        {
+            WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] malloc failed for response_buf");
+            return ERRCODE_FAIL;
+        }
+        memcpy(response_buf, clientData.responseBuf, real_len);
+        response_buf[real_len] = '\0';
+        WEARABLE_LOGE(WEARABLE_LOG_MODULE_APP, "[EMOJI_REPORT_STATUS] response body: %s", response_buf);
+        free(response_buf);
     }
     if (code != 200)
     {
@@ -719,4 +663,24 @@ uint32_t ln_emoji_report_all_http(const char *device_id,
     free(body);
     HttpClientUnprepare(&clientData);
     return ERRCODE_SUCC;
+}
+
+/* 上报角色表情组下载状态 */
+void ln_emoji_report_status(char* status)
+{
+    ln_emoji_report_status_http(uapi_ln_get_device_id(),
+                                uapi_ln_get_device_token(),
+                                g_emoji_package.characterId,
+                                g_emoji_package.eyesColor,
+                                g_emoji_package.eyesShape,
+                                status);
+}
+
+/* 上报所有角色表情组状态 */
+void ln_emoji_report_all(void)
+{
+    ln_emoji_report_all_http(uapi_ln_get_device_id(),
+                             uapi_ln_get_device_token(),
+                             g_emoji_package.emojis,
+                             g_emoji_package.emojiCount);
 }
\ No newline at end of file